LVDS Channel
============
-Each LVDS Channel has to contain a display-timings node that describes the
-video timings for the connected LVDS display. For detailed information, also
-have a look at Documentation/devicetree/bindings/video/display-timing.txt.
+Each LVDS Channel has to contain either an of graph link to a panel device node
+or a display-timings node that describes the video timings for the connected
+LVDS display as well as the fsl,data-mapping and fsl,data-width properties.
Required properties:
- reg : should be <0> or <1>
+ - port: Input and output port nodes with endpoint definitions as defined in
+ Documentation/devicetree/bindings/graph.txt.
+ On i.MX5, the internal two-input-multiplexer is used. Due to hardware
+ limitations, only one input port (port@[0,1]) can be used for each channel
+ (lvds-channel@[0,1], respectively).
+ On i.MX6, there should be four input ports (port@[0-3]) that correspond
+ to the four LVDS multiplexer inputs.
+ A single output port (port@2 on i.MX5, port@4 on i.MX6) must be connected
+ to a panel input port. Optionally, the output port can be left out if
+ display-timings are used instead.
+
+Optional properties (required if display-timings are used):
+ - display-timings : A node that describes the display timings as defined in
+ Documentation/devicetree/bindings/video/display-timing.txt.
- fsl,data-mapping : should be "spwg" or "jeida"
This describes how the color bits are laid out in the
serialized LVDS signal.
- fsl,data-width : should be <18> or <24>
- - port: A port node with endpoint definitions as defined in
- Documentation/devicetree/bindings/media/video-interfaces.txt.
- On i.MX5, the internal two-input-multiplexer is used.
- Due to hardware limitations, only one port (port@[0,1])
- can be used for each channel (lvds-channel@[0,1], respectively)
- On i.MX6, there should be four ports (port@[0-3]) that correspond
- to the four LVDS multiplexer inputs.
example:
#size-cells = <0>;
compatible = "fsl,imx53-ldb";
gpr = <&gpr>;
- clocks = <&clks 122>, <&clks 120>,
- <&clks 115>, <&clks 116>,
- <&clks 123>, <&clks 85>;
+ clocks = <&clks IMX5_CLK_LDB_DI0_SEL>,
+ <&clks IMX5_CLK_LDB_DI1_SEL>,
+ <&clks IMX5_CLK_IPU_DI0_SEL>,
+ <&clks IMX5_CLK_IPU_DI1_SEL>,
+ <&clks IMX5_CLK_LDB_DI0_GATE>,
+ <&clks IMX5_CLK_LDB_DI1_GATE>;
clock-names = "di0_pll", "di1_pll",
"di0_sel", "di1_sel",
"di0", "di1";
+ /* Using an of-graph endpoint link to connect the panel */
lvds-channel@0 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0>;
- fsl,data-mapping = "spwg";
- fsl,data-width = <24>;
-
- display-timings {
- /* ... */
- };
port@0 {
reg = <0>;
remote-endpoint = <&ipu_di0_lvds0>;
};
};
+
+ port@2 {
+ reg = <2>;
+
+ lvds0_out: endpoint {
+ remote-endpoint = <&panel_in>;
+ };
+ };
};
+ /* Using display-timings and fsl,data-mapping/width instead */
lvds-channel@1 {
#address-cells = <1>;
#size-cells = <0>;
};
};
};
+
+panel: lvds-panel {
+ /* ... */
+
+ port {
+ panel_in: endpoint {
+ remote-endpoint = <&lvds0_out>;
+ };
+ };
+};
#include <drm/drmP.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_crtc_helper.h>
+#include <drm/drm_panel.h>
#include <linux/mfd/syscon.h>
#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
-#include <linux/of_address.h>
#include <linux/of_device.h>
+#include <linux/of_graph.h>
#include <video/of_videomode.h>
#include <linux/regmap.h>
#include <linux/videodev2.h>
struct imx_ldb *ldb;
struct drm_connector connector;
struct drm_encoder encoder;
+ struct drm_panel *panel;
struct device_node *child;
int chno;
void *edid;
struct imx_ldb_channel *imx_ldb_ch = con_to_imx_ldb_ch(connector);
int num_modes = 0;
+ if (imx_ldb_ch->panel && imx_ldb_ch->panel->funcs &&
+ imx_ldb_ch->panel->funcs->get_modes) {
+ num_modes = imx_ldb_ch->panel->funcs->get_modes(imx_ldb_ch->panel);
+ if (num_modes > 0)
+ return num_modes;
+ }
+
if (imx_ldb_ch->edid) {
drm_mode_connector_update_edid_property(connector,
imx_ldb_ch->edid);
int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN;
int mux = imx_drm_encoder_get_mux_id(imx_ldb_ch->child, encoder);
+ drm_panel_prepare(imx_ldb_ch->panel);
+
if (dual) {
clk_prepare_enable(ldb->clk[0]);
clk_prepare_enable(ldb->clk[1]);
}
regmap_write(ldb->regmap, IOMUXC_GPR2, ldb->ldb_ctrl);
+
+ drm_panel_enable(imx_ldb_ch->panel);
}
static void imx_ldb_encoder_mode_set(struct drm_encoder *encoder,
(ldb->ldb_ctrl & LDB_CH1_MODE_EN_MASK) == 0)
return;
+ drm_panel_disable(imx_ldb_ch->panel);
+
if (imx_ldb_ch == &ldb->channel[0])
ldb->ldb_ctrl &= ~LDB_CH0_MODE_EN_MASK;
else if (imx_ldb_ch == &ldb->channel[1])
clk_disable_unprepare(ldb->clk[0]);
clk_disable_unprepare(ldb->clk[1]);
}
+
+ drm_panel_unprepare(imx_ldb_ch->panel);
}
static struct drm_connector_funcs imx_ldb_connector_funcs = {
drm_connector_init(drm, &imx_ldb_ch->connector,
&imx_ldb_connector_funcs, DRM_MODE_CONNECTOR_LVDS);
+ if (imx_ldb_ch->panel)
+ drm_panel_attach(imx_ldb_ch->panel, &imx_ldb_ch->connector);
+
drm_mode_connector_attach_encoder(&imx_ldb_ch->connector,
&imx_ldb_ch->encoder);
for_each_child_of_node(np, child) {
struct imx_ldb_channel *channel;
+ struct device_node *port;
ret = of_property_read_u32(child, "reg", &i);
if (ret || i < 0 || i > 1)
channel->chno = i;
channel->child = child;
+ /*
+ * 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;
+ }
+ }
+ }
+
edidp = of_get_property(child, "edid", &channel->edid_len);
if (edidp) {
channel->edid = kmemdup(edidp, channel->edid_len,
GFP_KERNEL);
- } else {
+ } else if (!channel->panel) {
ret = of_get_drm_display_mode(child, &channel->mode, 0);
if (!ret)
channel->mode_valid = 1;