diff options
| -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; |
