This patch adds support for CSI2RX v2.1 version of the controller.

Signed-off-by: Jan Kotas <j...@cadence.com>
---
 drivers/media/platform/cadence/cdns-csi2rx.c | 137 ++++++++++++++++++++++-----
 1 file changed, 115 insertions(+), 22 deletions(-)

diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c 
b/drivers/media/platform/cadence/cdns-csi2rx.c
index 97ec09e72..4c76c6490 100644
--- a/drivers/media/platform/cadence/cdns-csi2rx.c
+++ b/drivers/media/platform/cadence/cdns-csi2rx.c
@@ -26,6 +26,9 @@
 #define CSI2RX_SOFT_RESET_PROTOCOL                     BIT(1)
 #define CSI2RX_SOFT_RESET_FRONT                                BIT(0)
 
+#define CSI2RX_V2_CORE_CTRL_REG                        0x004
+#define CSI2RX_V2_CORE_CTRL_START                      BIT(0)
+
 #define CSI2RX_STATIC_CFG_REG                  0x008
 #define CSI2RX_STATIC_CFG_DLANE_MAP(llane, plane)      ((plane) << (16 + 
(llane) * 4))
 #define CSI2RX_STATIC_CFG_LANES_MASK                   GENMASK(11, 8)
@@ -54,6 +57,15 @@ enum csi2rx_pads {
        CSI2RX_PAD_MAX,
 };
 
