drm/msm/dsi: Parse DSI lanes via DT
authorArchit Taneja <architt@codeaurora.org>
Thu, 25 Feb 2016 05:49:48 +0000 (11:19 +0530)
committerRob Clark <robdclark@gmail.com>
Thu, 3 Mar 2016 16:55:20 +0000 (11:55 -0500)
The DSI driver is currently unaware of how the DSI physical data lanes
are mapped to the logical lanes provided by the DSI controller.

Create a DT binding "qcom,data-lane-map" that provides this information
on a given platform.

The MSM DSI controller is restricted in terms of what all mappings
it can support. The lane polarity is fixed for all the lanes, the clock
lanes are fixed, and the data lanes can be swapped among each other only
for a few combinations. Apply these restrictions when we parse the DT
data.

Cc: devicetree@vger.kernel.org
Cc: Rob Herring <robh@kernel.org>
Cc: Tomi Valkeinen <tomi.valkeinen@ti.com>
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
Acked-by: Rob Herring <robh@kernel.org>
Documentation/devicetree/bindings/display/msm/dsi.txt
drivers/gpu/drm/msm/dsi/dsi_host.c

index e7423be..f5948c4 100644 (file)
@@ -44,9 +44,34 @@ Optional properties:
 - pinctrl-names: the pin control state names; should contain "default"
 - pinctrl-0: the default pinctrl state (active)
 - pinctrl-n: the "sleep" pinctrl state
-- port: DSI controller output port. This contains one endpoint subnode, with its
-  remote-endpoint set to the phandle of the connected panel's endpoint.
-  See Documentation/devicetree/bindings/graph.txt for device graph info.
+- port: DSI controller output port, containing one endpoint subnode.
+
+  DSI Endpoint properties:
+  - remote-endpoint: set to phandle of the connected panel's endpoint.
+    See Documentation/devicetree/bindings/graph.txt for device graph info.
+  - qcom,data-lane-map: this describes how the logical DSI lanes are mapped
+    to the physical lanes on the given platform. The value contained in
+    index n describes what logical data lane is mapped to the physical data
+    lane n (DATAn, where n lies between 0 and 3).
+
+    For example:
+
+    qcom,data-lane-map = <3 0 1 2>;
+
+    The above mapping describes that the logical data lane DATA3 is mapped to
+    the physical data lane DATA0, logical DATA0 to physical DATA1, logic DATA1
+    to phys DATA2 and logic DATA2 to phys DATA3.
+
+    There are only a limited number of physical to logical mappings possible:
+
+    "0123": Logic 0->Phys 0; Logic 1->Phys 1; Logic 2->Phys 2; Logic 3->Phys 3;
+    "3012": Logic 3->Phys 0; Logic 0->Phys 1; Logic 1->Phys 2; Logic 2->Phys 3;
+    "2301": Logic 2->Phys 0; Logic 3->Phys 1; Logic 0->Phys 2; Logic 1->Phys 3;
+    "1230": Logic 1->Phys 0; Logic 2->Phys 1; Logic 3->Phys 2; Logic 0->Phys 3;
+    "0321": Logic 0->Phys 0; Logic 3->Phys 1; Logic 2->Phys 2; Logic 1->Phys 3;
+    "1032": Logic 1->Phys 0; Logic 0->Phys 1; Logic 3->Phys 2; Logic 2->Phys 3;
+    "2103": Logic 2->Phys 0; Logic 1->Phys 1; Logic 0->Phys 2; Logic 3->Phys 3;
+    "3210": Logic 3->Phys 0; Logic 2->Phys 1; Logic 1->Phys 2; Logic 0->Phys 3;
 
 DSI PHY:
 Required properties:
@@ -131,6 +156,7 @@ Example:
                port {
                        dsi0_out: endpoint {
                                remote-endpoint = <&panel_in>;
+                               lanes = <0 1 2 3>;
                        };
                };
        };
