From a6d206e28abe468aac5bb589669c6beb855b1a66 Mon Sep 17 00:00:00 2001 From: Steve Longerbeam Date: Wed, 27 Apr 2016 16:23:33 -0400 Subject: drm/imx: imx-ldb: Add DDC support Add support for reading EDID over Display Data Channel. If no DDC adapter is available, falls back to hardcoded EDID or display-timings node as before. Signed-off-by: Steve Longerbeam Signed-off-by: Akshay Bhat Acked-by: Philipp Zabel Signed-off-by: Philipp Zabel --- drivers/gpu/drm/imx/imx-ldb.c | 42 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/imx/imx-ldb.c b/drivers/gpu/drm/imx/imx-ldb.c index a58eee59550a..f506e8dd76cb 100644 --- a/drivers/gpu/drm/imx/imx-ldb.c +++ b/drivers/gpu/drm/imx/imx-ldb.c @@ -59,6 +59,7 @@ struct imx_ldb_channel { struct drm_encoder encoder; struct drm_panel *panel; struct device_node *child; + struct i2c_adapter *ddc; int chno; void *edid; int edid_len; @@ -107,6 +108,9 @@ static int imx_ldb_connector_get_modes(struct drm_connector *connector) return num_modes; } + if (!imx_ldb_ch->edid && imx_ldb_ch->ddc) + imx_ldb_ch->edid = drm_get_edid(connector, imx_ldb_ch->ddc); + if (imx_ldb_ch->edid) { drm_mode_connector_update_edid_property(connector, imx_ldb_ch->edid); @@ -553,6 +557,7 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data) for_each_child_of_node(np, child) { struct imx_ldb_channel *channel; + struct device_node *ddc_node; struct device_node *port; ret = of_property_read_u32(child, "reg", &i); @@ -595,14 +600,34 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data) } } - edidp = of_get_property(child, "edid", &channel->edid_len); - if (edidp) { - channel->edid = kmemdup(edidp, channel->edid_len, - GFP_KERNEL); - } else if (!channel->panel) { - ret = of_get_drm_display_mode(child, &channel->mode, 0); - if (!ret) - channel->mode_valid = 1; + ddc_node = of_parse_phandle(child, "ddc-i2c-bus", 0); + if (ddc_node) { + channel->ddc = of_find_i2c_adapter_by_node(ddc_node); + of_node_put(ddc_node); + if (!channel->ddc) { + dev_warn(dev, "failed to get ddc i2c adapter\n"); + return -EPROBE_DEFER; + } + } + + if (!channel->ddc) { + /* if no DDC available, fallback to hardcoded EDID */ + dev_dbg(dev, "no ddc available\n"); + + edidp = of_get_property(child, "edid", + &channel->edid_len); + if (edidp) { + channel->edid = kmemdup(edidp, + channel->edid_len, + GFP_KERNEL); + } else if (!channel->panel) { + /* fallback to display-timings node */ + ret = of_get_drm_display_mode(child, + &channel->mode, + 0); + if (!ret) + channel->mode_valid = 1; + } } channel->bus_format = of_get_bus_format(dev, child); @@ -647,6 +672,7 @@ static void imx_ldb_unbind(struct device *dev, struct device *master, channel->encoder.funcs->destroy(&channel->encoder); kfree(channel->edid); + i2c_put_adapter(channel->ddc); } } -- cgit v1.2.3 From ee89686631174fa576e61400ac3580722edac120 Mon Sep 17 00:00:00 2001 From: Akshay Bhat Date: Wed, 27 Apr 2016 16:23:34 -0400 Subject: dt-bindings: imx: ldb: Add ddc-i2c-bus property Document the ddc-i2c-bus property used by imx-ldb driver to read EDID information via I2C interface. Signed-off-by: Akshay Bhat Acked-by: Rob Herring Signed-off-by: Philipp Zabel --- Documentation/devicetree/bindings/display/imx/ldb.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/display/imx/ldb.txt b/Documentation/devicetree/bindings/display/imx/ldb.txt index 0a175d991b52..a407462c885e 100644 --- a/Documentation/devicetree/bindings/display/imx/ldb.txt +++ b/Documentation/devicetree/bindings/display/imx/ldb.txt @@ -62,6 +62,7 @@ Required properties: display-timings are used instead. Optional properties (required if display-timings are used): + - ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing - display-timings : A node that describes the display timings as defined in Documentation/devicetree/bindings/display/display-timing.txt. - fsl,data-mapping : should be "spwg" or "jeida" -- cgit v1.2.3 From 620011e0477e485fc6112ace3c0a772130e8fd4f Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Tue, 3 May 2016 14:37:13 +0200 Subject: drm/imx: imx-ldb: use of_graph_get_endpoint_by_regs helper Instead of using of_graph_get_port_by_id() to get the port and then of_get_child_by_name() to get the first endpoint, get to the endpoint in a single step. Signed-off-by: Philipp Zabel --- drivers/gpu/drm/imx/imx-ldb.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/imx/imx-ldb.c b/drivers/gpu/drm/imx/imx-ldb.c index f506e8dd76cb..5f8f0bf45a62 100644 --- a/drivers/gpu/drm/imx/imx-ldb.c +++ b/drivers/gpu/drm/imx/imx-ldb.c @@ -558,7 +558,7 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data) for_each_child_of_node(np, child) { struct imx_ldb_channel *channel; struct device_node *ddc_node; - struct device_node *port; + struct device_node *ep; ret = of_property_read_u32(child, "reg", &i); if (ret || i < 0 || i > 1) @@ -581,22 +581,23 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data) * The output port is port@4 with an external 4-port mux or * port@2 with the internal 2-port mux. */ - port = of_graph_get_port_by_id(child, imx_ldb->lvds_mux ? 4 : 2); - if (port) { - struct device_node *endpoint, *remote; - - endpoint = of_get_child_by_name(port, "endpoint"); - if (endpoint) { - remote = of_graph_get_remote_port_parent(endpoint); - if (remote) - channel->panel = of_drm_find_panel(remote); - else - return -EPROBE_DEFER; - if (!channel->panel) { - dev_err(dev, "panel not found: %s\n", - remote->full_name); - return -EPROBE_DEFER; - } + ep = of_graph_get_endpoint_by_regs(child, + imx_ldb->lvds_mux ? 4 : 2, + -1); + if (ep) { + struct device_node *remote; + + remote = of_graph_get_remote_port_parent(ep); + of_node_put(ep); + if (remote) + channel->panel = of_drm_find_panel(remote); + else + return -EPROBE_DEFER; + of_node_put(remote); + if (!channel->panel) { + dev_err(dev, "panel not found: %s\n", + remote->full_name); + return -EPROBE_DEFER; } } -- cgit v1.2.3 From 3a1c117f8bf13435f6db78f294d5754b2eb6185b Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Tue, 3 May 2016 14:39:29 +0200 Subject: drm/imx: parallel-display: use of_graph_get_endpoint_by_regs helper Instead of using of_graph_get_port_by_id() to get the port and then of_get_child_by_name() to get the first endpoint, get to the endpoint in a single step. Signed-off-by: Philipp Zabel --- drivers/gpu/drm/imx/parallel-display.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/imx/parallel-display.c b/drivers/gpu/drm/imx/parallel-display.c index 363e2c7741e2..27755ecbf516 100644 --- a/drivers/gpu/drm/imx/parallel-display.c +++ b/drivers/gpu/drm/imx/parallel-display.c @@ -203,7 +203,7 @@ static int imx_pd_bind(struct device *dev, struct device *master, void *data) { struct drm_device *drm = data; struct device_node *np = dev->of_node; - struct device_node *port; + struct device_node *ep; const u8 *edidp; struct imx_parallel_display *imxpd; int ret; @@ -230,18 +230,18 @@ static int imx_pd_bind(struct device *dev, struct device *master, void *data) } /* port@1 is the output port */ - port = of_graph_get_port_by_id(np, 1); - if (port) { - struct device_node *endpoint, *remote; - - endpoint = of_get_child_by_name(port, "endpoint"); - if (endpoint) { - remote = of_graph_get_remote_port_parent(endpoint); - if (remote) - imxpd->panel = of_drm_find_panel(remote); - if (!imxpd->panel) - return -EPROBE_DEFER; + ep = of_graph_get_endpoint_by_regs(np, 1, -1); + if (ep) { + struct device_node *remote; + + remote = of_graph_get_remote_port_parent(ep); + of_node_put(ep); + if (remote) { + imxpd->panel = of_drm_find_panel(remote); + of_node_put(remote); } + if (!imxpd->panel) + return -EPROBE_DEFER; } imxpd->dev = dev; -- cgit v1.2.3 From 7932131f6381e5d2d4a78d9134f5f6bf45900ae5 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 12 Feb 2016 14:35:55 +0100 Subject: drm/imx: ipuv3-plane: enable UYVY and VYUY formats Advertise the DRM_FORMAT_UYVY and DRM_FORMAT_VYUY formats to userspace. Signed-off-by: Philipp Zabel --- drivers/gpu/drm/imx/ipuv3-plane.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/imx/ipuv3-plane.c b/drivers/gpu/drm/imx/ipuv3-plane.c index 681ec6eb77d9..8c24df787884 100644 --- a/drivers/gpu/drm/imx/ipuv3-plane.c +++ b/drivers/gpu/drm/imx/ipuv3-plane.c @@ -38,6 +38,8 @@ static const uint32_t ipu_plane_formats[] = { DRM_FORMAT_RGBX8888, DRM_FORMAT_BGRA8888, DRM_FORMAT_BGRA8888, + DRM_FORMAT_UYVY, + DRM_FORMAT_VYUY, DRM_FORMAT_YUYV, DRM_FORMAT_YVYU, DRM_FORMAT_YUV420, -- cgit v1.2.3 From 4ed094fd73a7c0b8534685d2ed0f0f3ccdb96bdf Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 9 May 2016 17:02:13 +0200 Subject: drm/imx: use bus_flags for pixel clock polarity This patch allows panels to set pixel clock and data enable pin polarity other than the default of driving data at the falling pixel clock edge and active high display enable. Signed-off-by: Philipp Zabel --- drivers/gpu/drm/imx/imx-drm-core.c | 13 ++++++++----- drivers/gpu/drm/imx/imx-drm.h | 7 ++++--- drivers/gpu/drm/imx/imx-tve.c | 6 ++++-- drivers/gpu/drm/imx/ipuv3-crtc.c | 10 +++++++--- drivers/gpu/drm/imx/parallel-display.c | 4 ++-- 5 files changed, 25 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c index 1f14b602882b..82656654fb21 100644 --- a/drivers/gpu/drm/imx/imx-drm-core.c +++ b/drivers/gpu/drm/imx/imx-drm-core.c @@ -97,8 +97,8 @@ static struct imx_drm_crtc *imx_drm_find_crtc(struct drm_crtc *crtc) return NULL; } -int imx_drm_set_bus_format_pins(struct drm_encoder *encoder, u32 bus_format, - int hsync_pin, int vsync_pin) +int imx_drm_set_bus_config(struct drm_encoder *encoder, u32 bus_format, + int hsync_pin, int vsync_pin, u32 bus_flags) { struct imx_drm_crtc_helper_funcs *helper; struct imx_drm_crtc *imx_crtc; @@ -110,14 +110,17 @@ int imx_drm_set_bus_format_pins(struct drm_encoder *encoder, u32 bus_format, helper = &imx_crtc->imx_drm_helper_funcs; if (helper->set_interface_pix_fmt) return helper->set_interface_pix_fmt(encoder->crtc, - bus_format, hsync_pin, vsync_pin); + bus_format, hsync_pin, vsync_pin, + bus_flags); return 0; } -EXPORT_SYMBOL_GPL(imx_drm_set_bus_format_pins); +EXPORT_SYMBOL_GPL(imx_drm_set_bus_config); int imx_drm_set_bus_format(struct drm_encoder *encoder, u32 bus_format) { - return imx_drm_set_bus_format_pins(encoder, bus_format, 2, 3); + return imx_drm_set_bus_config(encoder, bus_format, 2, 3, + DRM_BUS_FLAG_DE_HIGH | + DRM_BUS_FLAG_PIXDATA_NEGEDGE); } EXPORT_SYMBOL_GPL(imx_drm_set_bus_format); diff --git a/drivers/gpu/drm/imx/imx-drm.h b/drivers/gpu/drm/imx/imx-drm.h index b0241b9d1334..74320a1723b7 100644 --- a/drivers/gpu/drm/imx/imx-drm.h +++ b/drivers/gpu/drm/imx/imx-drm.h @@ -19,7 +19,8 @@ struct imx_drm_crtc_helper_funcs { int (*enable_vblank)(struct drm_crtc *crtc); void (*disable_vblank)(struct drm_crtc *crtc); int (*set_interface_pix_fmt)(struct drm_crtc *crtc, - u32 bus_format, int hsync_pin, int vsync_pin); + u32 bus_format, int hsync_pin, int vsync_pin, + u32 bus_flags); const struct drm_crtc_helper_funcs *crtc_helper_funcs; const struct drm_crtc_funcs *crtc_funcs; }; @@ -41,8 +42,8 @@ void imx_drm_mode_config_init(struct drm_device *drm); struct drm_gem_cma_object *imx_drm_fb_get_obj(struct drm_framebuffer *fb); -int imx_drm_set_bus_format_pins(struct drm_encoder *encoder, - u32 bus_format, int hsync_pin, int vsync_pin); +int imx_drm_set_bus_config(struct drm_encoder *encoder, u32 bus_format, + int hsync_pin, int vsync_pin, u32 bus_flags); int imx_drm_set_bus_format(struct drm_encoder *encoder, u32 bus_format); diff --git a/drivers/gpu/drm/imx/imx-tve.c b/drivers/gpu/drm/imx/imx-tve.c index ae7a9fb3b8a2..baf788121287 100644 --- a/drivers/gpu/drm/imx/imx-tve.c +++ b/drivers/gpu/drm/imx/imx-tve.c @@ -294,8 +294,10 @@ static void imx_tve_encoder_prepare(struct drm_encoder *encoder) switch (tve->mode) { case TVE_MODE_VGA: - imx_drm_set_bus_format_pins(encoder, MEDIA_BUS_FMT_GBR888_1X24, - tve->hsync_pin, tve->vsync_pin); + imx_drm_set_bus_config(encoder, MEDIA_BUS_FMT_GBR888_1X24, + tve->hsync_pin, tve->vsync_pin, + DRM_BUS_FLAG_DE_HIGH | + DRM_BUS_FLAG_PIXDATA_NEGEDGE); break; case TVE_MODE_TVOUT: imx_drm_set_bus_format(encoder, MEDIA_BUS_FMT_YUV8_1X24); diff --git a/drivers/gpu/drm/imx/ipuv3-crtc.c b/drivers/gpu/drm/imx/ipuv3-crtc.c index b2c30b8d9816..fc040417e1e8 100644 --- a/drivers/gpu/drm/imx/ipuv3-crtc.c +++ b/drivers/gpu/drm/imx/ipuv3-crtc.c @@ -66,6 +66,7 @@ struct ipu_crtc { struct ipu_flip_work *flip_work; int irq; u32 bus_format; + u32 bus_flags; int di_hsync_pin; int di_vsync_pin; }; @@ -271,8 +272,10 @@ static int ipu_crtc_mode_set(struct drm_crtc *crtc, else sig_cfg.clkflags = 0; - sig_cfg.enable_pol = 1; - sig_cfg.clk_pol = 0; + sig_cfg.enable_pol = !(ipu_crtc->bus_flags & DRM_BUS_FLAG_DE_LOW); + /* Default to driving pixel data on negative clock edges */ + sig_cfg.clk_pol = !!(ipu_crtc->bus_flags & + DRM_BUS_FLAG_PIXDATA_POSEDGE); sig_cfg.bus_format = ipu_crtc->bus_format; sig_cfg.v_to_h_sync = 0; sig_cfg.hsync_pin = ipu_crtc->di_hsync_pin; @@ -396,11 +399,12 @@ static void ipu_disable_vblank(struct drm_crtc *crtc) } static int ipu_set_interface_pix_fmt(struct drm_crtc *crtc, - u32 bus_format, int hsync_pin, int vsync_pin) + u32 bus_format, int hsync_pin, int vsync_pin, u32 bus_flags) { struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); ipu_crtc->bus_format = bus_format; + ipu_crtc->bus_flags = bus_flags; ipu_crtc->di_hsync_pin = hsync_pin; ipu_crtc->di_vsync_pin = vsync_pin; diff --git a/drivers/gpu/drm/imx/parallel-display.c b/drivers/gpu/drm/imx/parallel-display.c index 27755ecbf516..2c442516d4c7 100644 --- a/drivers/gpu/drm/imx/parallel-display.c +++ b/drivers/gpu/drm/imx/parallel-display.c @@ -115,8 +115,8 @@ static void imx_pd_encoder_dpms(struct drm_encoder *encoder, int mode) static void imx_pd_encoder_prepare(struct drm_encoder *encoder) { struct imx_parallel_display *imxpd = enc_to_imxpd(encoder); - - imx_drm_set_bus_format(encoder, imxpd->bus_format); + imx_drm_set_bus_config(encoder, imxpd->bus_format, 2, 3, + imxpd->connector.display_info.bus_flags); } static void imx_pd_encoder_commit(struct drm_encoder *encoder) -- cgit v1.2.3 From 97a6075d7cfabd0d45f8667a89da2a5fd13b4e70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lothar=20Wa=C3=9Fmann?= Date: Tue, 24 May 2016 08:31:51 +0200 Subject: drm/imx: parallel-display: remove dead code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'mode_valid' flag is never set in this driver. Remove it and the code that depends on it. Signed-off-by: Lothar Waßmann Signed-off-by: Philipp Zabel --- drivers/gpu/drm/imx/parallel-display.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/drivers/gpu/drm/imx/parallel-display.c b/drivers/gpu/drm/imx/parallel-display.c index 2c442516d4c7..2d1fd02cd3d6 100644 --- a/drivers/gpu/drm/imx/parallel-display.c +++ b/drivers/gpu/drm/imx/parallel-display.c @@ -35,7 +35,6 @@ struct imx_parallel_display { void *edid; int edid_len; u32 bus_format; - int mode_valid; struct drm_display_mode mode; struct drm_panel *panel; }; @@ -68,17 +67,6 @@ static int imx_pd_connector_get_modes(struct drm_connector *connector) num_modes = drm_add_edid_modes(connector, imxpd->edid); } - if (imxpd->mode_valid) { - struct drm_display_mode *mode = drm_mode_create(connector->dev); - - if (!mode) - return -EINVAL; - drm_mode_copy(mode, &imxpd->mode); - mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, - drm_mode_probed_add(connector, mode); - num_modes++; - } - if (np) { struct drm_display_mode *mode = drm_mode_create(connector->dev); -- cgit v1.2.3 From c82b4d73e2cd32fff41be0cd6e75ea83d32943b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lothar=20Wa=C3=9Fmann?= Date: Tue, 24 May 2016 08:31:49 +0200 Subject: drm/imx: imx-ldb: honor 'native-mode' property when selecting video mode from DT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch allows to select a specific video mode from a list of modes defined in DT by setting the 'native-mode' property appropriately. This change does not affect the behaviour of existing platforms, since they either: - have just one display-timings subnode - have the native-mode property pointing to the first entry - let the bootloader select the appropriate timing Signed-off-by: Lothar Waßmann Signed-off-by: Philipp Zabel --- drivers/gpu/drm/imx/imx-ldb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/imx/imx-ldb.c b/drivers/gpu/drm/imx/imx-ldb.c index 5f8f0bf45a62..beff793bb717 100644 --- a/drivers/gpu/drm/imx/imx-ldb.c +++ b/drivers/gpu/drm/imx/imx-ldb.c @@ -25,6 +25,7 @@ #include #include #include +#include