From 050ff2ad1cca6fe6fcb6edde6e3b91f7e47d1c9c Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Mon, 22 Jul 2019 04:22:23 -0400 Subject: [PATCH] media: Add support for Cadence CSI2TX 2.1 This patch adds support for CSI2TX v2.1 version of the controller. Signed-off-by: Jan Kotas Acked-by: Maxime Ripard Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/cadence/cdns-csi2tx.c | 144 +++++++++++++++---- 1 file changed, 113 insertions(+), 31 deletions(-) diff --git a/drivers/media/platform/cadence/cdns-csi2tx.c b/drivers/media/platform/cadence/cdns-csi2tx.c index c72c8a065428..e4d08acfbb49 100644 --- a/drivers/media/platform/cadence/cdns-csi2tx.c +++ b/drivers/media/platform/cadence/cdns-csi2tx.c @@ -52,6 +52,17 @@ #define CSI2TX_STREAM_IF_CFG_REG(n) (0x100 + (n) * 4) #define CSI2TX_STREAM_IF_CFG_FILL_LEVEL(n) ((n) & 0x1f) +/* CSI2TX V2 Registers */ +#define CSI2TX_V2_DPHY_CFG_REG 0x28 +#define CSI2TX_V2_DPHY_CFG_RESET BIT(16) +#define CSI2TX_V2_DPHY_CFG_CLOCK_MODE BIT(10) +#define CSI2TX_V2_DPHY_CFG_MODE_MASK GENMASK(9, 8) +#define CSI2TX_V2_DPHY_CFG_MODE_LPDT (2 << 8) +#define CSI2TX_V2_DPHY_CFG_MODE_HS (1 << 8) +#define CSI2TX_V2_DPHY_CFG_MODE_ULPS (0 << 8) +#define CSI2TX_V2_DPHY_CFG_CLK_ENABLE BIT(4) +#define CSI2TX_V2_DPHY_CFG_LANE_ENABLE(n) BIT(n) + #define CSI2TX_LANES_MAX 4 #define CSI2TX_STREAMS_MAX 4 @@ -70,6 +81,13 @@ struct csi2tx_fmt { u32 bpp; }; +struct csi2tx_priv; + +/* CSI2TX Variant Operations */ +struct csi2tx_vops { + void (*dphy_setup)(struct csi2tx_priv *csi2tx); +}; + struct csi2tx_priv { struct device *dev; unsigned int count; @@ -82,6 +100,8 @@ struct csi2tx_priv { void __iomem *base; + struct csi2tx_vops *vops; + struct clk *esc_clk; struct clk *p_clk; struct clk *pixel_clk[CSI2TX_STREAMS_MAX]; @@ -209,35 +229,20 @@ static const struct v4l2_subdev_pad_ops csi2tx_pad_ops = { .set_fmt = csi2tx_set_pad_format, }; -static void csi2tx_reset(struct csi2tx_priv *csi2tx) +/* Set Wake Up value in the D-PHY */ +static void csi2tx_dphy_set_wakeup(struct csi2tx_priv *csi2tx) { - writel(CSI2TX_CONFIG_SRST_REQ, csi2tx->base + CSI2TX_CONFIG_REG); - - udelay(10); -} - -static int csi2tx_start(struct csi2tx_priv *csi2tx) -{ - struct media_entity *entity = &csi2tx->subdev.entity; - struct media_link *link; - unsigned int i; - u32 reg; - - csi2tx_reset(csi2tx); - - writel(CSI2TX_CONFIG_CFG_REQ, csi2tx->base + CSI2TX_CONFIG_REG); - - udelay(10); - - /* Configure our PPI interface with the D-PHY */ writel(CSI2TX_DPHY_CLK_WAKEUP_ULPS_CYCLES(32), csi2tx->base + CSI2TX_DPHY_CLK_WAKEUP_REG); +} - /* Put our lanes (clock and data) out of reset */ - reg = CSI2TX_DPHY_CFG_CLK_RESET | CSI2TX_DPHY_CFG_MODE_LPDT; - for (i = 0; i < csi2tx->num_lanes; i++) - reg |= CSI2TX_DPHY_CFG_LANE_RESET(csi2tx->lanes[i] - 1); - writel(reg, csi2tx->base + CSI2TX_DPHY_CFG_REG); +/* + * Finishes the D-PHY initialization + * reg dphy cfg value to be used + */ +static void csi2tx_dphy_init_finish(struct csi2tx_priv *csi2tx, u32 reg) +{ + unsigned int i; udelay(10); @@ -253,8 +258,62 @@ static int csi2tx_start(struct csi2tx_priv *csi2tx) reg &= ~CSI2TX_DPHY_CFG_MODE_MASK; writel(reg | CSI2TX_DPHY_CFG_MODE_HS, csi2tx->base + CSI2TX_DPHY_CFG_REG); +} + +/* Configures D-PHY in CSIv1.3 */ +static void csi2tx_dphy_setup(struct csi2tx_priv *csi2tx) +{ + u32 reg; + unsigned int i; + + csi2tx_dphy_set_wakeup(csi2tx); + + /* Put our lanes (clock and data) out of reset */ + reg = CSI2TX_DPHY_CFG_CLK_RESET | CSI2TX_DPHY_CFG_MODE_LPDT; + for (i = 0; i < csi2tx->num_lanes; i++) + reg |= CSI2TX_DPHY_CFG_LANE_RESET(csi2tx->lanes[i] - 1); + writel(reg, csi2tx->base + CSI2TX_DPHY_CFG_REG); + + csi2tx_dphy_init_finish(csi2tx, reg); +} + +/* Configures D-PHY in CSIv2 */ +static void csi2tx_v2_dphy_setup(struct csi2tx_priv *csi2tx) +{ + u32 reg; + + csi2tx_dphy_set_wakeup(csi2tx); + + /* Put our lanes (clock and data) out of reset */ + reg = CSI2TX_V2_DPHY_CFG_RESET | CSI2TX_V2_DPHY_CFG_MODE_LPDT; + writel(reg, csi2tx->base + CSI2TX_V2_DPHY_CFG_REG); + + csi2tx_dphy_init_finish(csi2tx, reg); +} + +static void csi2tx_reset(struct csi2tx_priv *csi2tx) +{ + writel(CSI2TX_CONFIG_SRST_REQ, csi2tx->base + CSI2TX_CONFIG_REG); udelay(10); +} + +static int csi2tx_start(struct csi2tx_priv *csi2tx) +{ + struct media_entity *entity = &csi2tx->subdev.entity; + struct media_link *link; + unsigned int i; + + csi2tx_reset(csi2tx); + + writel(CSI2TX_CONFIG_CFG_REQ, csi2tx->base + CSI2TX_CONFIG_REG); + + udelay(10); + + if (csi2tx->vops && csi2tx->vops->dphy_setup) { + csi2tx->vops->dphy_setup(csi2tx); + udelay(10); + } /* * Create a static mapping between the CSI virtual channels @@ -478,9 +537,35 @@ out: return ret; } +static const struct csi2tx_vops csi2tx_vops = { + .dphy_setup = csi2tx_dphy_setup, +}; + +static const struct csi2tx_vops csi2tx_v2_vops = { + .dphy_setup = csi2tx_v2_dphy_setup, +}; + +static const struct of_device_id csi2tx_of_table[] = { + { + .compatible = "cdns,csi2tx", + .data = &csi2tx_vops + }, + { + .compatible = "cdns,csi2tx-1.3", + .data = &csi2tx_vops + }, + { + .compatible = "cdns,csi2tx-2.1", + .data = &csi2tx_v2_vops + }, + { } +}; +MODULE_DEVICE_TABLE(of, csi2tx_of_table); + static int csi2tx_probe(struct platform_device *pdev) { struct csi2tx_priv *csi2tx; + const struct of_device_id *of_id; unsigned int i; int ret; @@ -495,6 +580,9 @@ static int csi2tx_probe(struct platform_device *pdev) if (ret) goto err_free_priv; + of_id = of_match_node(csi2tx_of_table, pdev->dev.of_node); + csi2tx->vops = (struct csi2tx_vops *)of_id->data; + v4l2_subdev_init(&csi2tx->subdev, &csi2tx_subdev_ops); csi2tx->subdev.owner = THIS_MODULE; csi2tx->subdev.dev = &pdev->dev; @@ -552,12 +640,6 @@ static int csi2tx_remove(struct platform_device *pdev) return 0; } -static const struct of_device_id csi2tx_of_table[] = { - { .compatible = "cdns,csi2tx" }, - { }, -}; -MODULE_DEVICE_TABLE(of, csi2tx_of_table); - static struct platform_driver csi2tx_driver = { .probe = csi2tx_probe, .remove = csi2tx_remove,