16f684d4fSMaxime Ripard // SPDX-License-Identifier: GPL-2.0+ 26f684d4fSMaxime Ripard /* 36f684d4fSMaxime Ripard * Driver for Cadence MIPI-CSI2 TX Controller 46f684d4fSMaxime Ripard * 56f684d4fSMaxime Ripard * Copyright (C) 2017-2018 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 556f684d4fSMaxime Ripard #define CSI2TX_LANES_MAX 4 566f684d4fSMaxime Ripard #define CSI2TX_STREAMS_MAX 4 576f684d4fSMaxime Ripard 586f684d4fSMaxime Ripard enum csi2tx_pads { 596f684d4fSMaxime Ripard CSI2TX_PAD_SOURCE, 606f684d4fSMaxime Ripard CSI2TX_PAD_SINK_STREAM0, 616f684d4fSMaxime Ripard CSI2TX_PAD_SINK_STREAM1, 626f684d4fSMaxime Ripard CSI2TX_PAD_SINK_STREAM2, 636f684d4fSMaxime Ripard CSI2TX_PAD_SINK_STREAM3, 646f684d4fSMaxime Ripard CSI2TX_PAD_MAX, 656f684d4fSMaxime Ripard }; 666f684d4fSMaxime Ripard 676f684d4fSMaxime Ripard struct csi2tx_fmt { 686f684d4fSMaxime Ripard u32 mbus; 696f684d4fSMaxime Ripard u32 dt; 706f684d4fSMaxime Ripard u32 bpp; 716f684d4fSMaxime Ripard }; 726f684d4fSMaxime Ripard 736f684d4fSMaxime Ripard struct csi2tx_priv { 746f684d4fSMaxime Ripard struct device *dev; 756f684d4fSMaxime Ripard unsigned int count; 766f684d4fSMaxime Ripard 776f684d4fSMaxime Ripard /* 786f684d4fSMaxime Ripard * Used to prevent race conditions between multiple, 796f684d4fSMaxime Ripard * concurrent calls to start and stop. 806f684d4fSMaxime Ripard */ 816f684d4fSMaxime Ripard struct mutex lock; 826f684d4fSMaxime Ripard 836f684d4fSMaxime Ripard void __iomem *base; 846f684d4fSMaxime Ripard 856f684d4fSMaxime Ripard struct clk *esc_clk; 866f684d4fSMaxime Ripard struct clk *p_clk; 876f684d4fSMaxime Ripard struct clk *pixel_clk[CSI2TX_STREAMS_MAX]; 886f684d4fSMaxime Ripard 896f684d4fSMaxime Ripard struct v4l2_subdev subdev; 906f684d4fSMaxime Ripard struct media_pad pads[CSI2TX_PAD_MAX]; 916f684d4fSMaxime Ripard struct v4l2_mbus_framefmt pad_fmts[CSI2TX_PAD_MAX]; 926f684d4fSMaxime Ripard 936f684d4fSMaxime Ripard bool has_internal_dphy; 946f684d4fSMaxime Ripard u8 lanes[CSI2TX_LANES_MAX]; 956f684d4fSMaxime Ripard unsigned int num_lanes; 966f684d4fSMaxime Ripard unsigned int max_lanes; 976f684d4fSMaxime Ripard unsigned int max_streams; 986f684d4fSMaxime Ripard }; 996f684d4fSMaxime Ripard 1006f684d4fSMaxime Ripard static const struct csi2tx_fmt csi2tx_formats[] = { 1016f684d4fSMaxime Ripard { 1026f684d4fSMaxime Ripard .mbus = MEDIA_BUS_FMT_UYVY8_1X16, 1036f684d4fSMaxime Ripard .bpp = 2, 1046f684d4fSMaxime Ripard .dt = 0x1e, 1056f684d4fSMaxime Ripard }, 1066f684d4fSMaxime Ripard { 1076f684d4fSMaxime Ripard .mbus = MEDIA_BUS_FMT_RGB888_1X24, 1086f684d4fSMaxime Ripard .bpp = 3, 1096f684d4fSMaxime Ripard .dt = 0x24, 1106f684d4fSMaxime Ripard }, 1116f684d4fSMaxime Ripard }; 1126f684d4fSMaxime Ripard 1136f684d4fSMaxime Ripard static const struct v4l2_mbus_framefmt fmt_default = { 1146f684d4fSMaxime Ripard .width = 1280, 1156f684d4fSMaxime Ripard .height = 720, 1166f684d4fSMaxime Ripard .code = MEDIA_BUS_FMT_RGB888_1X24, 1176f684d4fSMaxime Ripard .field = V4L2_FIELD_NONE, 1186f684d4fSMaxime Ripard .colorspace = V4L2_COLORSPACE_DEFAULT, 1196f684d4fSMaxime Ripard }; 1206f684d4fSMaxime Ripard 1216f684d4fSMaxime Ripard static inline 1226f684d4fSMaxime Ripard struct csi2tx_priv *v4l2_subdev_to_csi2tx(struct v4l2_subdev *subdev) 1236f684d4fSMaxime Ripard { 1246f684d4fSMaxime Ripard return container_of(subdev, struct csi2tx_priv, subdev); 1256f684d4fSMaxime Ripard } 1266f684d4fSMaxime Ripard 1276f684d4fSMaxime Ripard static const struct csi2tx_fmt *csi2tx_get_fmt_from_mbus(u32 mbus) 1286f684d4fSMaxime Ripard { 1296f684d4fSMaxime Ripard unsigned int i; 1306f684d4fSMaxime Ripard 1316f684d4fSMaxime Ripard for (i = 0; i < ARRAY_SIZE(csi2tx_formats); i++) 1326f684d4fSMaxime Ripard if (csi2tx_formats[i].mbus == mbus) 1336f684d4fSMaxime Ripard return &csi2tx_formats[i]; 1346f684d4fSMaxime Ripard 1356f684d4fSMaxime Ripard return NULL; 1366f684d4fSMaxime Ripard } 1376f684d4fSMaxime Ripard 1386f684d4fSMaxime Ripard static int csi2tx_enum_mbus_code(struct v4l2_subdev *subdev, 1396f684d4fSMaxime Ripard struct v4l2_subdev_pad_config *cfg, 1406f684d4fSMaxime Ripard struct v4l2_subdev_mbus_code_enum *code) 1416f684d4fSMaxime Ripard { 1426f684d4fSMaxime Ripard if (code->pad || code->index >= ARRAY_SIZE(csi2tx_formats)) 1436f684d4fSMaxime Ripard return -EINVAL; 1446f684d4fSMaxime Ripard 1456f684d4fSMaxime Ripard code->code = csi2tx_formats[code->index].mbus; 1466f684d4fSMaxime Ripard 1476f684d4fSMaxime Ripard return 0; 1486f684d4fSMaxime Ripard } 1496f684d4fSMaxime Ripard 1506f684d4fSMaxime Ripard static struct v4l2_mbus_framefmt * 1516f684d4fSMaxime Ripard __csi2tx_get_pad_format(struct v4l2_subdev *subdev, 1526f684d4fSMaxime Ripard struct v4l2_subdev_pad_config *cfg, 1536f684d4fSMaxime Ripard struct v4l2_subdev_format *fmt) 1546f684d4fSMaxime Ripard { 1556f684d4fSMaxime Ripard struct csi2tx_priv *csi2tx = v4l2_subdev_to_csi2tx(subdev); 1566f684d4fSMaxime Ripard 1576f684d4fSMaxime Ripard if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) 1586f684d4fSMaxime Ripard return v4l2_subdev_get_try_format(subdev, cfg, 1596f684d4fSMaxime Ripard fmt->pad); 1606f684d4fSMaxime Ripard 1616f684d4fSMaxime Ripard return &csi2tx->pad_fmts[fmt->pad]; 1626f684d4fSMaxime Ripard } 1636f684d4fSMaxime Ripard 1646f684d4fSMaxime Ripard static int csi2tx_get_pad_format(struct v4l2_subdev *subdev, 1656f684d4fSMaxime Ripard struct v4l2_subdev_pad_config *cfg, 1666f684d4fSMaxime Ripard struct v4l2_subdev_format *fmt) 1676f684d4fSMaxime Ripard { 1686f684d4fSMaxime Ripard const struct v4l2_mbus_framefmt *format; 1696f684d4fSMaxime Ripard 1706f684d4fSMaxime Ripard /* Multiplexed pad? */ 1716f684d4fSMaxime Ripard if (fmt->pad == CSI2TX_PAD_SOURCE) 1726f684d4fSMaxime Ripard return -EINVAL; 1736f684d4fSMaxime Ripard 1746f684d4fSMaxime Ripard format = __csi2tx_get_pad_format(subdev, cfg, fmt); 1756f684d4fSMaxime Ripard if (!format) 1766f684d4fSMaxime Ripard return -EINVAL; 1776f684d4fSMaxime Ripard 1786f684d4fSMaxime Ripard fmt->format = *format; 1796f684d4fSMaxime Ripard 1806f684d4fSMaxime Ripard return 0; 1816f684d4fSMaxime Ripard } 1826f684d4fSMaxime Ripard 1836f684d4fSMaxime Ripard static int csi2tx_set_pad_format(struct v4l2_subdev *subdev, 1846f684d4fSMaxime Ripard struct v4l2_subdev_pad_config *cfg, 1856f684d4fSMaxime Ripard struct v4l2_subdev_format *fmt) 1866f684d4fSMaxime Ripard { 1876f684d4fSMaxime Ripard const struct v4l2_mbus_framefmt *src_format = &fmt->format; 1886f684d4fSMaxime Ripard struct v4l2_mbus_framefmt *dst_format; 1896f684d4fSMaxime Ripard 1906f684d4fSMaxime Ripard /* Multiplexed pad? */ 1916f684d4fSMaxime Ripard if (fmt->pad == CSI2TX_PAD_SOURCE) 1926f684d4fSMaxime Ripard return -EINVAL; 1936f684d4fSMaxime Ripard 1946f684d4fSMaxime Ripard if (!csi2tx_get_fmt_from_mbus(fmt->format.code)) 1956f684d4fSMaxime Ripard src_format = &fmt_default; 1966f684d4fSMaxime Ripard 1976f684d4fSMaxime Ripard dst_format = __csi2tx_get_pad_format(subdev, cfg, fmt); 1986f684d4fSMaxime Ripard if (!dst_format) 1996f684d4fSMaxime Ripard return -EINVAL; 2006f684d4fSMaxime Ripard 2016f684d4fSMaxime Ripard *dst_format = *src_format; 2026f684d4fSMaxime Ripard 2036f684d4fSMaxime Ripard return 0; 2046f684d4fSMaxime Ripard } 2056f684d4fSMaxime Ripard 2066f684d4fSMaxime Ripard static const struct v4l2_subdev_pad_ops csi2tx_pad_ops = { 2076f684d4fSMaxime Ripard .enum_mbus_code = csi2tx_enum_mbus_code, 2086f684d4fSMaxime Ripard .get_fmt = csi2tx_get_pad_format, 2096f684d4fSMaxime Ripard .set_fmt = csi2tx_set_pad_format, 2106f684d4fSMaxime Ripard }; 2116f684d4fSMaxime Ripard 2126f684d4fSMaxime Ripard static void csi2tx_reset(struct csi2tx_priv *csi2tx) 2136f684d4fSMaxime Ripard { 2146f684d4fSMaxime Ripard writel(CSI2TX_CONFIG_SRST_REQ, csi2tx->base + CSI2TX_CONFIG_REG); 2156f684d4fSMaxime Ripard 2166f684d4fSMaxime Ripard udelay(10); 2176f684d4fSMaxime Ripard } 2186f684d4fSMaxime Ripard 2196f684d4fSMaxime Ripard static int csi2tx_start(struct csi2tx_priv *csi2tx) 2206f684d4fSMaxime Ripard { 2216f684d4fSMaxime Ripard struct media_entity *entity = &csi2tx->subdev.entity; 2226f684d4fSMaxime Ripard struct media_link *link; 2236f684d4fSMaxime Ripard unsigned int i; 2246f684d4fSMaxime Ripard u32 reg; 2256f684d4fSMaxime Ripard 2266f684d4fSMaxime Ripard csi2tx_reset(csi2tx); 2276f684d4fSMaxime Ripard 2286f684d4fSMaxime Ripard writel(CSI2TX_CONFIG_CFG_REQ, csi2tx->base + CSI2TX_CONFIG_REG); 2296f684d4fSMaxime Ripard 2306f684d4fSMaxime Ripard udelay(10); 2316f684d4fSMaxime Ripard 2326f684d4fSMaxime Ripard /* Configure our PPI interface with the D-PHY */ 2336f684d4fSMaxime Ripard writel(CSI2TX_DPHY_CLK_WAKEUP_ULPS_CYCLES(32), 2346f684d4fSMaxime Ripard csi2tx->base + CSI2TX_DPHY_CLK_WAKEUP_REG); 2356f684d4fSMaxime Ripard 2366f684d4fSMaxime Ripard /* Put our lanes (clock and data) out of reset */ 2376f684d4fSMaxime Ripard reg = CSI2TX_DPHY_CFG_CLK_RESET | CSI2TX_DPHY_CFG_MODE_LPDT; 2386f684d4fSMaxime Ripard for (i = 0; i < csi2tx->num_lanes; i++) 2396f684d4fSMaxime Ripard reg |= CSI2TX_DPHY_CFG_LANE_RESET(csi2tx->lanes[i]); 2406f684d4fSMaxime Ripard writel(reg, csi2tx->base + CSI2TX_DPHY_CFG_REG); 2416f684d4fSMaxime Ripard 2426f684d4fSMaxime Ripard udelay(10); 2436f684d4fSMaxime Ripard 2446f684d4fSMaxime Ripard /* Enable our (clock and data) lanes */ 2456f684d4fSMaxime Ripard reg |= CSI2TX_DPHY_CFG_CLK_ENABLE; 2466f684d4fSMaxime Ripard for (i = 0; i < csi2tx->num_lanes; i++) 2476f684d4fSMaxime Ripard reg |= CSI2TX_DPHY_CFG_LANE_ENABLE(csi2tx->lanes[i]); 2486f684d4fSMaxime Ripard writel(reg, csi2tx->base + CSI2TX_DPHY_CFG_REG); 2496f684d4fSMaxime Ripard 2506f684d4fSMaxime Ripard udelay(10); 2516f684d4fSMaxime Ripard 2526f684d4fSMaxime Ripard /* Switch to HS mode */ 2536f684d4fSMaxime Ripard reg &= ~CSI2TX_DPHY_CFG_MODE_MASK; 2546f684d4fSMaxime Ripard writel(reg | CSI2TX_DPHY_CFG_MODE_HS, 2556f684d4fSMaxime Ripard csi2tx->base + CSI2TX_DPHY_CFG_REG); 2566f684d4fSMaxime Ripard 2576f684d4fSMaxime Ripard udelay(10); 2586f684d4fSMaxime Ripard 2596f684d4fSMaxime Ripard /* 2606f684d4fSMaxime Ripard * Create a static mapping between the CSI virtual channels 2616f684d4fSMaxime Ripard * and the input streams. 2626f684d4fSMaxime Ripard * 2636f684d4fSMaxime Ripard * This should be enhanced, but v4l2 lacks the support for 2646f684d4fSMaxime Ripard * changing that mapping dynamically at the moment. 2656f684d4fSMaxime Ripard * 2666f684d4fSMaxime Ripard * We're protected from the userspace setting up links at the 2676f684d4fSMaxime Ripard * same time by the upper layer having called 2686f684d4fSMaxime Ripard * media_pipeline_start(). 2696f684d4fSMaxime Ripard */ 2706f684d4fSMaxime Ripard list_for_each_entry(link, &entity->links, list) { 2716f684d4fSMaxime Ripard struct v4l2_mbus_framefmt *mfmt; 2726f684d4fSMaxime Ripard const struct csi2tx_fmt *fmt; 2736f684d4fSMaxime Ripard unsigned int stream; 2746f684d4fSMaxime Ripard int pad_idx = -1; 2756f684d4fSMaxime Ripard 2766f684d4fSMaxime Ripard /* Only consider our enabled input pads */ 2776f684d4fSMaxime Ripard for (i = CSI2TX_PAD_SINK_STREAM0; i < CSI2TX_PAD_MAX; i++) { 2786f684d4fSMaxime Ripard struct media_pad *pad = &csi2tx->pads[i]; 2796f684d4fSMaxime Ripard 2806f684d4fSMaxime Ripard if ((pad == link->sink) && 2816f684d4fSMaxime Ripard (link->flags & MEDIA_LNK_FL_ENABLED)) { 2826f684d4fSMaxime Ripard pad_idx = i; 2836f684d4fSMaxime Ripard break; 2846f684d4fSMaxime Ripard } 2856f684d4fSMaxime Ripard } 2866f684d4fSMaxime Ripard 2876f684d4fSMaxime Ripard if (pad_idx < 0) 2886f684d4fSMaxime Ripard continue; 2896f684d4fSMaxime Ripard 2906f684d4fSMaxime Ripard mfmt = &csi2tx->pad_fmts[pad_idx]; 2916f684d4fSMaxime Ripard fmt = csi2tx_get_fmt_from_mbus(mfmt->code); 2926f684d4fSMaxime Ripard if (!fmt) 2936f684d4fSMaxime Ripard continue; 2946f684d4fSMaxime Ripard 2956f684d4fSMaxime Ripard stream = pad_idx - CSI2TX_PAD_SINK_STREAM0; 2966f684d4fSMaxime Ripard 2976f684d4fSMaxime Ripard /* 2986f684d4fSMaxime Ripard * We use the stream ID there, but it's wrong. 2996f684d4fSMaxime Ripard * 3006f684d4fSMaxime Ripard * A stream could very well send a data type that is 3016f684d4fSMaxime Ripard * not equal to its stream ID. We need to find a 3026f684d4fSMaxime Ripard * proper way to address it. 3036f684d4fSMaxime Ripard */ 3046f684d4fSMaxime Ripard writel(CSI2TX_DT_CFG_DT(fmt->dt), 3056f684d4fSMaxime Ripard csi2tx->base + CSI2TX_DT_CFG_REG(stream)); 3066f684d4fSMaxime Ripard 3076f684d4fSMaxime Ripard writel(CSI2TX_DT_FORMAT_BYTES_PER_LINE(mfmt->width * fmt->bpp) | 3086f684d4fSMaxime Ripard CSI2TX_DT_FORMAT_MAX_LINE_NUM(mfmt->height + 1), 3096f684d4fSMaxime Ripard csi2tx->base + CSI2TX_DT_FORMAT_REG(stream)); 3106f684d4fSMaxime Ripard 3116f684d4fSMaxime Ripard /* 3126f684d4fSMaxime Ripard * TODO: This needs to be calculated based on the 3136f684d4fSMaxime Ripard * output CSI2 clock rate. 3146f684d4fSMaxime Ripard */ 3156f684d4fSMaxime Ripard writel(CSI2TX_STREAM_IF_CFG_FILL_LEVEL(4), 3166f684d4fSMaxime Ripard csi2tx->base + CSI2TX_STREAM_IF_CFG_REG(stream)); 3176f684d4fSMaxime Ripard } 3186f684d4fSMaxime Ripard 3196f684d4fSMaxime Ripard /* Disable the configuration mode */ 3206f684d4fSMaxime Ripard writel(0, csi2tx->base + CSI2TX_CONFIG_REG); 3216f684d4fSMaxime Ripard 3226f684d4fSMaxime Ripard return 0; 3236f684d4fSMaxime Ripard } 3246f684d4fSMaxime Ripard 3256f684d4fSMaxime Ripard static void csi2tx_stop(struct csi2tx_priv *csi2tx) 3266f684d4fSMaxime Ripard { 3276f684d4fSMaxime Ripard writel(CSI2TX_CONFIG_CFG_REQ | CSI2TX_CONFIG_SRST_REQ, 3286f684d4fSMaxime Ripard csi2tx->base + CSI2TX_CONFIG_REG); 3296f684d4fSMaxime Ripard } 3306f684d4fSMaxime Ripard 3316f684d4fSMaxime Ripard static int csi2tx_s_stream(struct v4l2_subdev *subdev, int enable) 3326f684d4fSMaxime Ripard { 3336f684d4fSMaxime Ripard struct csi2tx_priv *csi2tx = v4l2_subdev_to_csi2tx(subdev); 3346f684d4fSMaxime Ripard int ret = 0; 3356f684d4fSMaxime Ripard 3366f684d4fSMaxime Ripard mutex_lock(&csi2tx->lock); 3376f684d4fSMaxime Ripard 3386f684d4fSMaxime Ripard if (enable) { 3396f684d4fSMaxime Ripard /* 3406f684d4fSMaxime Ripard * If we're not the first users, there's no need to 3416f684d4fSMaxime Ripard * enable the whole controller. 3426f684d4fSMaxime Ripard */ 3436f684d4fSMaxime Ripard if (!csi2tx->count) { 3446f684d4fSMaxime Ripard ret = csi2tx_start(csi2tx); 3456f684d4fSMaxime Ripard if (ret) 3466f684d4fSMaxime Ripard goto out; 3476f684d4fSMaxime Ripard } 3486f684d4fSMaxime Ripard 3496f684d4fSMaxime Ripard csi2tx->count++; 3506f684d4fSMaxime Ripard } else { 3516f684d4fSMaxime Ripard csi2tx->count--; 3526f684d4fSMaxime Ripard 3536f684d4fSMaxime Ripard /* 3546f684d4fSMaxime Ripard * Let the last user turn off the lights. 3556f684d4fSMaxime Ripard */ 3566f684d4fSMaxime Ripard if (!csi2tx->count) 3576f684d4fSMaxime Ripard csi2tx_stop(csi2tx); 3586f684d4fSMaxime Ripard } 3596f684d4fSMaxime Ripard 3606f684d4fSMaxime Ripard out: 3616f684d4fSMaxime Ripard mutex_unlock(&csi2tx->lock); 3626f684d4fSMaxime Ripard return ret; 3636f684d4fSMaxime Ripard } 3646f684d4fSMaxime Ripard 3656f684d4fSMaxime Ripard static const struct v4l2_subdev_video_ops csi2tx_video_ops = { 3666f684d4fSMaxime Ripard .s_stream = csi2tx_s_stream, 3676f684d4fSMaxime Ripard }; 3686f684d4fSMaxime Ripard 3696f684d4fSMaxime Ripard static const struct v4l2_subdev_ops csi2tx_subdev_ops = { 3706f684d4fSMaxime Ripard .pad = &csi2tx_pad_ops, 3716f684d4fSMaxime Ripard .video = &csi2tx_video_ops, 3726f684d4fSMaxime Ripard }; 3736f684d4fSMaxime Ripard 3746f684d4fSMaxime Ripard static int csi2tx_get_resources(struct csi2tx_priv *csi2tx, 3756f684d4fSMaxime Ripard struct platform_device *pdev) 3766f684d4fSMaxime Ripard { 3776f684d4fSMaxime Ripard struct resource *res; 3786f684d4fSMaxime Ripard unsigned int i; 3796f684d4fSMaxime Ripard u32 dev_cfg; 3806f684d4fSMaxime Ripard 3816f684d4fSMaxime Ripard res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 3826f684d4fSMaxime Ripard csi2tx->base = devm_ioremap_resource(&pdev->dev, res); 3836f684d4fSMaxime Ripard if (IS_ERR(csi2tx->base)) 3846f684d4fSMaxime Ripard return PTR_ERR(csi2tx->base); 3856f684d4fSMaxime Ripard 3866f684d4fSMaxime Ripard csi2tx->p_clk = devm_clk_get(&pdev->dev, "p_clk"); 3876f684d4fSMaxime Ripard if (IS_ERR(csi2tx->p_clk)) { 3886f684d4fSMaxime Ripard dev_err(&pdev->dev, "Couldn't get p_clk\n"); 3896f684d4fSMaxime Ripard return PTR_ERR(csi2tx->p_clk); 3906f684d4fSMaxime Ripard } 3916f684d4fSMaxime Ripard 3926f684d4fSMaxime Ripard csi2tx->esc_clk = devm_clk_get(&pdev->dev, "esc_clk"); 3936f684d4fSMaxime Ripard if (IS_ERR(csi2tx->esc_clk)) { 3946f684d4fSMaxime Ripard dev_err(&pdev->dev, "Couldn't get the esc_clk\n"); 3956f684d4fSMaxime Ripard return PTR_ERR(csi2tx->esc_clk); 3966f684d4fSMaxime Ripard } 3976f684d4fSMaxime Ripard 3986f684d4fSMaxime Ripard clk_prepare_enable(csi2tx->p_clk); 3996f684d4fSMaxime Ripard dev_cfg = readl(csi2tx->base + CSI2TX_DEVICE_CONFIG_REG); 4006f684d4fSMaxime Ripard clk_disable_unprepare(csi2tx->p_clk); 4016f684d4fSMaxime Ripard 4026f684d4fSMaxime Ripard csi2tx->max_lanes = dev_cfg & CSI2TX_DEVICE_CONFIG_LANES_MASK; 4036f684d4fSMaxime Ripard if (csi2tx->max_lanes > CSI2TX_LANES_MAX) { 4046f684d4fSMaxime Ripard dev_err(&pdev->dev, "Invalid number of lanes: %u\n", 4056f684d4fSMaxime Ripard csi2tx->max_lanes); 4066f684d4fSMaxime Ripard return -EINVAL; 4076f684d4fSMaxime Ripard } 4086f684d4fSMaxime Ripard 4096f684d4fSMaxime Ripard csi2tx->max_streams = (dev_cfg & CSI2TX_DEVICE_CONFIG_STREAMS_MASK) >> 4; 4106f684d4fSMaxime Ripard if (csi2tx->max_streams > CSI2TX_STREAMS_MAX) { 4116f684d4fSMaxime Ripard dev_err(&pdev->dev, "Invalid number of streams: %u\n", 4126f684d4fSMaxime Ripard csi2tx->max_streams); 4136f684d4fSMaxime Ripard return -EINVAL; 4146f684d4fSMaxime Ripard } 4156f684d4fSMaxime Ripard 4166f684d4fSMaxime Ripard csi2tx->has_internal_dphy = !!(dev_cfg & CSI2TX_DEVICE_CONFIG_HAS_DPHY); 4176f684d4fSMaxime Ripard 4186f684d4fSMaxime Ripard for (i = 0; i < csi2tx->max_streams; i++) { 4196f684d4fSMaxime Ripard char clk_name[16]; 4206f684d4fSMaxime Ripard 4216f684d4fSMaxime Ripard snprintf(clk_name, sizeof(clk_name), "pixel_if%u_clk", i); 4226f684d4fSMaxime Ripard csi2tx->pixel_clk[i] = devm_clk_get(&pdev->dev, clk_name); 4236f684d4fSMaxime Ripard if (IS_ERR(csi2tx->pixel_clk[i])) { 4246f684d4fSMaxime Ripard dev_err(&pdev->dev, "Couldn't get clock %s\n", 4256f684d4fSMaxime Ripard clk_name); 4266f684d4fSMaxime Ripard return PTR_ERR(csi2tx->pixel_clk[i]); 4276f684d4fSMaxime Ripard } 4286f684d4fSMaxime Ripard } 4296f684d4fSMaxime Ripard 4306f684d4fSMaxime Ripard return 0; 4316f684d4fSMaxime Ripard } 4326f684d4fSMaxime Ripard 4336f684d4fSMaxime Ripard static int csi2tx_check_lanes(struct csi2tx_priv *csi2tx) 4346f684d4fSMaxime Ripard { 435*60359a28SSakari Ailus struct v4l2_fwnode_endpoint v4l2_ep = { .bus_type = 0 }; 4366f684d4fSMaxime Ripard struct device_node *ep; 4376f684d4fSMaxime Ripard int ret; 4386f684d4fSMaxime Ripard 4396f684d4fSMaxime Ripard ep = of_graph_get_endpoint_by_regs(csi2tx->dev->of_node, 0, 0); 4406f684d4fSMaxime Ripard if (!ep) 4416f684d4fSMaxime Ripard return -EINVAL; 4426f684d4fSMaxime Ripard 4436f684d4fSMaxime Ripard ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &v4l2_ep); 4446f684d4fSMaxime Ripard if (ret) { 4456f684d4fSMaxime Ripard dev_err(csi2tx->dev, "Could not parse v4l2 endpoint\n"); 4466f684d4fSMaxime Ripard goto out; 4476f684d4fSMaxime Ripard } 4486f684d4fSMaxime Ripard 4492d95e7edSSakari Ailus if (v4l2_ep.bus_type != V4L2_MBUS_CSI2_DPHY) { 4506f684d4fSMaxime Ripard dev_err(csi2tx->dev, "Unsupported media bus type: 0x%x\n", 4516f684d4fSMaxime Ripard v4l2_ep.bus_type); 4526f684d4fSMaxime Ripard ret = -EINVAL; 4536f684d4fSMaxime Ripard goto out; 4546f684d4fSMaxime Ripard } 4556f684d4fSMaxime Ripard 4566f684d4fSMaxime Ripard csi2tx->num_lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes; 4576f684d4fSMaxime Ripard if (csi2tx->num_lanes > csi2tx->max_lanes) { 4586f684d4fSMaxime Ripard dev_err(csi2tx->dev, 4596f684d4fSMaxime Ripard "Current configuration uses more lanes than supported\n"); 4606f684d4fSMaxime Ripard ret = -EINVAL; 4616f684d4fSMaxime Ripard goto out; 4626f684d4fSMaxime Ripard } 4636f684d4fSMaxime Ripard 4646f684d4fSMaxime Ripard memcpy(csi2tx->lanes, v4l2_ep.bus.mipi_csi2.data_lanes, 4656f684d4fSMaxime Ripard sizeof(csi2tx->lanes)); 4666f684d4fSMaxime Ripard 4676f684d4fSMaxime Ripard out: 4686f684d4fSMaxime Ripard of_node_put(ep); 4696f684d4fSMaxime Ripard return ret; 4706f684d4fSMaxime Ripard } 4716f684d4fSMaxime Ripard 4726f684d4fSMaxime Ripard static int csi2tx_probe(struct platform_device *pdev) 4736f684d4fSMaxime Ripard { 4746f684d4fSMaxime Ripard struct csi2tx_priv *csi2tx; 4756f684d4fSMaxime Ripard unsigned int i; 4766f684d4fSMaxime Ripard int ret; 4776f684d4fSMaxime Ripard 4786f684d4fSMaxime Ripard csi2tx = kzalloc(sizeof(*csi2tx), GFP_KERNEL); 4796f684d4fSMaxime Ripard if (!csi2tx) 4806f684d4fSMaxime Ripard return -ENOMEM; 4816f684d4fSMaxime Ripard platform_set_drvdata(pdev, csi2tx); 4826f684d4fSMaxime Ripard mutex_init(&csi2tx->lock); 4836f684d4fSMaxime Ripard csi2tx->dev = &pdev->dev; 4846f684d4fSMaxime Ripard 4856f684d4fSMaxime Ripard ret = csi2tx_get_resources(csi2tx, pdev); 4866f684d4fSMaxime Ripard if (ret) 4876f684d4fSMaxime Ripard goto err_free_priv; 4886f684d4fSMaxime Ripard 4896f684d4fSMaxime Ripard v4l2_subdev_init(&csi2tx->subdev, &csi2tx_subdev_ops); 4906f684d4fSMaxime Ripard csi2tx->subdev.owner = THIS_MODULE; 4916f684d4fSMaxime Ripard csi2tx->subdev.dev = &pdev->dev; 4926f684d4fSMaxime Ripard csi2tx->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 4936f684d4fSMaxime Ripard snprintf(csi2tx->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s.%s", 4946f684d4fSMaxime Ripard KBUILD_MODNAME, dev_name(&pdev->dev)); 4956f684d4fSMaxime Ripard 4966f684d4fSMaxime Ripard ret = csi2tx_check_lanes(csi2tx); 4976f684d4fSMaxime Ripard if (ret) 4986f684d4fSMaxime Ripard goto err_free_priv; 4996f684d4fSMaxime Ripard 5006f684d4fSMaxime Ripard /* Create our media pads */ 5016f684d4fSMaxime Ripard csi2tx->subdev.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; 5026f684d4fSMaxime Ripard csi2tx->pads[CSI2TX_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; 5036f684d4fSMaxime Ripard for (i = CSI2TX_PAD_SINK_STREAM0; i < CSI2TX_PAD_MAX; i++) 5046f684d4fSMaxime Ripard csi2tx->pads[i].flags = MEDIA_PAD_FL_SINK; 5056f684d4fSMaxime Ripard 5066f684d4fSMaxime Ripard /* 5076f684d4fSMaxime Ripard * Only the input pads are considered to have a format at the 5086f684d4fSMaxime Ripard * moment. The CSI link can multiplex various streams with 5096f684d4fSMaxime Ripard * different formats, and we can't expose this in v4l2 right 5106f684d4fSMaxime Ripard * now. 5116f684d4fSMaxime Ripard */ 5126f684d4fSMaxime Ripard for (i = CSI2TX_PAD_SINK_STREAM0; i < CSI2TX_PAD_MAX; i++) 5136f684d4fSMaxime Ripard csi2tx->pad_fmts[i] = fmt_default; 5146f684d4fSMaxime Ripard 5156f684d4fSMaxime Ripard ret = media_entity_pads_init(&csi2tx->subdev.entity, CSI2TX_PAD_MAX, 5166f684d4fSMaxime Ripard csi2tx->pads); 5176f684d4fSMaxime Ripard if (ret) 5186f684d4fSMaxime Ripard goto err_free_priv; 5196f684d4fSMaxime Ripard 5206f684d4fSMaxime Ripard ret = v4l2_async_register_subdev(&csi2tx->subdev); 5216f684d4fSMaxime Ripard if (ret < 0) 5226f684d4fSMaxime Ripard goto err_free_priv; 5236f684d4fSMaxime Ripard 5246f684d4fSMaxime Ripard dev_info(&pdev->dev, 5256f684d4fSMaxime Ripard "Probed CSI2TX with %u/%u lanes, %u streams, %s D-PHY\n", 5266f684d4fSMaxime Ripard csi2tx->num_lanes, csi2tx->max_lanes, csi2tx->max_streams, 5276f684d4fSMaxime Ripard csi2tx->has_internal_dphy ? "internal" : "no"); 5286f684d4fSMaxime Ripard 5296f684d4fSMaxime Ripard return 0; 5306f684d4fSMaxime Ripard 5316f684d4fSMaxime Ripard err_free_priv: 5326f684d4fSMaxime Ripard kfree(csi2tx); 5336f684d4fSMaxime Ripard return ret; 5346f684d4fSMaxime Ripard } 5356f684d4fSMaxime Ripard 5366f684d4fSMaxime Ripard static int csi2tx_remove(struct platform_device *pdev) 5376f684d4fSMaxime Ripard { 5386f684d4fSMaxime Ripard struct csi2tx_priv *csi2tx = platform_get_drvdata(pdev); 5396f684d4fSMaxime Ripard 5406f684d4fSMaxime Ripard v4l2_async_unregister_subdev(&csi2tx->subdev); 5416f684d4fSMaxime Ripard kfree(csi2tx); 5426f684d4fSMaxime Ripard 5436f684d4fSMaxime Ripard return 0; 5446f684d4fSMaxime Ripard } 5456f684d4fSMaxime Ripard 5466f684d4fSMaxime Ripard static const struct of_device_id csi2tx_of_table[] = { 5476f684d4fSMaxime Ripard { .compatible = "cdns,csi2tx" }, 5486f684d4fSMaxime Ripard { }, 5496f684d4fSMaxime Ripard }; 5506f684d4fSMaxime Ripard MODULE_DEVICE_TABLE(of, csi2tx_of_table); 5516f684d4fSMaxime Ripard 5526f684d4fSMaxime Ripard static struct platform_driver csi2tx_driver = { 5536f684d4fSMaxime Ripard .probe = csi2tx_probe, 5546f684d4fSMaxime Ripard .remove = csi2tx_remove, 5556f684d4fSMaxime Ripard 5566f684d4fSMaxime Ripard .driver = { 5576f684d4fSMaxime Ripard .name = "cdns-csi2tx", 5586f684d4fSMaxime Ripard .of_match_table = csi2tx_of_table, 5596f684d4fSMaxime Ripard }, 5606f684d4fSMaxime Ripard }; 5616f684d4fSMaxime Ripard module_platform_driver(csi2tx_driver); 5626f684d4fSMaxime Ripard MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin.com>"); 5636f684d4fSMaxime Ripard MODULE_DESCRIPTION("Cadence CSI2-TX controller"); 5646f684d4fSMaxime Ripard MODULE_LICENSE("GPL"); 565