The updated bindings for the display node uses graph nodes to identify the panel. Update the driver to support the new binding: - Add support for the video pipeline (vpl) - Add guard-time to the chip info - Move deprecated display support to own function With this update there is support for specifying a backlight node as part of the panel node. Signed-off-by: Sam Ravnborg <sam@xxxxxxxxxxxx> --- drivers/video/atmel_lcdfb.c | 6 ++ drivers/video/atmel_lcdfb.h | 4 + drivers/video/atmel_lcdfb_core.c | 125 +++++++++++++++++++++++++------ 3 files changed, 113 insertions(+), 22 deletions(-) diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c index 38b4b8df3..1087102c4 100644 --- a/drivers/video/atmel_lcdfb.c +++ b/drivers/video/atmel_lcdfb.c @@ -231,26 +231,32 @@ static int atmel_lcdc_probe(struct device_d *dev) static struct atmel_lcdfb_config at91sam9261_config = { .have_hozval = true, .have_intensity_bit = true, + .guard_time = 1, }; static struct atmel_lcdfb_config at91sam9263_config = { .have_intensity_bit = true, + .guard_time = 1, }; static struct atmel_lcdfb_config at91sam9g10_config = { .have_hozval = true, + .guard_time = 1, }; static struct atmel_lcdfb_config at91sam9g45_config = { .have_alt_pixclock = true, + .guard_time = 9, }; static struct atmel_lcdfb_config at91sam9rl_config = { .have_intensity_bit = true, + .guard_time = 1, }; static struct atmel_lcdfb_config at32ap_config = { .have_hozval = true, + .guard_time = 1, }; static __maybe_unused struct of_device_id atmel_lcdfb_compatible[] = { diff --git a/drivers/video/atmel_lcdfb.h b/drivers/video/atmel_lcdfb.h index 110c71bdb..69a27cb23 100644 --- a/drivers/video/atmel_lcdfb.h +++ b/drivers/video/atmel_lcdfb.h @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only #include <fb.h> +#include <video/vpl.h> #include <video/atmel_lcdc.h> struct atmel_lcdfb_info; @@ -9,6 +10,7 @@ struct atmel_lcdfb_config { bool have_alt_pixclock; bool have_hozval; bool have_intensity_bit; + unsigned int guard_time; }; struct atmel_lcdfb_devdata { @@ -25,6 +27,8 @@ struct atmel_lcdfb_info { struct fb_info info; void __iomem *mmio; struct device_d *device; + int id; + struct vpl vpl; unsigned int guard_time; unsigned int smem_len; diff --git a/drivers/video/atmel_lcdfb_core.c b/drivers/video/atmel_lcdfb_core.c index 17f754b56..681b20a3d 100644 --- a/drivers/video/atmel_lcdfb_core.c +++ b/drivers/video/atmel_lcdfb_core.c @@ -13,6 +13,7 @@ #include <linux/err.h> #include <linux/clk.h> #include <malloc.h> +#include <of_graph.h> #include <mach/cpu.h> @@ -30,10 +31,8 @@ static void atmel_lcdfb_stop_clock(struct atmel_lcdfb_info *sinfo) clk_disable(sinfo->lcdc_clk); } -static void atmel_lcdc_power_controller(struct fb_info *fb_info, int on) +static void atmel_lcdc_power_controller(struct atmel_lcdfb_info *sinfo, int on) { - struct atmel_lcdfb_info *sinfo = fb_info->priv; - if (sinfo->gpio_power_control < 0) return; @@ -48,7 +47,12 @@ static void atmel_lcdc_power_controller(struct fb_info *fb_info, int on) */ static void atmel_lcdc_enable_controller(struct fb_info *fb_info) { - atmel_lcdc_power_controller(fb_info, 1); + struct atmel_lcdfb_info *sinfo = fb_info->priv; + struct fb_videomode *mode = fb_info->mode; + + vpl_ioctl_prepare(&sinfo->vpl, sinfo->id, mode); + atmel_lcdc_power_controller(sinfo, 1); + vpl_ioctl_enable(&sinfo->vpl, sinfo->id); } /** @@ -56,7 +60,11 @@ static void atmel_lcdc_enable_controller(struct fb_info *fb_info) */ static void atmel_lcdc_disable_controller(struct fb_info *fb_info) { - atmel_lcdc_power_controller(fb_info, 0); + struct atmel_lcdfb_info *sinfo = fb_info->priv; + + vpl_ioctl_disable(&sinfo->vpl, sinfo->id); + atmel_lcdc_power_controller(sinfo, 0); + vpl_ioctl_unprepare(&sinfo->vpl, sinfo->id); } @@ -311,28 +319,14 @@ static int of_get_power_control(struct device_d *dev, return power_control_init(dev, sinfo, gpio, active_low); } -static int lcdfb_of_init(struct device_d *dev, struct atmel_lcdfb_info *sinfo) +static int lcdfb_display_init(struct device_d *dev, + struct atmel_lcdfb_info *sinfo, + struct device_node *display) { struct fb_info *info = &sinfo->info; struct display_timings *modes; - struct device_node *display; - struct atmel_lcdfb_config *config; int ret; - /* Driver data - optional */ - ret = dev_get_drvdata(dev, (const void **)&config); - if (!ret) { - sinfo->have_hozval = config->have_hozval; - sinfo->have_intensity_bit = config->have_intensity_bit; - sinfo->have_alt_pixclock = config->have_alt_pixclock; - } - - /* Required properties */ - display = of_parse_phandle(dev->device_node, "display", 0); - if (!display) { - dev_err(dev, "no display phandle\n"); - return -ENOENT; - } ret = of_property_read_u32(display, "atmel,guard-time", &sinfo->guard_time); if (ret < 0) { dev_err(dev, "failed to get atmel,guard-time property\n"); @@ -373,9 +367,95 @@ static int lcdfb_of_init(struct device_d *dev, struct atmel_lcdfb_info *sinfo) dev_err(dev, "failed to get power control gpio\n"); goto err; } + return 0; + err: return ret; + +} + +static int lcdfb_of_init(struct device_d *dev, struct atmel_lcdfb_info *sinfo) +{ + struct fb_info *info = &sinfo->info; + struct atmel_lcdfb_config *config; + struct device_node *display; + struct device_node *np = NULL; + struct of_endpoint ep; + int ret; + + /* Driver data - optional */ + ret = dev_get_drvdata(dev, (const void **)&config); + if (!ret) { + sinfo->have_hozval = config->have_hozval; + sinfo->have_intensity_bit = config->have_intensity_bit; + sinfo->have_alt_pixclock = config->have_alt_pixclock; + + /* The display node will override the guard time if present */ + sinfo->guard_time = config->guard_time; + } + + /* The display handle is deprecated but still supported */ + display = of_parse_phandle(dev->device_node, "display", 0); + if (display) { + return lcdfb_display_init(dev, sinfo, display); + } + + for_each_available_child_of_node(dev->device_node, np) { + if (!of_graph_port_is_available(np)) + continue; + + ret = of_graph_parse_endpoint(np, &ep); + if (ret) { + dev_err(dev, "failed to parse endpoint\n"); + return ret; + } + sinfo->id = ep.id; + break; + } + + dev_dbg(dev, "register vpl for %s\n", dev->device_node->full_name); + ret = vpl_register(&sinfo->vpl); + if (ret) + return ret; + + /* Only TFT panels - STN is obsolete */ + sinfo->lcdcon2 = ATMEL_LCDC_DISTYPE_TFT; + + /* LCDD Polarity normal */ + sinfo->lcdcon2 |= ATMEL_LCDC_INVVD_NORMAL; + + /* Clock is always active */ + sinfo->lcdcon2 |= ATMEL_LCDC_CLKMOD_ALWAYSACTIVE; + + /* Memory layout */ + sinfo->lcdcon2 |= ATMEL_LCDC_MEMOR_LITTLE; + + sinfo->dmacon = ATMEL_LCDC_DMAEN; + + /* TODO: Maybe obtain this from panel */ + info->bits_per_pixel = 16; + + ret = vpl_ioctl(&sinfo->vpl, sinfo->id, VPL_GET_VIDEOMODES, &info->modes); + if (ret) { + dev_dbg(dev, "failed to get modes: %s\n", strerror(-ret)); + return ret; + } + + /* Optional properties */ + ret = of_get_wiring_mode(dev->device_node, sinfo); + if (ret < 0) { + dev_err(dev, "failed to get atmel,lcd-wiring-mode property\n"); + return ret; + } + + ret = of_get_power_control(dev, dev->device_node, sinfo); + if (ret < 0) { + dev_err(dev, "failed to get power control gpio\n"); + return ret; + } + + return 0; } static int lcdfb_pdata_init(struct device_d *dev, struct atmel_lcdfb_info *sinfo) @@ -441,6 +521,7 @@ int atmel_lcdc_register(struct device_d *dev, struct atmel_lcdfb_devdata *data) info = &sinfo->info; info->priv = sinfo; info->fbops = &atmel_lcdc_ops; + sinfo->vpl.node = dev->device_node; if (dev->platform_data) { ret = lcdfb_pdata_init(dev, sinfo); -- 2.34.1