|
@@ -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)
|