This patch adds support for CSI2RX v2.1 version of the controller.
Signed-off-by: Jan Kotas <[email protected]>
---
drivers/media/platform/cadence/cdns-csi2rx.c | 139 ++++++++++++++++++++++-----
1 file changed, 116 insertions(+), 23 deletions(-)
diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c
index 97ec09e72..aa1d46111 100644
--- a/drivers/media/platform/cadence/cdns-csi2rx.c
+++ b/drivers/media/platform/cadence/cdns-csi2rx.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * Driver for Cadence MIPI-CSI2 RX Controller v1.3
+ * Driver for Cadence MIPI-CSI2 RX Controller
*
* Copyright (C) 2017-2019 Cadence Design Systems Inc.
*/
@@ -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
Hi Jan,
On Thu, Sep 05, 2019 at 11:56:01AM +0100, Jan Kotas wrote:
> This patch adds support for CSI2RX v2.1 version of the controller.
>
> Signed-off-by: Jan Kotas <[email protected]>
> ---
> drivers/media/platform/cadence/cdns-csi2rx.c | 139 ++++++++++++++++++++++-----
> 1 file changed, 116 insertions(+), 23 deletions(-)
>
> diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c
> index 97ec09e72..aa1d46111 100644
> --- a/drivers/media/platform/cadence/cdns-csi2rx.c
> +++ b/drivers/media/platform/cadence/cdns-csi2rx.c
> @@ -1,6 +1,6 @@
> // SPDX-License-Identifier: GPL-2.0+
> /*
> - * Driver for Cadence MIPI-CSI2 RX Controller v1.3
> + * Driver for Cadence MIPI-CSI2 RX Controller
> *
> * Copyright (C) 2017-2019 Cadence Design Systems Inc.
> */
> @@ -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;
const?
>
> 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;
Could you spell out these a little, by adding #defines for the register
bits? Same below.
> +}
> +
> +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;
The cast isn't needed, is it?
> +
> 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,
--
Kind regards,
Sakari Ailus
Hi Jan,
On Mon, Sep 09, 2019 at 09:24:15AM +0000, Jan Kotas wrote:
> >> @@ -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;
> >
> > The cast isn't needed, is it?
>
> It’s not, but doesn’t make it more readable?
If there's no need for it, please don't use explicit cast.
--
Sakari Ailus
Hello Sakari,
Thanks for the review.
> On 6 Sep 2019, at 10:01, Sakari Ailus <[email protected]> wrote:
>
> Hi Jan,
>
> On Thu, Sep 05, 2019 at 11:56:01AM +0100, Jan Kotas wrote:
>> This patch adds support for CSI2RX v2.1 version of the controller.
>>
>> Signed-off-by: Jan Kotas <[email protected]>
>> ---
>> drivers/media/platform/cadence/cdns-csi2rx.c | 139 ++++++++++++++++++++++-----
>> 1 file changed, 116 insertions(+), 23 deletions(-)
>>
>> 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;
>
> const?
I’ll add it, thanks.
>>
>> 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;
>
> Could you spell out these a little, by adding #defines for the register
> bits? Same below.
I tried to keep impact on the original code to the minimum,
but you’re right. It’s a good opportunity for a cleanup.
>>
>> @@ -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;
>
> The cast isn't needed, is it?
It’s not, but doesn’t make it more readable?
> --
> Kind regards,
>
> Sakari Ailus
Regards,
Jan