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 <jank@cadence.com> Acked-by: Maxime Ripard <maxime.ripard@bootlin.com> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com> Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
This commit is contained in:
parent
6ded416d4a
commit
050ff2ad1c
|
@ -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 @@ static int csi2tx_check_lanes(struct csi2tx_priv *csi2tx)
|
|||
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,
|
||||
|
|
Loading…
Reference in New Issue