The newer version of DSS (AM625-DSS) has 2 OLDI TXes at its disposal.
These can be configured to support the following modes:

1. OLDI_SINGLE_LINK_SINGLE_MODE
Single Output over OLDI 0.
+------+        +---------+      +-------+
|      |        |         |      |       |
| CRTC +------->+ ENCODER +----->| PANEL |
|      |        |         |      |       |
+------+        +---------+      +-------+

2. OLDI_SINGLE_LINK_CLONE_MODE
Duplicate Output over OLDI 0 and 1.
+------+        +---------+      +-------+
|      |        |         |      |       |
| CRTC +---+--->| ENCODER +----->| PANEL |
|      |   |    |         |      |       |
+------+   |    +---------+      +-------+
           |
           |    +---------+      +-------+
           |    |         |      |       |
           +--->| ENCODER +----->| PANEL |
                |         |      |       |
                +---------+      +-------+

3. OLDI_DUAL_LINK_MODE
Combined Output over OLDI 0 and 1.
+------+        +---------+      +-------+
|      |        |         +----->|       |
| CRTC +------->+ ENCODER |      | PANEL |
|      |        |         +----->|       |
+------+        +---------+      +-------+

Following the above pathways for different modes, 2 encoder/panel-bridge
pipes get created for clone mode, and 1 pipe in cases of single link and
dual link mode.

Add support for confgure the OLDI modes using of and lvds DRM helper
functions.

Signed-off-by: Aradhya Bhatia <a-bhat...@ti.com>
---
 drivers/gpu/drm/tidss/tidss_dispc.c |  11 +++
 drivers/gpu/drm/tidss/tidss_dispc.h |   8 ++
 drivers/gpu/drm/tidss/tidss_drv.h   |   3 +
 drivers/gpu/drm/tidss/tidss_kms.c   | 146 +++++++++++++++++++++++-----
 4 files changed, 145 insertions(+), 23 deletions(-)

diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c 
b/drivers/gpu/drm/tidss/tidss_dispc.c
index 34f0da4bb3e3..88008ad39b55 100644
--- a/drivers/gpu/drm/tidss/tidss_dispc.c
+++ b/drivers/gpu/drm/tidss/tidss_dispc.c
@@ -354,6 +354,8 @@ struct dispc_device {
 
        bool is_enabled;
 
+       enum dispc_oldi_modes oldi_mode;
+
        struct dss_vp_data vp_data[TIDSS_MAX_PORTS];
 
        u32 *fourccs;
@@ -1958,6 +1960,15 @@ const u32 *dispc_plane_formats(struct dispc_device 
*dispc, unsigned int *len)
        return dispc->fourccs;
 }
 
