aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPhilipp Zabel <p.zabel@pengutronix.de>2014-03-06 08:54:39 -0500
committerPhilipp Zabel <p.zabel@pengutronix.de>2015-03-31 06:44:49 -0400
commit751e2676ee9272a0fbde6566afde33c1106d7da1 (patch)
treedb63630644c046470a78af04a0ee81d5a46d5512
parent2872c8072aae65fa55cafea50e73d69d423df168 (diff)
drm/imx: imx-ldb: add drm_panel support
This patch allows to optionally attach the lvds-channel to a panel supported by a drm_panel driver using of-graph bindings, instead of supplying the modes via display-timings in the device tree. This depends on of_graph_get_port_by_id and uses the OF graph to link the optional DRM panel to the LDB lvds-channel. The output port number is 1 on devices without the 4-port input multiplexer (i.MX5) and 4 on devices with the mux (i.MX6). Before: ldb { ... lvds-channel@0 { ... display-timings { native-timing = <&timing1>; timing1: etm0700g0dh6 { hactive = <800>; vactive = <480>; clock-frequency = <33260000>; hsync-len = <128>; hback-porch = <88>; hfront-porch = <40>; vsync-len = <2>; vback-porch = <33>; vfront-porch = <10>; hsync-active = <0>; vsync-active = <0>; ... }; }; ... }; }; After: ldb { ... lvds-channel@0 { ... port@4 { reg = <4>; lvds_out: endpoint { remote_endpoint = <&panel_in>; }; }; }; }; panel { compatible = "edt,etm0700g0dh6", "simple-panel"; ... port { panel_in: endpoint { remote-endpoint = <&lvds_out>; }; }; }; [Fixed build error due to missing select on DRM_PANEL --rmk] Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
-rw-r--r--Documentation/devicetree/bindings/drm/imx/ldb.txt62
-rw-r--r--drivers/gpu/drm/imx/Kconfig1
-rw-r--r--drivers/gpu/drm/imx/imx-ldb.c48
3 files changed, 90 insertions, 21 deletions
diff --git a/Documentation/devicetree/bindings/drm/imx/ldb.txt b/Documentation/devicetree/bindings/drm/imx/ldb.txt
index 443bcb6134d5..9a21366436f6 100644
--- a/Documentation/devicetree/bindings/drm/imx/ldb.txt
+++ b/Documentation/devicetree/bindings/drm/imx/ldb.txt
@@ -44,23 +44,30 @@ Optional properties:
44LVDS Channel 44LVDS Channel
45============ 45============
46 46
47Each LVDS Channel has to contain a display-timings node that describes the 47Each LVDS Channel has to contain either an of graph link to a panel device node
48video timings for the connected LVDS display. For detailed information, also 48or a display-timings node that describes the video timings for the connected
49have a look at Documentation/devicetree/bindings/video/display-timing.txt. 49LVDS display as well as the fsl,data-mapping and fsl,data-width properties.
50 50
51Required properties: 51Required properties:
52 - reg : should be <0> or <1> 52 - reg : should be <0> or <1>
53 - port: Input and output port nodes with endpoint definitions as defined in
54 Documentation/devicetree/bindings/graph.txt.
55 On i.MX5, the internal two-input-multiplexer is used. Due to hardware
56 limitations, only one input port (port@[0,1]) can be used for each channel
57 (lvds-channel@[0,1], respectively).
58 On i.MX6, there should be four input ports (port@[0-3]) that correspond
59 to the four LVDS multiplexer inputs.
60 A single output port (port@2 on i.MX5, port@4 on i.MX6) must be connected
61 to a panel input port. Optionally, the output port can be left out if
62 display-timings are used instead.
63
64Optional properties (required if display-timings are used):
65 - display-timings : A node that describes the display timings as defined in
66 Documentation/devicetree/bindings/video/display-timing.txt.
53 - fsl,data-mapping : should be "spwg" or "jeida" 67 - fsl,data-mapping : should be "spwg" or "jeida"
54 This describes how the color bits are laid out in the 68 This describes how the color bits are laid out in the
55 serialized LVDS signal. 69 serialized LVDS signal.
56 - fsl,data-width : should be <18> or <24> 70 - fsl,data-width : should be <18> or <24>
57 - port: A port node with endpoint definitions as defined in
58 Documentation/devicetree/bindings/media/video-interfaces.txt.
59 On i.MX5, the internal two-input-multiplexer is used.
60 Due to hardware limitations, only one port (port@[0,1])
61 can be used for each channel (lvds-channel@[0,1], respectively)
62 On i.MX6, there should be four ports (port@[0-3]) that correspond
63 to the four LVDS multiplexer inputs.
64 71
65example: 72example:
66 73
@@ -73,23 +80,21 @@ ldb: ldb@53fa8008 {
73 #size-cells = <0>; 80 #size-cells = <0>;
74 compatible = "fsl,imx53-ldb"; 81 compatible = "fsl,imx53-ldb";
75 gpr = <&gpr>; 82 gpr = <&gpr>;
76 clocks = <&clks 122>, <&clks 120>, 83 clocks = <&clks IMX5_CLK_LDB_DI0_SEL>,
77 <&clks 115>, <&clks 116>, 84 <&clks IMX5_CLK_LDB_DI1_SEL>,
78 <&clks 123>, <&clks 85>; 85 <&clks IMX5_CLK_IPU_DI0_SEL>,
86 <&clks IMX5_CLK_IPU_DI1_SEL>,
87 <&clks IMX5_CLK_LDB_DI0_GATE>,
88 <&clks IMX5_CLK_LDB_DI1_GATE>;
79 clock-names = "di0_pll", "di1_pll", 89 clock-names = "di0_pll", "di1_pll",
80 "di0_sel", "di1_sel", 90 "di0_sel", "di1_sel",
81 "di0", "di1"; 91 "di0", "di1";
82 92
93 /* Using an of-graph endpoint link to connect the panel */
83 lvds-channel@0 { 94 lvds-channel@0 {
84 #address-cells = <1>; 95 #address-cells = <1>;
85 #size-cells = <0>; 96 #size-cells = <0>;
86 reg = <0>; 97 reg = <0>;
87 fsl,data-mapping = "spwg";
88 fsl,data-width = <24>;
89
90 display-timings {
91 /* ... */
92 };
93 98
94 port@0 { 99 port@0 {
95 reg = <0>; 100 reg = <0>;
@@ -98,8 +103,17 @@ ldb: ldb@53fa8008 {
98 remote-endpoint = <&ipu_di0_lvds0>; 103 remote-endpoint = <&ipu_di0_lvds0>;
99 }; 104 };
100 }; 105 };
106
107 port@2 {
108 reg = <2>;
109
110 lvds0_out: endpoint {
111 remote-endpoint = <&panel_in>;
112 };
113 };
101 }; 114 };
102 115
116 /* Using display-timings and fsl,data-mapping/width instead */
103 lvds-channel@1 { 117 lvds-channel@1 {
104 #address-cells = <1>; 118 #address-cells = <1>;
105 #size-cells = <0>; 119 #size-cells = <0>;
@@ -120,3 +134,13 @@ ldb: ldb@53fa8008 {
120 }; 134 };
121 }; 135 };
122}; 136};
137
138panel: lvds-panel {
139 /* ... */
140
141 port {
142 panel_in: endpoint {
143 remote-endpoint = <&lvds0_out>;
144 };
145 };
146};
diff --git a/drivers/gpu/drm/imx/Kconfig b/drivers/gpu/drm/imx/Kconfig
index 33cdddf26684..2b81a417cf29 100644
--- a/drivers/gpu/drm/imx/Kconfig
+++ b/drivers/gpu/drm/imx/Kconfig
@@ -36,6 +36,7 @@ config DRM_IMX_TVE
36config DRM_IMX_LDB 36config DRM_IMX_LDB
37 tristate "Support for LVDS displays" 37 tristate "Support for LVDS displays"
38 depends on DRM_IMX && MFD_SYSCON 38 depends on DRM_IMX && MFD_SYSCON
39 select DRM_PANEL
39 help 40 help
40 Choose this to enable the internal LVDS Display Bridge (LDB) 41 Choose this to enable the internal LVDS Display Bridge (LDB)
41 found on i.MX53 and i.MX6 processors. 42 found on i.MX53 and i.MX6 processors.
diff --git a/drivers/gpu/drm/imx/imx-ldb.c b/drivers/gpu/drm/imx/imx-ldb.c
index cd062b11a102..4286399590c3 100644
--- a/drivers/gpu/drm/imx/imx-ldb.c
+++ b/drivers/gpu/drm/imx/imx-ldb.c
@@ -19,10 +19,11 @@
19#include <drm/drmP.h> 19#include <drm/drmP.h>
20#include <drm/drm_fb_helper.h> 20#include <drm/drm_fb_helper.h>
21#include <drm/drm_crtc_helper.h> 21#include <drm/drm_crtc_helper.h>
22#include <drm/drm_panel.h>
22#include <linux/mfd/syscon.h> 23#include <linux/mfd/syscon.h>
23#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> 24#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
24#include <linux/of_address.h>
25#include <linux/of_device.h> 25#include <linux/of_device.h>
26#include <linux/of_graph.h>
26#include <video/of_videomode.h> 27#include <video/of_videomode.h>
27#include <linux/regmap.h> 28#include <linux/regmap.h>
28#include <linux/videodev2.h> 29#include <linux/videodev2.h>
@@ -55,6 +56,7 @@ struct imx_ldb_channel {
55 struct imx_ldb *ldb; 56 struct imx_ldb *ldb;
56 struct drm_connector connector; 57 struct drm_connector connector;
57 struct drm_encoder encoder; 58 struct drm_encoder encoder;
59 struct drm_panel *panel;
58 struct device_node *child; 60 struct device_node *child;
59 int chno; 61 int chno;
60 void *edid; 62 void *edid;
@@ -91,6 +93,13 @@ static int imx_ldb_connector_get_modes(struct drm_connector *connector)
91 struct imx_ldb_channel *imx_ldb_ch = con_to_imx_ldb_ch(connector); 93 struct imx_ldb_channel *imx_ldb_ch = con_to_imx_ldb_ch(connector);
92 int num_modes = 0; 94 int num_modes = 0;
93 95
96 if (imx_ldb_ch->panel && imx_ldb_ch->panel->funcs &&
97 imx_ldb_ch->panel->funcs->get_modes) {
98 num_modes = imx_ldb_ch->panel->funcs->get_modes(imx_ldb_ch->panel);
99 if (num_modes > 0)
100 return num_modes;
101 }
102
94 if (imx_ldb_ch->edid) { 103 if (imx_ldb_ch->edid) {
95 drm_mode_connector_update_edid_property(connector, 104 drm_mode_connector_update_edid_property(connector,
96 imx_ldb_ch->edid); 105 imx_ldb_ch->edid);
@@ -190,6 +199,8 @@ static void imx_ldb_encoder_commit(struct drm_encoder *encoder)
190 int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN; 199 int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN;
191 int mux = imx_drm_encoder_get_mux_id(imx_ldb_ch->child, encoder); 200 int mux = imx_drm_encoder_get_mux_id(imx_ldb_ch->child, encoder);
192 201
202 drm_panel_prepare(imx_ldb_ch->panel);
203
193 if (dual) { 204 if (dual) {
194 clk_prepare_enable(ldb->clk[0]); 205 clk_prepare_enable(ldb->clk[0]);
195 clk_prepare_enable(ldb->clk[1]); 206 clk_prepare_enable(ldb->clk[1]);
@@ -223,6 +234,8 @@ static void imx_ldb_encoder_commit(struct drm_encoder *encoder)
223 } 234 }
224 235
225 regmap_write(ldb->regmap, IOMUXC_GPR2, ldb->ldb_ctrl); 236 regmap_write(ldb->regmap, IOMUXC_GPR2, ldb->ldb_ctrl);
237
238 drm_panel_enable(imx_ldb_ch->panel);
226} 239}
227 240
228static void imx_ldb_encoder_mode_set(struct drm_encoder *encoder, 241static void imx_ldb_encoder_mode_set(struct drm_encoder *encoder,
@@ -287,6 +300,8 @@ static void imx_ldb_encoder_disable(struct drm_encoder *encoder)
287 (ldb->ldb_ctrl & LDB_CH1_MODE_EN_MASK) == 0) 300 (ldb->ldb_ctrl & LDB_CH1_MODE_EN_MASK) == 0)
288 return; 301 return;
289 302
303 drm_panel_disable(imx_ldb_ch->panel);
304
290 if (imx_ldb_ch == &ldb->channel[0]) 305 if (imx_ldb_ch == &ldb->channel[0])
291 ldb->ldb_ctrl &= ~LDB_CH0_MODE_EN_MASK; 306 ldb->ldb_ctrl &= ~LDB_CH0_MODE_EN_MASK;
292 else if (imx_ldb_ch == &ldb->channel[1]) 307 else if (imx_ldb_ch == &ldb->channel[1])
@@ -298,6 +313,8 @@ static void imx_ldb_encoder_disable(struct drm_encoder *encoder)
298 clk_disable_unprepare(ldb->clk[0]); 313 clk_disable_unprepare(ldb->clk[0]);
299 clk_disable_unprepare(ldb->clk[1]); 314 clk_disable_unprepare(ldb->clk[1]);
300 } 315 }
316
317 drm_panel_unprepare(imx_ldb_ch->panel);
301} 318}
302 319
303static struct drm_connector_funcs imx_ldb_connector_funcs = { 320static struct drm_connector_funcs imx_ldb_connector_funcs = {
@@ -371,6 +388,9 @@ static int imx_ldb_register(struct drm_device *drm,
371 drm_connector_init(drm, &imx_ldb_ch->connector, 388 drm_connector_init(drm, &imx_ldb_ch->connector,
372 &imx_ldb_connector_funcs, DRM_MODE_CONNECTOR_LVDS); 389 &imx_ldb_connector_funcs, DRM_MODE_CONNECTOR_LVDS);
373 390
391 if (imx_ldb_ch->panel)
392 drm_panel_attach(imx_ldb_ch->panel, &imx_ldb_ch->connector);
393
374 drm_mode_connector_attach_encoder(&imx_ldb_ch->connector, 394 drm_mode_connector_attach_encoder(&imx_ldb_ch->connector,
375 &imx_ldb_ch->encoder); 395 &imx_ldb_ch->encoder);
376 396
@@ -485,6 +505,7 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data)
485 505
486 for_each_child_of_node(np, child) { 506 for_each_child_of_node(np, child) {
487 struct imx_ldb_channel *channel; 507 struct imx_ldb_channel *channel;
508 struct device_node *port;
488 509
489 ret = of_property_read_u32(child, "reg", &i); 510 ret = of_property_read_u32(child, "reg", &i);
490 if (ret || i < 0 || i > 1) 511 if (ret || i < 0 || i > 1)
@@ -503,11 +524,34 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data)
503 channel->chno = i; 524 channel->chno = i;
504 channel->child = child; 525 channel->child = child;
505 526
527 /*
528 * The output port is port@4 with an external 4-port mux or
529 * port@2 with the internal 2-port mux.
530 */
531 port = of_graph_get_port_by_id(child, imx_ldb->lvds_mux ? 4 : 2);
532 if (port) {
533 struct device_node *endpoint, *remote;
534
535 endpoint = of_get_child_by_name(port, "endpoint");
536 if (endpoint) {
537 remote = of_graph_get_remote_port_parent(endpoint);
538 if (remote)
539 channel->panel = of_drm_find_panel(remote);
540 else
541 return -EPROBE_DEFER;
542 if (!channel->panel) {
543 dev_err(dev, "panel not found: %s\n",
544 remote->full_name);
545 return -EPROBE_DEFER;
546 }
547 }
548 }
549
506 edidp = of_get_property(child, "edid", &channel->edid_len); 550 edidp = of_get_property(child, "edid", &channel->edid_len);
507 if (edidp) { 551 if (edidp) {
508 channel->edid = kmemdup(edidp, channel->edid_len, 552 channel->edid = kmemdup(edidp, channel->edid_len,
509 GFP_KERNEL); 553 GFP_KERNEL);
510 } else { 554 } else if (!channel->panel) {
511 ret = of_get_drm_display_mode(child, &channel->mode, 0); 555 ret = of_get_drm_display_mode(child, &channel->mode, 0);
512 if (!ret) 556 if (!ret)
513 channel->mode_valid = 1; 557 channel->mode_valid = 1;