diff options
| -rw-r--r-- | drivers/video/panel-uclass.c | 38 | ||||
| -rw-r--r-- | drivers/video/simple_panel.c | 62 | ||||
| -rw-r--r-- | drivers/video/ti/tilcdc.c | 46 | ||||
| -rw-r--r-- | include/panel.h | 72 |
4 files changed, 200 insertions, 18 deletions
diff --git a/drivers/video/panel-uclass.c b/drivers/video/panel-uclass.c index 52a3466dc8c..82e8339dde1 100644 --- a/drivers/video/panel-uclass.c +++ b/drivers/video/panel-uclass.c @@ -37,15 +37,47 @@ int panel_set_backlight(struct udevice *dev, int percent) return ops->set_backlight(dev, percent); } +static void drm_mode_to_display_timing(const struct drm_display_mode *mode, + struct display_timing *timing) +{ + timing->pixelclock.typ = mode->clock * 1000; /* kHz to Hz */ + timing->hactive.typ = mode->hdisplay; + timing->hfront_porch.typ = mode->hsync_start - mode->hdisplay; + timing->hsync_len.typ = mode->hsync_end - mode->hsync_start; + timing->hback_porch.typ = mode->htotal - mode->hsync_end; + timing->vactive.typ = mode->vdisplay; + timing->vfront_porch.typ = mode->vsync_start - mode->vdisplay; + timing->vsync_len.typ = mode->vsync_end - mode->vsync_start; + timing->vback_porch.typ = mode->vtotal - mode->vsync_end; + + /* DRM_MODE_FLAG_* defines are already mapped to u-boot DISPLAY_FLAGS */ + timing->flags = mode->flags; +} + int panel_get_display_timing(struct udevice *dev, struct display_timing *timings) { struct panel_ops *ops = panel_get_ops(dev); + const struct drm_display_mode *modes; + int ret = -ENOSYS; - if (!ops->get_display_timing) - return -ENOSYS; + if (ops->get_display_timing) { + ret = ops->get_display_timing(dev, timings); + if (ret != -ENODEV) + return ret; + } + + if (!ops->get_modes) + return ret; + + ret = ops->get_modes(dev, &modes); + if (ret < 0) + return ret; + else if (ret == 0) + return -ENODEV; - return ops->get_display_timing(dev, timings); + drm_mode_to_display_timing(&modes[0], timings); + return 0; } UCLASS_DRIVER(panel) = { diff --git a/drivers/video/simple_panel.c b/drivers/video/simple_panel.c index 0f23df701bc..8fa7ea4e87c 100644 --- a/drivers/video/simple_panel.c +++ b/drivers/video/simple_panel.c @@ -22,6 +22,12 @@ struct simple_panel_priv { struct gpio_desc enable; }; +struct simple_panel_drv_data { + const struct drm_display_mode *modes; + unsigned int num_modes; + const struct mipi_dsi_panel_plat *mipi_dsi; +}; + static int simple_panel_enable_backlight(struct udevice *dev) { struct simple_panel_priv *priv = dev_get_priv(dev); @@ -104,13 +110,32 @@ static int simple_panel_get_edid_timing(struct udevice *dev, } #endif +static int simple_panel_get_modes(struct udevice *dev, + const struct drm_display_mode **modes) +{ + const struct simple_panel_drv_data *data = + (const struct simple_panel_drv_data *)dev_get_driver_data(dev); + + if (!data || !data->modes || data->num_modes == 0) + return -ENODEV; + + *modes = data->modes; + return data->num_modes; +} + static int simple_panel_get_display_timing(struct udevice *dev, struct display_timing *timings) { + const struct simple_panel_drv_data *data = + (const struct simple_panel_drv_data *)dev_get_driver_data(dev); const void *blob = gd->fdt_blob; int ret; - /* Check for timing subnode if panel node first */ + /* Prefer the use of drm_display_mode if available */ + if (data && data->modes && data->num_modes > 0) + return -ENODEV; + + /* Check for timing subnode in panel node */ ret = fdtdec_decode_display_timing(blob, dev_of_offset(dev), 0, timings); if (!ret) @@ -158,9 +183,9 @@ static int simple_panel_of_to_plat(struct udevice *dev) static int simple_panel_probe(struct udevice *dev) { struct simple_panel_priv *priv = dev_get_priv(dev); + const struct simple_panel_drv_data *data = + (const struct simple_panel_drv_data *)dev_get_driver_data(dev); struct mipi_dsi_panel_plat *plat = dev_get_plat(dev); - struct mipi_dsi_panel_plat *dsi_data = - (struct mipi_dsi_panel_plat *)dev_get_driver_data(dev); int ret; ret = regulator_set_enable_if_allowed(priv->reg, true); @@ -170,8 +195,8 @@ static int simple_panel_probe(struct udevice *dev) return ret; } - if (dsi_data) - memcpy(plat, dsi_data, sizeof(struct mipi_dsi_panel_plat)); + if (data && data->mipi_dsi) + memcpy(plat, data->mipi_dsi, sizeof(struct mipi_dsi_panel_plat)); return 0; } @@ -180,6 +205,7 @@ static const struct panel_ops simple_panel_ops = { .enable_backlight = simple_panel_enable_backlight, .set_backlight = simple_panel_set_backlight, .get_display_timing = simple_panel_get_display_timing, + .get_modes = simple_panel_get_modes, }; static const struct mipi_dsi_panel_plat panasonic_vvx10f004b00 = { @@ -189,6 +215,28 @@ static const struct mipi_dsi_panel_plat panasonic_vvx10f004b00 = { .lanes = 4, }; +static const struct simple_panel_drv_data panasonic_vvx10f004b00_data = { + .mipi_dsi = &panasonic_vvx10f004b00, +}; + +static const struct drm_display_mode tfc_s9700rtwv43tr_01b_mode = { + .clock = 30000, + .hdisplay = 800, + .hsync_start = 800 + 39, + .hsync_end = 800 + 39 + 47, + .htotal = 800 + 39 + 47 + 39, + .vdisplay = 480, + .vsync_start = 480 + 13, + .vsync_end = 480 + 13 + 2, + .vtotal = 480 + 13 + 2 + 29, + .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC, +}; + +static const struct simple_panel_drv_data tfc_s9700rtwv43tr_01b_data = { + .modes = &tfc_s9700rtwv43tr_01b_mode, + .num_modes = 1, +}; + static const struct udevice_id simple_panel_ids[] = { { .compatible = "simple-panel" }, { .compatible = "panel-lvds" }, @@ -200,7 +248,9 @@ static const struct udevice_id simple_panel_ids[] = { { .compatible = "sharp,lq123p1jx31" }, { .compatible = "boe,nv101wxmn51" }, { .compatible = "panasonic,vvx10f004b00", - .data = (ulong)&panasonic_vvx10f004b00 }, + .data = (ulong)&panasonic_vvx10f004b00_data }, + { .compatible = "tfc,s9700rtwv43tr-01b", + .data = (ulong)&tfc_s9700rtwv43tr_01b_data }, { } }; diff --git a/drivers/video/ti/tilcdc.c b/drivers/video/ti/tilcdc.c index 340c79299ba..b498e81864b 100644 --- a/drivers/video/ti/tilcdc.c +++ b/drivers/video/ti/tilcdc.c @@ -6,6 +6,7 @@ #include <clk.h> #include <dm.h> #include <dm/device_compat.h> +#include <dm/ofnode_graph.h> #include <log.h> #include <panel.h> #include <video.h> @@ -113,6 +114,18 @@ struct tilcdc_priv { DECLARE_GLOBAL_DATA_PTR; +static const struct tilcdc_panel_info tilcdc_panel_info_default = { + .ac_bias = 255, + .ac_bias_intrpt = 0, + .dma_burst_sz = 16, + .bpp = 16, + .fdd = 0x80, + .tft_alt_mode = 0, + .sync_edge = 0, + .sync_ctrl = 1, + .raster_order = 0, +}; + static ulong tilcdc_set_pixel_clk_rate(struct udevice *dev, ulong rate) { struct tilcdc_priv *priv = dev_get_priv(dev); @@ -173,6 +186,8 @@ static int tilcdc_probe(struct udevice *dev) struct udevice *panel, *clk_dev; struct tilcdc_panel_info info; struct display_timing timing; + bool is_legacy_panel = false; + ofnode remote; ulong rate; u32 reg; int err; @@ -181,10 +196,21 @@ static int tilcdc_probe(struct udevice *dev) if (!(gd->flags & GD_FLG_RELOC)) return 0; - err = uclass_get_device(UCLASS_PANEL, 0, &panel); - if (err) { - dev_err(dev, "failed to get panel\n"); - return err; + /* Try using remote node first, then fall back to using UCLASS_PANEL */ + remote = ofnode_graph_get_remote_node(dev_ofnode(dev), -1, -1); + if (ofnode_valid(remote)) { + err = uclass_get_device_by_ofnode(UCLASS_PANEL, remote, &panel); + if (err) { + dev_err(dev, "failed to get panel via OF graph\n"); + return err; + } + } else { + err = uclass_get_device(UCLASS_PANEL, 0, &panel); + if (err) { + dev_err(dev, "failed to get panel\n"); + return err; + } + is_legacy_panel = true; } err = panel_get_display_timing(panel, &timing); @@ -205,10 +231,14 @@ static int tilcdc_probe(struct udevice *dev) if (timing.vactive.typ > LCDC_MAX_HEIGHT) timing.vactive.typ = LCDC_MAX_HEIGHT; - err = tilcdc_panel_get_display_info(panel, &info); - if (err) { - dev_err(dev, "failed to get panel info\n"); - return err; + if (is_legacy_panel) { + err = tilcdc_panel_get_display_info(panel, &info); + if (err) { + dev_err(dev, "failed to get panel info\n"); + return err; + } + } else { + info = tilcdc_panel_info_default; } switch (info.bpp) { diff --git a/include/panel.h b/include/panel.h index ab417bc91ed..8d46fb26a07 100644 --- a/include/panel.h +++ b/include/panel.h @@ -7,6 +7,62 @@ #ifndef _PANEL_H #define _PANEL_H +#include <video.h> +#include <fdtdec.h> + +/* DRM mode flags mapped to U-Boot DISPLAY_FLAGS for direct compatibility */ +#define DRM_MODE_FLAG_NHSYNC DISPLAY_FLAGS_HSYNC_LOW +#define DRM_MODE_FLAG_PHSYNC DISPLAY_FLAGS_HSYNC_HIGH +#define DRM_MODE_FLAG_NVSYNC DISPLAY_FLAGS_VSYNC_LOW +#define DRM_MODE_FLAG_PVSYNC DISPLAY_FLAGS_VSYNC_HIGH +#define DRM_MODE_FLAG_INTERLACE DISPLAY_FLAGS_INTERLACED +#define DRM_MODE_FLAG_DBLSCAN DISPLAY_FLAGS_DOUBLESCAN +#define DRM_MODE_FLAG_DBLCLK DISPLAY_FLAGS_DOUBLECLK + +/** + * struct drm_display_mode - DRM kernel-internal display mode structure + * simplified for U-Boot + * @hdisplay: horizontal display size + * @hsync_start: horizontal sync start + * @hsync_end: horizontal sync end + * @htotal: horizontal total size + * @vdisplay: vertical display size + * @vsync_start: vertical sync start + * @vsync_end: vertical sync end + * @vtotal: vertical total size + * + * The horizontal and vertical timings are defined per the following diagram. + * + * :: + * + * + * Active Front Sync Back + * Region Porch Porch + * <-----------------------><----------------><-------------><--------------> + * //////////////////////| + * ////////////////////// | + * ////////////////////// |.................. ................ + * _______________ + * <----- [hv]display -----> + * <------------- [hv]sync_start ------------> + * <--------------------- [hv]sync_end ---------------------> + * <-------------------------------- [hv]total ----------------------------->* + */ +struct drm_display_mode { + unsigned int clock; /* in kHz */ + + u16 hdisplay; + u16 hsync_start; + u16 hsync_end; + u16 htotal; + u16 vdisplay; + u16 vsync_start; + u16 vsync_end; + u16 vtotal; + + u32 flags; +}; + struct panel_ops { /** * enable_backlight() - Enable the panel backlight @@ -29,11 +85,24 @@ struct panel_ops { * get_timings() - Get display timings from panel. * * @dev: Panel device containing the display timings - * @tim: Place to put timings + * @timing: Pointer to the timing for storing * @return 0 if OK, -ve on error */ int (*get_display_timing)(struct udevice *dev, struct display_timing *timing); + + /** + * get_modes() - Get display modes from panel + * + * Returns an array of display modes supported by the panel. + * Similar to Linux's drm_panel_funcs->get_modes(). + * + * @dev: Panel device + * @modes: Pointer to an array of modes + * @return number of modes if OK, -ve on error + */ + int (*get_modes)(struct udevice *dev, + const struct drm_display_mode **modes); }; #define panel_get_ops(dev) ((struct panel_ops *)(dev)->driver->ops) @@ -60,6 +129,7 @@ int panel_set_backlight(struct udevice *dev, int percent); * panel_get_display_timing() - Get display timings from panel. * * @dev: Panel device containing the display timings + * @timing: Pointer to the timing for storing * Return: 0 if OK, -ve on error */ int panel_get_display_timing(struct udevice *dev, |