+int dispc_configure_oldi_mode(struct dispc_device *dispc,
+                             enum dispc_oldi_modes oldi_mode)
+{
+       WARN_ON(!dispc);
+
+       dispc->oldi_mode = oldi_mode;
+       return 0;
+}
+
 static s32 pixinc(int pixels, u8 ps)
 {
        if (pixels == 1)
diff --git a/drivers/gpu/drm/tidss/tidss_dispc.h 
b/drivers/gpu/drm/tidss/tidss_dispc.h
index b66418e583ee..45cce1054832 100644
--- a/drivers/gpu/drm/tidss/tidss_dispc.h
+++ b/drivers/gpu/drm/tidss/tidss_dispc.h
@@ -64,6 +64,13 @@ enum dispc_dss_subrevision {
        DISPC_AM625,
 };
 
+enum dispc_oldi_modes {
+       OLDI_MODE_OFF,                          /* OLDI turned off / tied off 
in IP. */
+       OLDI_SINGLE_LINK_SINGLE_MODE,           /* Single Output over OLDI 0. */
+       OLDI_SINGLE_LINK_CLONE_MODE,            /* Duplicate Output over OLDI 0 
and 1. */
+       OLDI_DUAL_LINK_MODE,                    /* Combined Output over OLDI 0 
and 1. */
+};
+
 struct dispc_features {
        int min_pclk_khz;
        int max_pclk_khz[DISPC_VP_MAX_BUS_TYPE];
@@ -131,6 +138,7 @@ int dispc_plane_setup(struct dispc_device *dispc, u32 
hw_plane,
                      u32 hw_videoport);
 int dispc_plane_enable(struct dispc_device *dispc, u32 hw_plane, bool enable);
 const u32 *dispc_plane_formats(struct dispc_device *dispc, unsigned int *len);
+int dispc_configure_oldi_mode(struct dispc_device *dispc, enum 
dispc_oldi_modes oldi_mode);
 
 int dispc_init(struct tidss_device *tidss);
 void dispc_remove(struct tidss_device *tidss);
diff --git a/drivers/gpu/drm/tidss/tidss_drv.h 
b/drivers/gpu/drm/tidss/tidss_drv.h
index d7f27b0b0315..2252ba0222ca 100644
--- a/drivers/gpu/drm/tidss/tidss_drv.h
+++ b/drivers/gpu/drm/tidss/tidss_drv.h
@@ -12,6 +12,9 @@
 #define TIDSS_MAX_PORTS 4
 #define TIDSS_MAX_PLANES 4
 
+/* For AM625-DSS with 2 OLDI TXes */
+#define TIDSS_MAX_BRIDGE_PER_PIPE      2
+
 typedef u32 dispc_irq_t;
 
 struct tidss_device {
diff --git a/drivers/gpu/drm/tidss/tidss_kms.c 
b/drivers/gpu/drm/tidss/tidss_kms.c
index 666e527a0acf..73afe390f36d 100644
--- a/drivers/gpu/drm/tidss/tidss_kms.c
+++ b/drivers/gpu/drm/tidss/tidss_kms.c
@@ -107,32 +107,84 @@ static const struct drm_mode_config_funcs 
mode_config_funcs = {
        .atomic_commit = drm_atomic_helper_commit,
 };
 
+static int tidss_get_oldi_mode(struct tidss_device *tidss)
+{
+       int pixel_order;
+       struct device_node *dss_ports, *oldi0_port, *oldi1_port;
+
+       dss_ports = of_get_next_child(tidss->dev->of_node, NULL);
+       oldi0_port = of_graph_get_port_by_id(dss_ports, 0);
+       oldi1_port = of_graph_get_port_by_id(dss_ports, 2);
+
+       if (!(oldi0_port && oldi1_port))
+               return OLDI_SINGLE_LINK_SINGLE_MODE;
+
+       /*
+        * OLDI Ports found for both the OLDI TXes. The DSS is to be configured
+        * in either Dual Link or Clone Mode.
+        */
+       pixel_order = drm_of_lvds_get_dual_link_pixel_order(oldi0_port,
+                                                           oldi1_port);
+       switch (pixel_order) {
+       case -EINVAL:
+               /*
+                * The dual link properties were not found in at least one of
+                * the sink nodes. Since 2 OLDI ports are present in the DT, it
+                * can be safely assumed that the required configuration is
+                * Clone Mode.
+                */
+               return OLDI_SINGLE_LINK_CLONE_MODE;
+
+       case DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS:
+       case DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS:
+               /*
+                * Note that the OLDI TX 0 transmits the odd set of pixels while
+                * the OLDI TX 1 transmits the even set. This is a fixed
+                * configuration in the IP and an cannot be change vis SW. These
+                * properties have been used to merely identify if a Dual Link
+                * configuration is required. Swapping this property in the 
panel
+                * port DT nodes will not make any difference.
+                */
+               return OLDI_DUAL_LINK_MODE;
+
+       default:
+               return OLDI_MODE_OFF;
+       }
+}
+
 static int tidss_dispc_modeset_init(struct tidss_device *tidss)
 {
        struct device *dev = tidss->dev;
        unsigned int fourccs_len;
        const u32 *fourccs = dispc_plane_formats(tidss->dispc, &fourccs_len);
-       unsigned int i;
+       unsigned int i, j;
 
        struct pipe {
                u32 hw_videoport;
-               struct drm_bridge *bridge;
+               struct drm_bridge *bridge[TIDSS_MAX_BRIDGE_PER_PIPE];
                u32 enc_type;
+               u32 num_bridges;
        };
 
        const struct dispc_features *feat = tidss->feat;
-       u32 max_vps = feat->num_vps;
+       u32 max_ports = feat->num_max_ports;
        u32 max_planes = feat->num_planes;
 
        struct pipe pipes[TIDSS_MAX_PORTS];
        u32 num_pipes = 0;
+       u32 pipe_number = 0;
        u32 crtc_mask;
+       u32 num_oldi = 0;
+       u32 oldi0_port = 0;
+       u32 hw_vp = 0;
+       enum dispc_oldi_modes oldi_mode;
 
        /* first find all the connected panels & bridges */
 
-       for (i = 0; i < max_vps; i++) {
+       for (i = 0; i < max_ports; i++) {
                struct drm_panel *panel;
                struct drm_bridge *bridge;
+               bool bridge_req = true;
                u32 enc_type = DRM_MODE_ENCODER_NONE;
                int ret;
 
@@ -146,6 +198,11 @@ static int tidss_dispc_modeset_init(struct tidss_device 
*tidss)
                        return ret;
                }
 
+               /* default number of bridges required for a panel/bridge*/
+               pipe_number = num_pipes;
+               pipes[pipe_number].num_bridges = 1;
+               hw_vp = i;
+
                if (panel) {
                        u32 conn_type;
 
@@ -155,7 +212,43 @@ static int tidss_dispc_modeset_init(struct tidss_device 
*tidss)
                        case DISPC_VP_OLDI:
                                enc_type = DRM_MODE_ENCODER_LVDS;
                                conn_type = DRM_MODE_CONNECTOR_LVDS;
+
+                               /*
+                                * A single DSS controller cannot support 2
+                                * independent displays. If 2nd node is 
detected,
+                                * it is for Dual Link Mode or Clone Mode.
+                                *
+                                * A new pipe instance is not required.
+                                */
+                               if (++num_oldi == 2) {
+                                       pipe_number = oldi0_port;
+                                       hw_vp = i;
+
+                                       /* 2nd OLDI DT node detected. Get its 
mode */
+                                       oldi_mode = tidss_get_oldi_mode(tidss);
+                                       bridge_req = false;
+
+                                       /*
+                                        * A separate panel bridge will only be
+                                        * required if 2 panels are connected 
for
+                                        * the OLDI Clone Mode.
+                                        */
+                                       if (oldi_mode == 
OLDI_SINGLE_LINK_CLONE_MODE) {
+                                               bridge_req = true;
+                                               
(pipes[pipe_number].num_bridges)++;
+                                       }
+                               } else {
+                                       /*
+                                        * First OLDI DT node detected. Save it
+                                        * in case there is another node for 
Dual
+                                        * Link Mode or Clone Mode.
+                                        */
+                                       oldi0_port = i;
+                                       oldi_mode = 
OLDI_SINGLE_LINK_SINGLE_MODE;
+                               }
+                               dispc_configure_oldi_mode(tidss->dispc, 
oldi_mode);
                                break;
+
                        case DISPC_VP_DPI:
                                enc_type = DRM_MODE_ENCODER_DPI;
                                conn_type = DRM_MODE_CONNECTOR_DPI;
@@ -173,19 +266,23 @@ static int tidss_dispc_modeset_init(struct tidss_device 
*tidss)
                                return -EINVAL;
                        }
 
-                       bridge = devm_drm_panel_bridge_add(dev, panel);
-                       if (IS_ERR(bridge)) {
-                               dev_err(dev,
-                                       "failed to set up panel bridge for port 
%d\n",
-                                       i);
-                               return PTR_ERR(bridge);
+                       if (bridge_req) {
+                               bridge = devm_drm_panel_bridge_add(dev, panel);
+                               if (IS_ERR(bridge)) {
+                                       dev_err(dev,
+                                               "failed to set up panel bridge 
for port %d\n",
+                                               i);
+                                       return PTR_ERR(bridge);
+                               }
                        }
                }
 
-               pipes[num_pipes].hw_videoport = i;
-               pipes[num_pipes].bridge = bridge;
-               pipes[num_pipes].enc_type = enc_type;
-               num_pipes++;
+               if (bridge_req) {
+                       pipes[pipe_number].hw_videoport = hw_vp;
+                       
pipes[pipe_number].bridge[pipes[pipe_number].num_bridges - 1] = bridge;
+                       pipes[pipe_number].enc_type = enc_type;
+                       num_pipes++;
+               }
        }
 
        /* all planes can be on any crtc */
@@ -200,6 +297,7 @@ static int tidss_dispc_modeset_init(struct tidss_device 
*tidss)
                u32 hw_plane_id = feat->vid_order[tidss->num_planes];
                int ret;
 
+               /* Creating planes and CRTCs only for real pipes */
                tplane = tidss_plane_create(tidss, hw_plane_id,
                                            DRM_PLANE_TYPE_PRIMARY, crtc_mask,
                                            fourccs, fourccs_len);
@@ -219,16 +317,18 @@ static int tidss_dispc_modeset_init(struct tidss_device 
*tidss)
 
                tidss->crtcs[tidss->num_crtcs++] = &tcrtc->crtc;
 
-               enc = tidss_encoder_create(tidss, pipes[i].enc_type,
-                                          1 << tcrtc->crtc.index);
-               if (IS_ERR(enc)) {
-                       dev_err(tidss->dev, "encoder create failed\n");
-                       return PTR_ERR(enc);
-               }
+               for (j = 0; j < pipes[i].num_bridges; j++) {
+                       enc = tidss_encoder_create(tidss, pipes[i].enc_type,
+                                                  1 << tcrtc->crtc.index);
+                       if (IS_ERR(enc)) {
+                               dev_err(tidss->dev, "encoder create failed\n");
+                               return PTR_ERR(enc);
+                       }
 
-               ret = drm_bridge_attach(enc, pipes[i].bridge, NULL, 0);
-               if (ret)
-                       return ret;
+                       ret = drm_bridge_attach(enc, pipes[i].bridge[j], NULL, 
0);
+                       if (ret)
+                               return ret;
+               }
        }
 
        /* create overlay planes of the leftover planes */
-- 
2.37.0

Reply via email to