diff options
author | Philipp Zabel <p.zabel@pengutronix.de> | 2014-03-06 08:54:39 -0500 |
---|---|---|
committer | Philipp Zabel <p.zabel@pengutronix.de> | 2015-03-31 06:44:49 -0400 |
commit | 751e2676ee9272a0fbde6566afde33c1106d7da1 (patch) | |
tree | db63630644c046470a78af04a0ee81d5a46d5512 | |
parent | 2872c8072aae65fa55cafea50e73d69d423df168 (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.txt | 62 | ||||
-rw-r--r-- | drivers/gpu/drm/imx/Kconfig | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/imx/imx-ldb.c | 48 |
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: | |||
44 | LVDS Channel | 44 | LVDS Channel |
45 | ============ | 45 | ============ |
46 | 46 | ||
47 | Each LVDS Channel has to contain a display-timings node that describes the | 47 | Each LVDS Channel has to contain either an of graph link to a panel device node |
48 | video timings for the connected LVDS display. For detailed information, also | 48 | or a display-timings node that describes the video timings for the connected |
49 | have a look at Documentation/devicetree/bindings/video/display-timing.txt. | 49 | LVDS display as well as the fsl,data-mapping and fsl,data-width properties. |
50 | 50 | ||
51 | Required properties: | 51 | Required 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 | |||
64 | Optional 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 | ||
65 | example: | 72 | example: |
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 | |||
138 | panel: 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 | |||
36 | config DRM_IMX_LDB | 36 | config 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 | ||
228 | static void imx_ldb_encoder_mode_set(struct drm_encoder *encoder, | 241 | static 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 | ||
303 | static struct drm_connector_funcs imx_ldb_connector_funcs = { | 320 | static 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; |