+struct csi2rx_priv;
+
+/* CSI2RX Variant Operations */
+struct csi2rx_vops {
+       void (*get_dev_cfg)(struct csi2rx_priv *csi2rx);
+       void (*reset)(struct csi2rx_priv *csi2rx);
+       void (*map_static)(struct csi2rx_priv *csi2rx);
+};
+
 struct csi2rx_priv {
        struct device                   *dev;
        unsigned int                    count;
@@ -69,6 +81,7 @@ struct csi2rx_priv {
        struct clk                      *p_clk;
        struct clk                      *pixel_clk[CSI2RX_STREAMS_MAX];
        struct phy                      *dphy;
+       struct csi2rx_vops              *vops;
 
        u8                              lanes[CSI2RX_LANES_MAX];
        u8                              num_lanes;
@@ -92,6 +105,32 @@ struct csi2rx_priv *v4l2_subdev_to_csi2rx(struct 
v4l2_subdev *subdev)
        return container_of(subdev, struct csi2rx_priv, subdev);
 }
 
+static void csi2rx_get_dev_cfg(struct csi2rx_priv *csi2rx)
+{
+       u32 dev_cfg;
+
+       clk_prepare_enable(csi2rx->p_clk);
+       dev_cfg = readl(csi2rx->base + CSI2RX_DEVICE_CFG_REG);
+       clk_disable_unprepare(csi2rx->p_clk);
+
+       csi2rx->max_lanes = dev_cfg & 7;
+       csi2rx->max_streams = (dev_cfg >> 4) & 7;
+       csi2rx->has_internal_dphy = dev_cfg & BIT(3) ? true : false;
+}
+
+static void csi2rx_v2_get_dev_cfg(struct csi2rx_priv *csi2rx)
+{
+       u32 dev_cfg;
+
+       clk_prepare_enable(csi2rx->p_clk);
+       dev_cfg = readl(csi2rx->base + CSI2RX_DEVICE_CFG_REG);
+       clk_disable_unprepare(csi2rx->p_clk);
+
+       csi2rx->max_lanes = dev_cfg & 0xF;
+       csi2rx->max_streams = (dev_cfg >> 5) & 0xF;
+       csi2rx->has_internal_dphy = dev_cfg & BIT(4) ? true : false;
+}
+
 static void csi2rx_reset(struct csi2rx_priv *csi2rx)
 {
        writel(CSI2RX_SOFT_RESET_PROTOCOL | CSI2RX_SOFT_RESET_FRONT,
@@ -102,18 +141,21 @@ static void csi2rx_reset(struct csi2rx_priv *csi2rx)
        writel(0, csi2rx->base + CSI2RX_SOFT_RESET_REG);
 }
 
-static int csi2rx_start(struct csi2rx_priv *csi2rx)
+static void csi2rx_v2_reset(struct csi2rx_priv *csi2rx)
+{
+       writel(0, csi2rx->base + CSI2RX_V2_CORE_CTRL_REG);
+
+       udelay(10);
+
+       writel(CSI2RX_V2_CORE_CTRL_START,
+              csi2rx->base + CSI2RX_V2_CORE_CTRL_REG);
+}
+
+static void csi2rx_map_static(struct csi2rx_priv *csi2rx)
 {
        unsigned int i;
        unsigned long lanes_used = 0;
        u32 reg;
-       int ret;
-
-       ret = clk_prepare_enable(csi2rx->p_clk);
-       if (ret)
-               return ret;
-
-       csi2rx_reset(csi2rx);
 
        reg = csi2rx->num_lanes << 8;
        for (i = 0; i < csi2rx->num_lanes; i++) {
@@ -135,6 +177,32 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)
        }
 
        writel(reg, csi2rx->base + CSI2RX_STATIC_CFG_REG);
+}
+
+static void csi2rx_v2_map_static(struct csi2rx_priv *csi2rx)
+{
+       u32 reg;
+
+       reg = csi2rx->num_lanes << 4;
+       writel(reg, csi2rx->base + CSI2RX_STATIC_CFG_REG);
+}
+
+static int csi2rx_start(struct csi2rx_priv *csi2rx)
+{
+       unsigned int i;
+       int ret;
+
+       ret = clk_prepare_enable(csi2rx->p_clk);
+       if (ret)
+               return ret;
+
+       if (csi2rx->vops) {
+               if (csi2rx->vops->reset)
+                       csi2rx->vops->reset(csi2rx);
+
+               if (csi2rx->vops->map_static)
+                       csi2rx->vops->map_static(csi2rx);
+       }
 
        ret = v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, true);
        if (ret)
@@ -282,7 +350,6 @@ static int csi2rx_get_resources(struct csi2rx_priv *csi2rx,
 {
        struct resource *res;
        unsigned char i;
-       u32 dev_cfg;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        csi2rx->base = devm_ioremap_resource(&pdev->dev, res);
@@ -316,26 +383,25 @@ static int csi2rx_get_resources(struct csi2rx_priv 
*csi2rx,
                return -EINVAL;
        }
 
-       clk_prepare_enable(csi2rx->p_clk);
-       dev_cfg = readl(csi2rx->base + CSI2RX_DEVICE_CFG_REG);
-       clk_disable_unprepare(csi2rx->p_clk);
+       if (csi2rx->vops && csi2rx->vops->get_dev_cfg) {
+               csi2rx->vops->get_dev_cfg(csi2rx);
+       } else {
+               dev_err(&pdev->dev, "Couldn't get device configuration\n");
+               return -EINVAL;
+       }
 
-       csi2rx->max_lanes = dev_cfg & 7;
        if (csi2rx->max_lanes > CSI2RX_LANES_MAX) {
                dev_err(&pdev->dev, "Invalid number of lanes: %u\n",
                        csi2rx->max_lanes);
                return -EINVAL;
        }
 
-       csi2rx->max_streams = (dev_cfg >> 4) & 7;
        if (csi2rx->max_streams > CSI2RX_STREAMS_MAX) {
                dev_err(&pdev->dev, "Invalid number of streams: %u\n",
                        csi2rx->max_streams);
                return -EINVAL;
        }
 
-       csi2rx->has_internal_dphy = dev_cfg & BIT(3) ? true : false;
-
        /*
         * FIXME: Once we'll have internal D-PHY support, the check
         * will need to be removed.
@@ -426,9 +492,39 @@ static int csi2rx_parse_dt(struct csi2rx_priv *csi2rx)
        return ret;
 }
 
+static const struct csi2rx_vops csi2rx_vops = {
+       .get_dev_cfg = csi2rx_get_dev_cfg,
+       .reset = csi2rx_reset,
+       .map_static = csi2rx_map_static
+};
+
+static const struct csi2rx_vops csi2rx_v2_vops = {
+       .get_dev_cfg = csi2rx_v2_get_dev_cfg,
+       .reset = csi2rx_v2_reset,
+       .map_static = csi2rx_v2_map_static
+};
+
+static const struct of_device_id csi2rx_of_table[] = {
+       {
+               .compatible = "cdns,csi2rx",
+               .data = &csi2rx_vops
+       },
+       {
+               .compatible = "cdns,csi2rx-1.3",
+               .data = &csi2rx_vops
+       },
+       {
+               .compatible = "cdns,csi2rx-2.1",
+               .data = &csi2rx_v2_vops
+       },
+       { }
+};
+MODULE_DEVICE_TABLE(of, csi2rx_of_table);
+
 static int csi2rx_probe(struct platform_device *pdev)
 {
        struct csi2rx_priv *csi2rx;
+       const struct of_device_id *of_id;
        unsigned int i;
        int ret;
 
@@ -439,6 +535,9 @@ static int csi2rx_probe(struct platform_device *pdev)
        csi2rx->dev = &pdev->dev;
        mutex_init(&csi2rx->lock);
 
+       of_id = of_match_node(csi2rx_of_table, pdev->dev.of_node);
+       csi2rx->vops = (struct csi2rx_vops *)of_id->data;
+
        ret = csi2rx_get_resources(csi2rx, pdev);
        if (ret)
                goto err_free_priv;
@@ -493,12 +592,6 @@ static int csi2rx_remove(struct platform_device *pdev)
        return 0;
 }
 
-static const struct of_device_id csi2rx_of_table[] = {
-       { .compatible = "cdns,csi2rx" },
-       { },
-};
-MODULE_DEVICE_TABLE(of, csi2rx_of_table);
-
 static struct platform_driver csi2rx_driver = {
        .probe  = csi2rx_probe,
        .remove = csi2rx_remove,
-- 
2.15.0

Reply via email to