16f684d4fSMaxime Ripard // SPDX-License-Identifier: GPL-2.0+ 26f684d4fSMaxime Ripard /* 36f684d4fSMaxime Ripard * Driver for Cadence MIPI-CSI2 TX Controller 46f684d4fSMaxime Ripard * 5bf9df90bSJan Kotas * Copyright (C) 2017-2019 Cadence Design Systems Inc. 66f684d4fSMaxime Ripard */ 76f684d4fSMaxime Ripard 86f684d4fSMaxime Ripard #include <linux/clk.h> 96f684d4fSMaxime Ripard #include <linux/delay.h> 106f684d4fSMaxime Ripard #include <linux/io.h> 116f684d4fSMaxime Ripard #include <linux/module.h> 126f684d4fSMaxime Ripard #include <linux/mutex.h> 136f684d4fSMaxime Ripard #include <linux/of.h> 146f684d4fSMaxime Ripard #include <linux/of_graph.h> 156f684d4fSMaxime Ripard #include <linux/platform_device.h> 163c46ab9dSArnd Bergmann #include <linux/slab.h> 176f684d4fSMaxime Ripard 186f684d4fSMaxime Ripard #include <media/v4l2-ctrls.h> 196f684d4fSMaxime Ripard #include <media/v4l2-device.h> 206f684d4fSMaxime Ripard #include <media/v4l2-fwnode.h> 216f684d4fSMaxime Ripard #include <media/v4l2-subdev.h> 226f684d4fSMaxime Ripard 236f684d4fSMaxime Ripard #define CSI2TX_DEVICE_CONFIG_REG 0x00 246f684d4fSMaxime Ripard #define CSI2TX_DEVICE_CONFIG_STREAMS_MASK GENMASK(6, 4) 256f684d4fSMaxime Ripard #define CSI2TX_DEVICE_CONFIG_HAS_DPHY BIT(3) 266f684d4fSMaxime Ripard #define CSI2TX_DEVICE_CONFIG_LANES_MASK GENMASK(2, 0) 276f684d4fSMaxime Ripard 286f684d4fSMaxime Ripard #define CSI2TX_CONFIG_REG 0x20 296f684d4fSMaxime Ripard #define CSI2TX_CONFIG_CFG_REQ BIT(2) 306f684d4fSMaxime Ripard #define CSI2TX_CONFIG_SRST_REQ BIT(1) 316f684d4fSMaxime Ripard 326f684d4fSMaxime Ripard #define CSI2TX_DPHY_CFG_REG 0x28 336f684d4fSMaxime Ripard #define CSI2TX_DPHY_CFG_CLK_RESET BIT(16) 346f684d4fSMaxime Ripard #define CSI2TX_DPHY_CFG_LANE_RESET(n) BIT((n) + 12) 356f684d4fSMaxime Ripard #define CSI2TX_DPHY_CFG_MODE_MASK GENMASK(9, 8) 366f684d4fSMaxime Ripard #define CSI2TX_DPHY_CFG_MODE_LPDT (2 << 8) 376f684d4fSMaxime Ripard #define CSI2TX_DPHY_CFG_MODE_HS (1 << 8) 386f684d4fSMaxime Ripard #define CSI2TX_DPHY_CFG_MODE_ULPS (0 << 8) 396f684d4fSMaxime Ripard #define CSI2TX_DPHY_CFG_CLK_ENABLE BIT(4) 406f684d4fSMaxime Ripard #define CSI2TX_DPHY_CFG_LANE_ENABLE(n) BIT(n) 416f684d4fSMaxime Ripard 426f684d4fSMaxime Ripard #define CSI2TX_DPHY_CLK_WAKEUP_REG 0x2c 436f684d4fSMaxime Ripard #define CSI2TX_DPHY_CLK_WAKEUP_ULPS_CYCLES(n) ((n) & 0xffff) 446f684d4fSMaxime Ripard 456f684d4fSMaxime Ripard #define CSI2TX_DT_CFG_REG(n) (0x80 + (n) * 8) 466f684d4fSMaxime Ripard #define CSI2TX_DT_CFG_DT(n) (((n) & 0x3f) << 2) 476f684d4fSMaxime Ripard 486f684d4fSMaxime Ripard #define CSI2TX_DT_FORMAT_REG(n) (0x84 + (n) * 8) 496f684d4fSMaxime Ripard #define CSI2TX_DT_FORMAT_BYTES_PER_LINE(n) (((n) & 0xffff) << 16) 506f684d4fSMaxime Ripard #define CSI2TX_DT_FORMAT_MAX_LINE_NUM(n) ((n) & 0xffff) 516f684d4fSMaxime Ripard 526f684d4fSMaxime Ripard #define CSI2TX_STREAM_IF_CFG_REG(n) (0x100 + (n) * 4) 536f684d4fSMaxime Ripard #define CSI2TX_STREAM_IF_CFG_FILL_LEVEL(n) ((n) & 0x1f) 546f684d4fSMaxime Ripard 55050ff2adSJan Kotas /* CSI2TX V2 Registers */ 56050ff2adSJan Kotas #define CSI2TX_V2_DPHY_CFG_REG 0x28 57050ff2adSJan Kotas #define CSI2TX_V2_DPHY_CFG_RESET BIT(16) 58050ff2adSJan Kotas #define CSI2TX_V2_DPHY_CFG_CLOCK_MODE BIT(10) 59050ff2adSJan Kotas #define CSI2TX_V2_DPHY_CFG_MODE_MASK GENMASK(9, 8) 60050ff2adSJan Kotas #define CSI2TX_V2_DPHY_CFG_MODE_LPDT (2 << 8) 61050ff2adSJan Kotas #define CSI2TX_V2_DPHY_CFG_MODE_HS (1 << 8) 62050ff2adSJan Kotas #define CSI2TX_V2_DPHY_CFG_MODE_ULPS (0 << 8) 63050ff2adSJan Kotas #define CSI2TX_V2_DPHY_CFG_CLK_ENABLE BIT(4) 64050ff2adSJan Kotas #define CSI2TX_V2_DPHY_CFG_LANE_ENABLE(n) BIT(n) 65050ff2adSJan Kotas 666f684d4fSMaxime Ripard #define CSI2TX_LANES_MAX 4 676f684d4fSMaxime Ripard #define CSI2TX_STREAMS_MAX 4 686f684d4fSMaxime Ripard 696f684d4fSMaxime Ripard enum csi2tx_pads { 706f684d4fSMaxime Ripard CSI2TX_PAD_SOURCE, 716f684d4fSMaxime Ripard CSI2TX_PAD_SINK_STREAM0, 726f684d4fSMaxime Ripard CSI2TX_PAD_SINK_STREAM1, 736f684d4fSMaxime Ripard CSI2TX_PAD_SINK_STREAM2, 746f684d4fSMaxime Ripard CSI2TX_PAD_SINK_STREAM3, 756f684d4fSMaxime Ripard CSI2TX_PAD_MAX, 766f684d4fSMaxime Ripard }; 776f684d4fSMaxime Ripard 786f684d4fSMaxime Ripard struct csi2tx_fmt { 796f684d4fSMaxime Ripard u32 mbus; 806f684d4fSMaxime Ripard u32 dt; 816f684d4fSMaxime Ripard u32 bpp; 826f684d4fSMaxime Ripard }; 836f684d4fSMaxime Ripard 84050ff2adSJan Kotas struct csi2tx_priv; 85050ff2adSJan Kotas 86050ff2adSJan Kotas /* CSI2TX Variant Operations */ 87050ff2adSJan Kotas struct csi2tx_vops { 88050ff2adSJan Kotas void (*dphy_setup)(struct csi2tx_priv *csi2tx); 89050ff2adSJan Kotas }; 90050ff2adSJan Kotas 916f684d4fSMaxime Ripard struct csi2tx_priv { 926f684d4fSMaxime Ripard struct device *dev; 936f684d4fSMaxime Ripard unsigned int count; 946f684d4fSMaxime Ripard 956f684d4fSMaxime Ripard /* 966f684d4fSMaxime Ripard * Used to prevent race conditions between multiple, 976f684d4fSMaxime Ripard * concurrent calls to start and stop. 986f684d4fSMaxime Ripard */ 996f684d4fSMaxime Ripard struct mutex lock; 1006f684d4fSMaxime Ripard 1016f684d4fSMaxime Ripard void __iomem *base; 1026f684d4fSMaxime Ripard 103050ff2adSJan Kotas struct csi2tx_vops *vops; 104050ff2adSJan Kotas 1056f684d4fSMaxime Ripard struct clk *esc_clk; 1066f684d4fSMaxime Ripard struct clk *p_clk; 1076f684d4fSMaxime Ripard struct clk *pixel_clk[CSI2TX_STREAMS_MAX]; 1086f684d4fSMaxime Ripard 1096f684d4fSMaxime Ripard struct v4l2_subdev subdev; 1106f684d4fSMaxime Ripard struct media_pad pads[CSI2TX_PAD_MAX]; 1116f684d4fSMaxime Ripard struct v4l2_mbus_framefmt pad_fmts[CSI2TX_PAD_MAX]; 1126f684d4fSMaxime Ripard 1136f684d4fSMaxime Ripard bool has_internal_dphy; 1146f684d4fSMaxime Ripard u8 lanes[CSI2TX_LANES_MAX]; 1156f684d4fSMaxime Ripard unsigned int num_lanes; 1166f684d4fSMaxime Ripard unsigned int max_lanes; 1176f684d4fSMaxime Ripard unsigned int max_streams; 1186f684d4fSMaxime Ripard }; 1196f684d4fSMaxime Ripard 1206f684d4fSMaxime Ripard static const struct csi2tx_fmt csi2tx_formats[] = { 1216f684d4fSMaxime Ripard { 1226f684d4fSMaxime Ripard .mbus = MEDIA_BUS_FMT_UYVY8_1X16, 1236f684d4fSMaxime Ripard .bpp = 2, 1246f684d4fSMaxime Ripard .dt = 0x1e, 1256f684d4fSMaxime Ripard }, 1266f684d4fSMaxime Ripard { 1276f684d4fSMaxime Ripard .mbus = MEDIA_BUS_FMT_RGB888_1X24, 1286f684d4fSMaxime Ripard .bpp = 3, 1296f684d4fSMaxime Ripard .dt = 0x24, 1306f684d4fSMaxime Ripard }, 1316f684d4fSMaxime Ripard }; 1326f684d4fSMaxime Ripard 1336f684d4fSMaxime Ripard static const struct v4l2_mbus_framefmt fmt_default = { 1346f684d4fSMaxime Ripard .width = 1280, 1356f684d4fSMaxime Ripard .height = 720, 1366f684d4fSMaxime Ripard .code = MEDIA_BUS_FMT_RGB888_1X24, 1376f684d4fSMaxime Ripard .field = V4L2_FIELD_NONE, 1386f684d4fSMaxime Ripard .colorspace = V4L2_COLORSPACE_DEFAULT, 1396f684d4fSMaxime Ripard }; 1406f684d4fSMaxime Ripard 1416f684d4fSMaxime Ripard static inline 1426f684d4fSMaxime Ripard struct csi2tx_priv *v4l2_subdev_to_csi2tx(struct v4l2_subdev *subdev) 1436f684d4fSMaxime Ripard { 1446f684d4fSMaxime Ripard return container_of(subdev, struct csi2tx_priv, subdev); 1456f684d4fSMaxime Ripard } 1466f684d4fSMaxime Ripard 1476f684d4fSMaxime Ripard static const struct csi2tx_fmt *csi2tx_get_fmt_from_mbus(u32 mbus) 1486f684d4fSMaxime Ripard { 1496f684d4fSMaxime Ripard unsigned int i; 1506f684d4fSMaxime Ripard 1516f684d4fSMaxime Ripard for (i = 0; i < ARRAY_SIZE(csi2tx_formats); i++) 1526f684d4fSMaxime Ripard if (csi2tx_formats[i].mbus == mbus) 1536f684d4fSMaxime Ripard return &csi2tx_formats[i]; 1546f684d4fSMaxime Ripard 1556f684d4fSMaxime Ripard return NULL; 1566f684d4fSMaxime Ripard } 1576f684d4fSMaxime Ripard 1586f684d4fSMaxime Ripard static int csi2tx_enum_mbus_code(struct v4l2_subdev *subdev, 1590d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 1606f684d4fSMaxime Ripard struct v4l2_subdev_mbus_code_enum *code) 1616f684d4fSMaxime Ripard { 1626f684d4fSMaxime Ripard if (code->pad || code->index >= ARRAY_SIZE(csi2tx_formats)) 1636f684d4fSMaxime Ripard return -EINVAL; 1646f684d4fSMaxime Ripard 1656f684d4fSMaxime Ripard code->code = csi2tx_formats[code->index].mbus; 1666f684d4fSMaxime Ripard 1676f684d4fSMaxime Ripard return 0; 1686f684d4fSMaxime Ripard } 1696f684d4fSMaxime Ripard 1706f684d4fSMaxime Ripard static struct v4l2_mbus_framefmt * 1716f684d4fSMaxime Ripard __csi2tx_get_pad_format(struct v4l2_subdev *subdev, 1720d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 1736f684d4fSMaxime Ripard struct v4l2_subdev_format *fmt) 1746f684d4fSMaxime Ripard { 1756f684d4fSMaxime Ripard struct csi2tx_priv *csi2tx = v4l2_subdev_to_csi2tx(subdev); 1766f684d4fSMaxime Ripard 1776f684d4fSMaxime Ripard if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) 1780d346d2aSTomi Valkeinen return v4l2_subdev_get_try_format(subdev, sd_state, 1796f684d4fSMaxime Ripard fmt->pad); 1806f684d4fSMaxime Ripard 1816f684d4fSMaxime Ripard return &csi2tx->pad_fmts[fmt->pad]; 1826f684d4fSMaxime Ripard } 1836f684d4fSMaxime Ripard 1846f684d4fSMaxime Ripard static int csi2tx_get_pad_format(struct v4l2_subdev *subdev, 1850d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 1866f684d4fSMaxime Ripard struct v4l2_subdev_format *fmt) 1876f684d4fSMaxime Ripard { 1886f684d4fSMaxime Ripard const struct v4l2_mbus_framefmt *format; 1896f684d4fSMaxime Ripard 1906f684d4fSMaxime Ripard /* Multiplexed pad? */ 1916f684d4fSMaxime Ripard if (fmt->pad == CSI2TX_PAD_SOURCE) 1926f684d4fSMaxime Ripard return -EINVAL; 1936f684d4fSMaxime Ripard 1940d346d2aSTomi Valkeinen format = __csi2tx_get_pad_format(subdev, sd_state, fmt); 1956f684d4fSMaxime Ripard if (!format) 1966f684d4fSMaxime Ripard return -EINVAL; 1976f684d4fSMaxime Ripard 1986f684d4fSMaxime Ripard fmt->format = *format; 1996f684d4fSMaxime Ripard 2006f684d4fSMaxime Ripard return 0; 2016f684d4fSMaxime Ripard } 2026f684d4fSMaxime Ripard 2036f684d4fSMaxime Ripard static int csi2tx_set_pad_format(struct v4l2_subdev *subdev, 2040d346d2aSTomi Valkeinen struct v4l2_subdev_state *sd_state, 2056f684d4fSMaxime Ripard struct v4l2_subdev_format *fmt) 2066f684d4fSMaxime Ripard { 2076f684d4fSMaxime Ripard const struct v4l2_mbus_framefmt *src_format = &fmt->format; 2086f684d4fSMaxime Ripard struct v4l2_mbus_framefmt *dst_format; 2096f684d4fSMaxime Ripard 2106f684d4fSMaxime Ripard /* Multiplexed pad? */ 2116f684d4fSMaxime Ripard if (fmt->pad == CSI2TX_PAD_SOURCE) 2126f684d4fSMaxime Ripard return -EINVAL; 2136f684d4fSMaxime Ripard 2146f684d4fSMaxime Ripard if (!csi2tx_get_fmt_from_mbus(fmt->format.code)) 2156f684d4fSMaxime Ripard src_format = &fmt_default; 2166f684d4fSMaxime Ripard 2170d346d2aSTomi Valkeinen dst_format = __csi2tx_get_pad_format(subdev, sd_state, fmt); 2186f684d4fSMaxime Ripard if (!dst_format) 2196f684d4fSMaxime Ripard return -EINVAL; 2206f684d4fSMaxime Ripard 2216f684d4fSMaxime Ripard *dst_format = *src_format; 2226f684d4fSMaxime Ripard 2236f684d4fSMaxime Ripard return 0; 2246f684d4fSMaxime Ripard } 2256f684d4fSMaxime Ripard 2266f684d4fSMaxime Ripard static const struct v4l2_subdev_pad_ops csi2tx_pad_ops = { 2276f684d4fSMaxime Ripard .enum_mbus_code = csi2tx_enum_mbus_code, 2286f684d4fSMaxime Ripard .get_fmt = csi2tx_get_pad_format, 2296f684d4fSMaxime Ripard .set_fmt = csi2tx_set_pad_format, 2306f684d4fSMaxime Ripard }; 2316f684d4fSMaxime Ripard 232050ff2adSJan Kotas /* Set Wake Up value in the D-PHY */ 233050ff2adSJan Kotas static void csi2tx_dphy_set_wakeup(struct csi2tx_priv *csi2tx) 2346f684d4fSMaxime Ripard { 2356f684d4fSMaxime Ripard writel(CSI2TX_DPHY_CLK_WAKEUP_ULPS_CYCLES(32), 2366f684d4fSMaxime Ripard csi2tx->base + CSI2TX_DPHY_CLK_WAKEUP_REG); 237050ff2adSJan Kotas } 2386f684d4fSMaxime Ripard 239050ff2adSJan Kotas /* 240050ff2adSJan Kotas * Finishes the D-PHY initialization 241050ff2adSJan Kotas * reg dphy cfg value to be used 242050ff2adSJan Kotas */ 243050ff2adSJan Kotas static void csi2tx_dphy_init_finish(struct csi2tx_priv *csi2tx, u32 reg) 244050ff2adSJan Kotas { 245050ff2adSJan Kotas unsigned int i; 2466f684d4fSMaxime Ripard 2476f684d4fSMaxime Ripard udelay(10); 2486f684d4fSMaxime Ripard 2496f684d4fSMaxime Ripard /* Enable our (clock and data) lanes */ 2506f684d4fSMaxime Ripard reg |= CSI2TX_DPHY_CFG_CLK_ENABLE; 2516f684d4fSMaxime Ripard for (i = 0; i < csi2tx->num_lanes; i++) 2526ded416dSJan Kotas reg |= CSI2TX_DPHY_CFG_LANE_ENABLE(csi2tx->lanes[i] - 1); 2536f684d4fSMaxime Ripard writel(reg, csi2tx->base + CSI2TX_DPHY_CFG_REG); 2546f684d4fSMaxime Ripard 2556f684d4fSMaxime Ripard udelay(10); 2566f684d4fSMaxime Ripard 2576f684d4fSMaxime Ripard /* Switch to HS mode */ 2586f684d4fSMaxime Ripard reg &= ~CSI2TX_DPHY_CFG_MODE_MASK; 2596f684d4fSMaxime Ripard writel(reg | CSI2TX_DPHY_CFG_MODE_HS, 2606f684d4fSMaxime Ripard csi2tx->base + CSI2TX_DPHY_CFG_REG); 261050ff2adSJan Kotas } 262050ff2adSJan Kotas 263050ff2adSJan Kotas /* Configures D-PHY in CSIv1.3 */ 264050ff2adSJan Kotas static void csi2tx_dphy_setup(struct csi2tx_priv *csi2tx) 265050ff2adSJan Kotas { 266050ff2adSJan Kotas u32 reg; 267050ff2adSJan Kotas unsigned int i; 268050ff2adSJan Kotas 269050ff2adSJan Kotas csi2tx_dphy_set_wakeup(csi2tx); 270050ff2adSJan Kotas 271050ff2adSJan Kotas /* Put our lanes (clock and data) out of reset */ 272050ff2adSJan Kotas reg = CSI2TX_DPHY_CFG_CLK_RESET | CSI2TX_DPHY_CFG_MODE_LPDT; 273050ff2adSJan Kotas for (i = 0; i < csi2tx->num_lanes; i++) 274050ff2adSJan Kotas reg |= CSI2TX_DPHY_CFG_LANE_RESET(csi2tx->lanes[i] - 1); 275050ff2adSJan Kotas writel(reg, csi2tx->base + CSI2TX_DPHY_CFG_REG); 276050ff2adSJan Kotas 277050ff2adSJan Kotas csi2tx_dphy_init_finish(csi2tx, reg); 278050ff2adSJan Kotas } 279050ff2adSJan Kotas 280050ff2adSJan Kotas /* Configures D-PHY in CSIv2 */ 281050ff2adSJan Kotas static void csi2tx_v2_dphy_setup(struct csi2tx_priv *csi2tx) 282050ff2adSJan Kotas { 283050ff2adSJan Kotas u32 reg; 284050ff2adSJan Kotas 285050ff2adSJan Kotas csi2tx_dphy_set_wakeup(csi2tx); 286050ff2adSJan Kotas 287050ff2adSJan Kotas /* Put our lanes (clock and data) out of reset */ 288050ff2adSJan Kotas reg = CSI2TX_V2_DPHY_CFG_RESET | CSI2TX_V2_DPHY_CFG_MODE_LPDT; 289050ff2adSJan Kotas writel(reg, csi2tx->base + CSI2TX_V2_DPHY_CFG_REG); 290050ff2adSJan Kotas 291050ff2adSJan Kotas csi2tx_dphy_init_finish(csi2tx, reg); 292050ff2adSJan Kotas } 293050ff2adSJan Kotas 294050ff2adSJan Kotas static void csi2tx_reset(struct csi2tx_priv *csi2tx) 295050ff2adSJan Kotas { 296050ff2adSJan Kotas writel(CSI2TX_CONFIG_SRST_REQ, csi2tx->base + CSI2TX_CONFIG_REG); 2976f684d4fSMaxime Ripard 2986f684d4fSMaxime Ripard udelay(10); 299050ff2adSJan Kotas } 300050ff2adSJan Kotas 301050ff2adSJan Kotas static int csi2tx_start(struct csi2tx_priv *csi2tx) 302050ff2adSJan Kotas { 303050ff2adSJan Kotas struct media_entity *entity = &csi2tx->subdev.entity; 304050ff2adSJan Kotas struct media_link *link; 305050ff2adSJan Kotas unsigned int i; 306050ff2adSJan Kotas 307050ff2adSJan Kotas csi2tx_reset(csi2tx); 308050ff2adSJan Kotas 309050ff2adSJan Kotas writel(CSI2TX_CONFIG_CFG_REQ, csi2tx->base + CSI2TX_CONFIG_REG); 310050ff2adSJan Kotas 311050ff2adSJan Kotas udelay(10); 312050ff2adSJan Kotas 313050ff2adSJan Kotas if (csi2tx->vops && csi2tx->vops->dphy_setup) { 314050ff2adSJan Kotas csi2tx->vops->dphy_setup(csi2tx); 315050ff2adSJan Kotas udelay(10); 316050ff2adSJan Kotas } 3176f684d4fSMaxime Ripard 3186f684d4fSMaxime Ripard /* 3196f684d4fSMaxime Ripard * Create a static mapping between the CSI virtual channels 3206f684d4fSMaxime Ripard * and the input streams. 3216f684d4fSMaxime Ripard * 3226f684d4fSMaxime Ripard * This should be enhanced, but v4l2 lacks the support for 3236f684d4fSMaxime Ripard * changing that mapping dynamically at the moment. 3246f684d4fSMaxime Ripard * 3256f684d4fSMaxime Ripard * We're protected from the userspace setting up links at the 3266f684d4fSMaxime Ripard * same time by the upper layer having called 3276f684d4fSMaxime Ripard * media_pipeline_start(). 3286f684d4fSMaxime Ripard */ 3296f684d4fSMaxime Ripard list_for_each_entry(link, &entity->links, list) { 3306f684d4fSMaxime Ripard struct v4l2_mbus_framefmt *mfmt; 3316f684d4fSMaxime Ripard const struct csi2tx_fmt *fmt; 3326f684d4fSMaxime Ripard unsigned int stream; 3336f684d4fSMaxime Ripard int pad_idx = -1; 3346f684d4fSMaxime Ripard 3356f684d4fSMaxime Ripard /* Only consider our enabled input pads */ 3366f684d4fSMaxime Ripard for (i = CSI2TX_PAD_SINK_STREAM0; i < CSI2TX_PAD_MAX; i++) { 3376f684d4fSMaxime Ripard struct media_pad *pad = &csi2tx->pads[i]; 3386f684d4fSMaxime Ripard 3396f684d4fSMaxime Ripard if ((pad == link->sink) && 3406f684d4fSMaxime Ripard (link->flags & MEDIA_LNK_FL_ENABLED)) { 3416f684d4fSMaxime Ripard pad_idx = i; 3426f684d4fSMaxime Ripard break; 3436f684d4fSMaxime Ripard } 3446f684d4fSMaxime Ripard } 3456f684d4fSMaxime Ripard 3466f684d4fSMaxime Ripard if (pad_idx < 0) 3476f684d4fSMaxime Ripard continue; 3486f684d4fSMaxime Ripard 3496f684d4fSMaxime Ripard mfmt = &csi2tx->pad_fmts[pad_idx]; 3506f684d4fSMaxime Ripard fmt = csi2tx_get_fmt_from_mbus(mfmt->code); 3516f684d4fSMaxime Ripard if (!fmt) 3526f684d4fSMaxime Ripard continue; 3536f684d4fSMaxime Ripard 3546f684d4fSMaxime Ripard stream = pad_idx - CSI2TX_PAD_SINK_STREAM0; 3556f684d4fSMaxime Ripard 3566f684d4fSMaxime Ripard /* 3576f684d4fSMaxime Ripard * We use the stream ID there, but it's wrong. 3586f684d4fSMaxime Ripard * 3596f684d4fSMaxime Ripard * A stream could very well send a data type that is 3606f684d4fSMaxime Ripard * not equal to its stream ID. We need to find a 3616f684d4fSMaxime Ripard * proper way to address it. 3626f684d4fSMaxime Ripard */ 3636f684d4fSMaxime Ripard writel(CSI2TX_DT_CFG_DT(fmt->dt), 3646f684d4fSMaxime Ripard csi2tx->base + CSI2TX_DT_CFG_REG(stream)); 3656f684d4fSMaxime Ripard 3666f684d4fSMaxime Ripard writel(CSI2TX_DT_FORMAT_BYTES_PER_LINE(mfmt->width * fmt->bpp) | 3676f684d4fSMaxime Ripard CSI2TX_DT_FORMAT_MAX_LINE_NUM(mfmt->height + 1), 3686f684d4fSMaxime Ripard csi2tx->base + CSI2TX_DT_FORMAT_REG(stream)); 3696f684d4fSMaxime Ripard 3706f684d4fSMaxime Ripard /* 3716f684d4fSMaxime Ripard * TODO: This needs to be calculated based on the 3726f684d4fSMaxime Ripard * output CSI2 clock rate. 3736f684d4fSMaxime Ripard */ 3746f684d4fSMaxime Ripard writel(CSI2TX_STREAM_IF_CFG_FILL_LEVEL(4), 3756f684d4fSMaxime Ripard csi2tx->base + CSI2TX_STREAM_IF_CFG_REG(stream)); 3766f684d4fSMaxime Ripard } 3776f684d4fSMaxime Ripard 3786f684d4fSMaxime Ripard /* Disable the configuration mode */ 3796f684d4fSMaxime Ripard writel(0, csi2tx->base + CSI2TX_CONFIG_REG); 3806f684d4fSMaxime Ripard 3816f684d4fSMaxime Ripard return 0; 3826f684d4fSMaxime Ripard } 3836f684d4fSMaxime Ripard 3846f684d4fSMaxime Ripard static void csi2tx_stop(struct csi2tx_priv *csi2tx) 3856f684d4fSMaxime Ripard { 3866f684d4fSMaxime Ripard writel(CSI2TX_CONFIG_CFG_REQ | CSI2TX_CONFIG_SRST_REQ, 3876f684d4fSMaxime Ripard csi2tx->base + CSI2TX_CONFIG_REG); 3886f684d4fSMaxime Ripard } 3896f684d4fSMaxime Ripard 3906f684d4fSMaxime Ripard static int csi2tx_s_stream(struct v4l2_subdev *subdev, int enable) 3916f684d4fSMaxime Ripard { 3926f684d4fSMaxime Ripard struct csi2tx_priv *csi2tx = v4l2_subdev_to_csi2tx(subdev); 3936f684d4fSMaxime Ripard int ret = 0; 3946f684d4fSMaxime Ripard 3956f684d4fSMaxime Ripard mutex_lock(&csi2tx->lock); 3966f684d4fSMaxime Ripard 3976f684d4fSMaxime Ripard if (enable) { 3986f684d4fSMaxime Ripard /* 3996f684d4fSMaxime Ripard * If we're not the first users, there's no need to 4006f684d4fSMaxime Ripard * enable the whole controller. 4016f684d4fSMaxime Ripard */ 4026f684d4fSMaxime Ripard if (!csi2tx->count) { 4036f684d4fSMaxime Ripard ret = csi2tx_start(csi2tx); 4046f684d4fSMaxime Ripard if (ret) 4056f684d4fSMaxime Ripard goto out; 4066f684d4fSMaxime Ripard } 4076f684d4fSMaxime Ripard 4086f684d4fSMaxime Ripard csi2tx->count++; 4096f684d4fSMaxime Ripard } else { 4106f684d4fSMaxime Ripard csi2tx->count--; 4116f684d4fSMaxime Ripard 4126f684d4fSMaxime Ripard /* 4136f684d4fSMaxime Ripard * Let the last user turn off the lights. 4146f684d4fSMaxime Ripard */ 4156f684d4fSMaxime Ripard if (!csi2tx->count) 4166f684d4fSMaxime Ripard csi2tx_stop(csi2tx); 4176f684d4fSMaxime Ripard } 4186f684d4fSMaxime Ripard 4196f684d4fSMaxime Ripard out: 4206f684d4fSMaxime Ripard mutex_unlock(&csi2tx->lock); 4216f684d4fSMaxime Ripard return ret; 4226f684d4fSMaxime Ripard } 4236f684d4fSMaxime Ripard 4246f684d4fSMaxime Ripard static const struct v4l2_subdev_video_ops csi2tx_video_ops = { 4256f684d4fSMaxime Ripard .s_stream = csi2tx_s_stream, 4266f684d4fSMaxime Ripard }; 4276f684d4fSMaxime Ripard 4286f684d4fSMaxime Ripard static const struct v4l2_subdev_ops csi2tx_subdev_ops = { 4296f684d4fSMaxime Ripard .pad = &csi2tx_pad_ops, 4306f684d4fSMaxime Ripard .video = &csi2tx_video_ops, 4316f684d4fSMaxime Ripard }; 4326f684d4fSMaxime Ripard 4336f684d4fSMaxime Ripard static int csi2tx_get_resources(struct csi2tx_priv *csi2tx, 4346f684d4fSMaxime Ripard struct platform_device *pdev) 4356f684d4fSMaxime Ripard { 4366f684d4fSMaxime Ripard unsigned int i; 4376f684d4fSMaxime Ripard u32 dev_cfg; 438e6001f69SEvgeny Novikov int ret; 4396f684d4fSMaxime Ripard 440*f5aae241SCai Huoqing csi2tx->base = devm_platform_ioremap_resource(pdev, 0); 4416f684d4fSMaxime Ripard if (IS_ERR(csi2tx->base)) 4426f684d4fSMaxime Ripard return PTR_ERR(csi2tx->base); 4436f684d4fSMaxime Ripard 4446f684d4fSMaxime Ripard csi2tx->p_clk = devm_clk_get(&pdev->dev, "p_clk"); 4456f684d4fSMaxime Ripard if (IS_ERR(csi2tx->p_clk)) { 4466f684d4fSMaxime Ripard dev_err(&pdev->dev, "Couldn't get p_clk\n"); 4476f684d4fSMaxime Ripard return PTR_ERR(csi2tx->p_clk); 4486f684d4fSMaxime Ripard } 4496f684d4fSMaxime Ripard 4506f684d4fSMaxime Ripard csi2tx->esc_clk = devm_clk_get(&pdev->dev, "esc_clk"); 4516f684d4fSMaxime Ripard if (IS_ERR(csi2tx->esc_clk)) { 4526f684d4fSMaxime Ripard dev_err(&pdev->dev, "Couldn't get the esc_clk\n"); 4536f684d4fSMaxime Ripard return PTR_ERR(csi2tx->esc_clk); 4546f684d4fSMaxime Ripard } 4556f684d4fSMaxime Ripard 456e6001f69SEvgeny Novikov ret = clk_prepare_enable(csi2tx->p_clk); 457e6001f69SEvgeny Novikov if (ret) { 458e6001f69SEvgeny Novikov dev_err(&pdev->dev, "Couldn't prepare and enable p_clk\n"); 459e6001f69SEvgeny Novikov return ret; 460e6001f69SEvgeny Novikov } 461e6001f69SEvgeny Novikov 4626f684d4fSMaxime Ripard dev_cfg = readl(csi2tx->base + CSI2TX_DEVICE_CONFIG_REG); 4636f684d4fSMaxime Ripard clk_disable_unprepare(csi2tx->p_clk); 4646f684d4fSMaxime Ripard 4656f684d4fSMaxime Ripard csi2tx->max_lanes = dev_cfg & CSI2TX_DEVICE_CONFIG_LANES_MASK; 4666f684d4fSMaxime Ripard if (csi2tx->max_lanes > CSI2TX_LANES_MAX) { 4676f684d4fSMaxime Ripard dev_err(&pdev->dev, "Invalid number of lanes: %u\n", 4686f684d4fSMaxime Ripard csi2tx->max_lanes); 4696f684d4fSMaxime Ripard return -EINVAL; 4706f684d4fSMaxime Ripard } 4716f684d4fSMaxime Ripard 4726f684d4fSMaxime Ripard csi2tx->max_streams = (dev_cfg & CSI2TX_DEVICE_CONFIG_STREAMS_MASK) >> 4; 4736f684d4fSMaxime Ripard if (csi2tx->max_streams > CSI2TX_STREAMS_MAX) { 4746f684d4fSMaxime Ripard dev_err(&pdev->dev, "Invalid number of streams: %u\n", 4756f684d4fSMaxime Ripard csi2tx->max_streams); 4766f684d4fSMaxime Ripard return -EINVAL; 4776f684d4fSMaxime Ripard } 4786f684d4fSMaxime Ripard 4796f684d4fSMaxime Ripard csi2tx->has_internal_dphy = !!(dev_cfg & CSI2TX_DEVICE_CONFIG_HAS_DPHY); 4806f684d4fSMaxime Ripard 4816f684d4fSMaxime Ripard for (i = 0; i < csi2tx->max_streams; i++) { 4826f684d4fSMaxime Ripard char clk_name[16]; 4836f684d4fSMaxime Ripard 4846f684d4fSMaxime Ripard snprintf(clk_name, sizeof(clk_name), "pixel_if%u_clk", i); 4856f684d4fSMaxime Ripard csi2tx->pixel_clk[i] = devm_clk_get(&pdev->dev, clk_name); 4866f684d4fSMaxime Ripard if (IS_ERR(csi2tx->pixel_clk[i])) { 4876f684d4fSMaxime Ripard dev_err(&pdev->dev, "Couldn't get clock %s\n", 4886f684d4fSMaxime Ripard clk_name); 4896f684d4fSMaxime Ripard return PTR_ERR(csi2tx->pixel_clk[i]); 4906f684d4fSMaxime Ripard } 4916f684d4fSMaxime Ripard } 4926f684d4fSMaxime Ripard 4936f684d4fSMaxime Ripard return 0; 4946f684d4fSMaxime Ripard } 4956f684d4fSMaxime Ripard 4966f684d4fSMaxime Ripard static int csi2tx_check_lanes(struct csi2tx_priv *csi2tx) 4976f684d4fSMaxime Ripard { 49860359a28SSakari Ailus struct v4l2_fwnode_endpoint v4l2_ep = { .bus_type = 0 }; 4996f684d4fSMaxime Ripard struct device_node *ep; 500bf9df90bSJan Kotas int ret, i; 5016f684d4fSMaxime Ripard 5026f684d4fSMaxime Ripard ep = of_graph_get_endpoint_by_regs(csi2tx->dev->of_node, 0, 0); 5036f684d4fSMaxime Ripard if (!ep) 5046f684d4fSMaxime Ripard return -EINVAL; 5056f684d4fSMaxime Ripard 5066f684d4fSMaxime Ripard ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &v4l2_ep); 5076f684d4fSMaxime Ripard if (ret) { 5086f684d4fSMaxime Ripard dev_err(csi2tx->dev, "Could not parse v4l2 endpoint\n"); 5096f684d4fSMaxime Ripard goto out; 5106f684d4fSMaxime Ripard } 5116f684d4fSMaxime Ripard 5122d95e7edSSakari Ailus if (v4l2_ep.bus_type != V4L2_MBUS_CSI2_DPHY) { 5136f684d4fSMaxime Ripard dev_err(csi2tx->dev, "Unsupported media bus type: 0x%x\n", 5146f684d4fSMaxime Ripard v4l2_ep.bus_type); 5156f684d4fSMaxime Ripard ret = -EINVAL; 5166f684d4fSMaxime Ripard goto out; 5176f684d4fSMaxime Ripard } 5186f684d4fSMaxime Ripard 5196f684d4fSMaxime Ripard csi2tx->num_lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes; 5206f684d4fSMaxime Ripard if (csi2tx->num_lanes > csi2tx->max_lanes) { 5216f684d4fSMaxime Ripard dev_err(csi2tx->dev, 5226f684d4fSMaxime Ripard "Current configuration uses more lanes than supported\n"); 5236f684d4fSMaxime Ripard ret = -EINVAL; 5246f684d4fSMaxime Ripard goto out; 5256f684d4fSMaxime Ripard } 5266f684d4fSMaxime Ripard 527bf9df90bSJan Kotas for (i = 0; i < csi2tx->num_lanes; i++) { 528bf9df90bSJan Kotas if (v4l2_ep.bus.mipi_csi2.data_lanes[i] < 1) { 529bf9df90bSJan Kotas dev_err(csi2tx->dev, "Invalid lane[%d] number: %u\n", 530bf9df90bSJan Kotas i, v4l2_ep.bus.mipi_csi2.data_lanes[i]); 531bf9df90bSJan Kotas ret = -EINVAL; 532bf9df90bSJan Kotas goto out; 533bf9df90bSJan Kotas } 534bf9df90bSJan Kotas } 535bf9df90bSJan Kotas 5366f684d4fSMaxime Ripard memcpy(csi2tx->lanes, v4l2_ep.bus.mipi_csi2.data_lanes, 5376f684d4fSMaxime Ripard sizeof(csi2tx->lanes)); 5386f684d4fSMaxime Ripard 5396f684d4fSMaxime Ripard out: 5406f684d4fSMaxime Ripard of_node_put(ep); 5416f684d4fSMaxime Ripard return ret; 5426f684d4fSMaxime Ripard } 5436f684d4fSMaxime Ripard 544050ff2adSJan Kotas static const struct csi2tx_vops csi2tx_vops = { 545050ff2adSJan Kotas .dphy_setup = csi2tx_dphy_setup, 546050ff2adSJan Kotas }; 547050ff2adSJan Kotas 548050ff2adSJan Kotas static const struct csi2tx_vops csi2tx_v2_vops = { 549050ff2adSJan Kotas .dphy_setup = csi2tx_v2_dphy_setup, 550050ff2adSJan Kotas }; 551050ff2adSJan Kotas 552050ff2adSJan Kotas static const struct of_device_id csi2tx_of_table[] = { 553050ff2adSJan Kotas { 554050ff2adSJan Kotas .compatible = "cdns,csi2tx", 555050ff2adSJan Kotas .data = &csi2tx_vops 556050ff2adSJan Kotas }, 557050ff2adSJan Kotas { 558050ff2adSJan Kotas .compatible = "cdns,csi2tx-1.3", 559050ff2adSJan Kotas .data = &csi2tx_vops 560050ff2adSJan Kotas }, 561050ff2adSJan Kotas { 562050ff2adSJan Kotas .compatible = "cdns,csi2tx-2.1", 563050ff2adSJan Kotas .data = &csi2tx_v2_vops 564050ff2adSJan Kotas }, 565050ff2adSJan Kotas { } 566050ff2adSJan Kotas }; 567050ff2adSJan Kotas MODULE_DEVICE_TABLE(of, csi2tx_of_table); 568050ff2adSJan Kotas 5696f684d4fSMaxime Ripard static int csi2tx_probe(struct platform_device *pdev) 5706f684d4fSMaxime Ripard { 5716f684d4fSMaxime Ripard struct csi2tx_priv *csi2tx; 572050ff2adSJan Kotas const struct of_device_id *of_id; 5736f684d4fSMaxime Ripard unsigned int i; 5746f684d4fSMaxime Ripard int ret; 5756f684d4fSMaxime Ripard 5766f684d4fSMaxime Ripard csi2tx = kzalloc(sizeof(*csi2tx), GFP_KERNEL); 5776f684d4fSMaxime Ripard if (!csi2tx) 5786f684d4fSMaxime Ripard return -ENOMEM; 5796f684d4fSMaxime Ripard platform_set_drvdata(pdev, csi2tx); 5806f684d4fSMaxime Ripard mutex_init(&csi2tx->lock); 5816f684d4fSMaxime Ripard csi2tx->dev = &pdev->dev; 5826f684d4fSMaxime Ripard 5836f684d4fSMaxime Ripard ret = csi2tx_get_resources(csi2tx, pdev); 5846f684d4fSMaxime Ripard if (ret) 5856f684d4fSMaxime Ripard goto err_free_priv; 5866f684d4fSMaxime Ripard 587050ff2adSJan Kotas of_id = of_match_node(csi2tx_of_table, pdev->dev.of_node); 588050ff2adSJan Kotas csi2tx->vops = (struct csi2tx_vops *)of_id->data; 589050ff2adSJan Kotas 5906f684d4fSMaxime Ripard v4l2_subdev_init(&csi2tx->subdev, &csi2tx_subdev_ops); 5916f684d4fSMaxime Ripard csi2tx->subdev.owner = THIS_MODULE; 5926f684d4fSMaxime Ripard csi2tx->subdev.dev = &pdev->dev; 5936f684d4fSMaxime Ripard csi2tx->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 5946f684d4fSMaxime Ripard snprintf(csi2tx->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s.%s", 5956f684d4fSMaxime Ripard KBUILD_MODNAME, dev_name(&pdev->dev)); 5966f684d4fSMaxime Ripard 5976f684d4fSMaxime Ripard ret = csi2tx_check_lanes(csi2tx); 5986f684d4fSMaxime Ripard if (ret) 5996f684d4fSMaxime Ripard goto err_free_priv; 6006f684d4fSMaxime Ripard 6016f684d4fSMaxime Ripard /* Create our media pads */ 6026f684d4fSMaxime Ripard csi2tx->subdev.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; 6036f684d4fSMaxime Ripard csi2tx->pads[CSI2TX_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; 6046f684d4fSMaxime Ripard for (i = CSI2TX_PAD_SINK_STREAM0; i < CSI2TX_PAD_MAX; i++) 6056f684d4fSMaxime Ripard csi2tx->pads[i].flags = MEDIA_PAD_FL_SINK; 6066f684d4fSMaxime Ripard 6076f684d4fSMaxime Ripard /* 6086f684d4fSMaxime Ripard * Only the input pads are considered to have a format at the 6096f684d4fSMaxime Ripard * moment. The CSI link can multiplex various streams with 6106f684d4fSMaxime Ripard * different formats, and we can't expose this in v4l2 right 6116f684d4fSMaxime Ripard * now. 6126f684d4fSMaxime Ripard */ 6136f684d4fSMaxime Ripard for (i = CSI2TX_PAD_SINK_STREAM0; i < CSI2TX_PAD_MAX; i++) 6146f684d4fSMaxime Ripard csi2tx->pad_fmts[i] = fmt_default; 6156f684d4fSMaxime Ripard 6166f684d4fSMaxime Ripard ret = media_entity_pads_init(&csi2tx->subdev.entity, CSI2TX_PAD_MAX, 6176f684d4fSMaxime Ripard csi2tx->pads); 6186f684d4fSMaxime Ripard if (ret) 6196f684d4fSMaxime Ripard goto err_free_priv; 6206f684d4fSMaxime Ripard 6216f684d4fSMaxime Ripard ret = v4l2_async_register_subdev(&csi2tx->subdev); 6226f684d4fSMaxime Ripard if (ret < 0) 6236f684d4fSMaxime Ripard goto err_free_priv; 6246f684d4fSMaxime Ripard 6256f684d4fSMaxime Ripard dev_info(&pdev->dev, 6266f684d4fSMaxime Ripard "Probed CSI2TX with %u/%u lanes, %u streams, %s D-PHY\n", 6276f684d4fSMaxime Ripard csi2tx->num_lanes, csi2tx->max_lanes, csi2tx->max_streams, 6286f684d4fSMaxime Ripard csi2tx->has_internal_dphy ? "internal" : "no"); 6296f684d4fSMaxime Ripard 6306f684d4fSMaxime Ripard return 0; 6316f684d4fSMaxime Ripard 6326f684d4fSMaxime Ripard err_free_priv: 6336f684d4fSMaxime Ripard kfree(csi2tx); 6346f684d4fSMaxime Ripard return ret; 6356f684d4fSMaxime Ripard } 6366f684d4fSMaxime Ripard 6376f684d4fSMaxime Ripard static int csi2tx_remove(struct platform_device *pdev) 6386f684d4fSMaxime Ripard { 6396f684d4fSMaxime Ripard struct csi2tx_priv *csi2tx = platform_get_drvdata(pdev); 6406f684d4fSMaxime Ripard 6416f684d4fSMaxime Ripard v4l2_async_unregister_subdev(&csi2tx->subdev); 6426f684d4fSMaxime Ripard kfree(csi2tx); 6436f684d4fSMaxime Ripard 6446f684d4fSMaxime Ripard return 0; 6456f684d4fSMaxime Ripard } 6466f684d4fSMaxime Ripard 6476f684d4fSMaxime Ripard static struct platform_driver csi2tx_driver = { 6486f684d4fSMaxime Ripard .probe = csi2tx_probe, 6496f684d4fSMaxime Ripard .remove = csi2tx_remove, 6506f684d4fSMaxime Ripard 6516f684d4fSMaxime Ripard .driver = { 6526f684d4fSMaxime Ripard .name = "cdns-csi2tx", 6536f684d4fSMaxime Ripard .of_match_table = csi2tx_of_table, 6546f684d4fSMaxime Ripard }, 6556f684d4fSMaxime Ripard }; 6566f684d4fSMaxime Ripard module_platform_driver(csi2tx_driver); 6576f684d4fSMaxime Ripard MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin.com>"); 6586f684d4fSMaxime Ripard MODULE_DESCRIPTION("Cadence CSI2-TX controller"); 6596f684d4fSMaxime Ripard MODULE_LICENSE("GPL"); 660