index 69bac59..4282ec6 100644 (file)
@@ -163,6 +163,10 @@ struct msm_dsi_host {
        enum mipi_dsi_pixel_format format;
        unsigned long mode_flags;
 
+       /* lane data parsed via DT */
+       int dlane_swap;
+       int num_data_lanes;
+
        u32 dma_cmd_ctrl_restore;
 
        bool registered;
@@ -845,19 +849,10 @@ static void dsi_ctrl_config(struct msm_dsi_host *msm_host, bool enable,
        data = DSI_CTRL_CLK_EN;
 
        DBG("lane number=%d", msm_host->lanes);
-       if (msm_host->lanes == 2) {
-               data |= DSI_CTRL_LANE1 | DSI_CTRL_LANE2;
-               /* swap lanes for 2-lane panel for better performance */
-               dsi_write(msm_host, REG_DSI_LANE_SWAP_CTRL,
-                       DSI_LANE_SWAP_CTRL_DLN_SWAP_SEL(LANE_SWAP_1230));
-       } else {
-               /* Take 4 lanes as default */
-               data |= DSI_CTRL_LANE0 | DSI_CTRL_LANE1 | DSI_CTRL_LANE2 |
-                       DSI_CTRL_LANE3;
-               /* Do not swap lanes for 4-lane panel */
-               dsi_write(msm_host, REG_DSI_LANE_SWAP_CTRL,
-                       DSI_LANE_SWAP_CTRL_DLN_SWAP_SEL(LANE_SWAP_0123));
-       }
+       data |= ((DSI_CTRL_LANE0 << msm_host->lanes) - DSI_CTRL_LANE0);
+
+       dsi_write(msm_host, REG_DSI_LANE_SWAP_CTRL,
+                 DSI_LANE_SWAP_CTRL_DLN_SWAP_SEL(msm_host->dlane_swap));
 
        if (!(flags & MIPI_DSI_CLOCK_NON_CONTINUOUS))
                dsi_write(msm_host, REG_DSI_LANE_CTRL,
@@ -1479,6 +1474,9 @@ static int dsi_host_attach(struct mipi_dsi_host *host,
        struct msm_dsi_host *msm_host = to_msm_dsi_host(host);
        int ret;
 
+       if (dsi->lanes > msm_host->num_data_lanes)
+               return -EINVAL;
+
        msm_host->channel = dsi->channel;
        msm_host->lanes = dsi->lanes;
        msm_host->format = dsi->format;
@@ -1532,6 +1530,75 @@ static struct mipi_dsi_host_ops dsi_host_ops = {
        .transfer = dsi_host_transfer,
 };
 
+/*
+ * List of supported physical to logical lane mappings.
+ * For example, the 2nd entry represents the following mapping:
+ *
+ * "3012": Logic 3->Phys 0; Logic 0->Phys 1; Logic 1->Phys 2; Logic 2->Phys 3;
+ */
+static const int supported_data_lane_swaps[][4] = {
+       { 0, 1, 2, 3 },
+       { 3, 0, 1, 2 },
+       { 2, 3, 0, 1 },
+       { 1, 2, 3, 0 },
+       { 0, 3, 2, 1 },
+       { 1, 0, 3, 2 },
+       { 2, 1, 0, 3 },
+       { 3, 2, 1, 0 },
+};
+
+static int dsi_host_parse_lane_data(struct msm_dsi_host *msm_host,
+                                   struct device_node *ep)
+{
+       struct device *dev = &msm_host->pdev->dev;
+       struct property *prop;
+       u32 lane_map[4];
+       int ret, i, len, num_lanes;
+
+       prop = of_find_property(ep, "qcom,data-lane-map", &len);
+       if (!prop) {
+               dev_dbg(dev, "failed to find data lane mapping\n");
+               return -EINVAL;
+       }
+
+       num_lanes = len / sizeof(u32);
+
+       if (num_lanes < 1 || num_lanes > 4) {
+               dev_err(dev, "bad number of data lanes\n");
+               return -EINVAL;
+       }
+
+       msm_host->num_data_lanes = num_lanes;
+
+       ret = of_property_read_u32_array(ep, "qcom,data-lane-map", lane_map,
+                                        num_lanes);
+       if (ret) {
+               dev_err(dev, "failed to read lane data\n");
+               return ret;
+       }
+
+       /*
+        * compare DT specified physical-logical lane mappings with the ones
+        * supported by hardware
+        */
+       for (i = 0; i < ARRAY_SIZE(supported_data_lane_swaps); i++) {
+               const int *swap = supported_data_lane_swaps[i];
+               int j;
+
+               for (j = 0; j < num_lanes; j++) {
+                       if (swap[j] != lane_map[j])
+                               break;
+               }
+
+               if (j == num_lanes) {
+                       msm_host->dlane_swap = i;
+                       return 0;
+               }
+       }
+
+       return -EINVAL;
+}
+
 static int dsi_host_parse_dt(struct msm_dsi_host *msm_host)
 {
        struct device *dev = &msm_host->pdev->dev;
@@ -1558,17 +1625,21 @@ static int dsi_host_parse_dt(struct msm_dsi_host *msm_host)
                return 0;
        }
 
+       ret = dsi_host_parse_lane_data(msm_host, endpoint);
+       if (ret) {
+               dev_err(dev, "%s: invalid lane configuration %d\n",
+                       __func__, ret);
+               goto err;
+       }
+
        /* Get panel node from the output port's endpoint data */
        device_node = of_graph_get_remote_port_parent(endpoint);
        if (!device_node) {
                dev_err(dev, "%s: no valid device\n", __func__);
-               of_node_put(endpoint);
-               return -ENODEV;
+               ret = -ENODEV;
+               goto err;
        }
 
-       of_node_put(endpoint);
-       of_node_put(device_node);
-
        msm_host->device_node = device_node;
 
        if (of_property_read_bool(np, "syscon-sfpb")) {
@@ -1577,11 +1648,16 @@ static int dsi_host_parse_dt(struct msm_dsi_host *msm_host)
                if (IS_ERR(msm_host->sfpb)) {
                        dev_err(dev, "%s: failed to get sfpb regmap\n",
                                __func__);
-                       return PTR_ERR(msm_host->sfpb);
+                       ret = PTR_ERR(msm_host->sfpb);
                }
        }
 
-       return 0;
+       of_node_put(device_node);
+
+err:
+       of_node_put(endpoint);
+
+       return ret;
 }
 
 int msm_dsi_host_init(struct msm_dsi *msm_dsi)