11fc3b37fSMaxime Ripard // SPDX-License-Identifier: GPL-2.0+ 21fc3b37fSMaxime Ripard /* 31fc3b37fSMaxime Ripard * Driver for Cadence MIPI-CSI2 RX Controller v1.3 41fc3b37fSMaxime Ripard * 51fc3b37fSMaxime Ripard * Copyright (C) 2017 Cadence Design Systems Inc. 61fc3b37fSMaxime Ripard */ 71fc3b37fSMaxime Ripard 81fc3b37fSMaxime Ripard #include <linux/clk.h> 91fc3b37fSMaxime Ripard #include <linux/delay.h> 101fc3b37fSMaxime Ripard #include <linux/io.h> 11a64175faSPratyush Yadav #include <linux/iopoll.h> 121fc3b37fSMaxime Ripard #include <linux/module.h> 131fc3b37fSMaxime Ripard #include <linux/of.h> 141fc3b37fSMaxime Ripard #include <linux/of_graph.h> 151fc3b37fSMaxime Ripard #include <linux/phy/phy.h> 161fc3b37fSMaxime Ripard #include <linux/platform_device.h> 17e0b9ce38SJack Zhu #include <linux/reset.h> 183c46ab9dSArnd Bergmann #include <linux/slab.h> 191fc3b37fSMaxime Ripard 201fc3b37fSMaxime Ripard #include <media/v4l2-ctrls.h> 211fc3b37fSMaxime Ripard #include <media/v4l2-device.h> 221fc3b37fSMaxime Ripard #include <media/v4l2-fwnode.h> 231fc3b37fSMaxime Ripard #include <media/v4l2-subdev.h> 241fc3b37fSMaxime Ripard 251fc3b37fSMaxime Ripard #define CSI2RX_DEVICE_CFG_REG 0x000 261fc3b37fSMaxime Ripard 271fc3b37fSMaxime Ripard #define CSI2RX_SOFT_RESET_REG 0x004 281fc3b37fSMaxime Ripard #define CSI2RX_SOFT_RESET_PROTOCOL BIT(1) 291fc3b37fSMaxime Ripard #define CSI2RX_SOFT_RESET_FRONT BIT(0) 301fc3b37fSMaxime Ripard 311fc3b37fSMaxime Ripard #define CSI2RX_STATIC_CFG_REG 0x008 321fc3b37fSMaxime Ripard #define CSI2RX_STATIC_CFG_DLANE_MAP(llane, plane) ((plane) << (16 + (llane) * 4)) 331fc3b37fSMaxime Ripard #define CSI2RX_STATIC_CFG_LANES_MASK GENMASK(11, 8) 341fc3b37fSMaxime Ripard 353295cf12SJack Zhu #define CSI2RX_DPHY_LANE_CTRL_REG 0x40 363295cf12SJack Zhu #define CSI2RX_DPHY_CL_RST BIT(16) 373295cf12SJack Zhu #define CSI2RX_DPHY_DL_RST(i) BIT((i) + 12) 383295cf12SJack Zhu #define CSI2RX_DPHY_CL_EN BIT(4) 393295cf12SJack Zhu #define CSI2RX_DPHY_DL_EN(i) BIT(i) 403295cf12SJack Zhu 411fc3b37fSMaxime Ripard #define CSI2RX_STREAM_BASE(n) (((n) + 1) * 0x100) 421fc3b37fSMaxime Ripard 431fc3b37fSMaxime Ripard #define CSI2RX_STREAM_CTRL_REG(n) (CSI2RX_STREAM_BASE(n) + 0x000) 446f28a427SPratyush Yadav #define CSI2RX_STREAM_CTRL_SOFT_RST BIT(4) 45a64175faSPratyush Yadav #define CSI2RX_STREAM_CTRL_STOP BIT(1) 461fc3b37fSMaxime Ripard #define CSI2RX_STREAM_CTRL_START BIT(0) 471fc3b37fSMaxime Ripard 48a64175faSPratyush Yadav #define CSI2RX_STREAM_STATUS_REG(n) (CSI2RX_STREAM_BASE(n) + 0x004) 49a64175faSPratyush Yadav #define CSI2RX_STREAM_STATUS_RDY BIT(31) 50a64175faSPratyush Yadav 511fc3b37fSMaxime Ripard #define CSI2RX_STREAM_DATA_CFG_REG(n) (CSI2RX_STREAM_BASE(n) + 0x008) 521fc3b37fSMaxime Ripard #define CSI2RX_STREAM_DATA_CFG_VC_SELECT(n) BIT((n) + 16) 531fc3b37fSMaxime Ripard 541fc3b37fSMaxime Ripard #define CSI2RX_STREAM_CFG_REG(n) (CSI2RX_STREAM_BASE(n) + 0x00c) 551fc3b37fSMaxime Ripard #define CSI2RX_STREAM_CFG_FIFO_MODE_LARGE_BUF (1 << 8) 561fc3b37fSMaxime Ripard 571fc3b37fSMaxime Ripard #define CSI2RX_LANES_MAX 4 581fc3b37fSMaxime Ripard #define CSI2RX_STREAMS_MAX 4 591fc3b37fSMaxime Ripard 601fc3b37fSMaxime Ripard enum csi2rx_pads { 611fc3b37fSMaxime Ripard CSI2RX_PAD_SINK, 621fc3b37fSMaxime Ripard CSI2RX_PAD_SOURCE_STREAM0, 631fc3b37fSMaxime Ripard CSI2RX_PAD_SOURCE_STREAM1, 641fc3b37fSMaxime Ripard CSI2RX_PAD_SOURCE_STREAM2, 651fc3b37fSMaxime Ripard CSI2RX_PAD_SOURCE_STREAM3, 661fc3b37fSMaxime Ripard CSI2RX_PAD_MAX, 671fc3b37fSMaxime Ripard }; 681fc3b37fSMaxime Ripard 69dbca7b3cSPratyush Yadav struct csi2rx_fmt { 70dbca7b3cSPratyush Yadav u32 code; 71dbca7b3cSPratyush Yadav u8 bpp; 72dbca7b3cSPratyush Yadav }; 73dbca7b3cSPratyush Yadav 741fc3b37fSMaxime Ripard struct csi2rx_priv { 751fc3b37fSMaxime Ripard struct device *dev; 761fc3b37fSMaxime Ripard unsigned int count; 771fc3b37fSMaxime Ripard 781fc3b37fSMaxime Ripard /* 791fc3b37fSMaxime Ripard * Used to prevent race conditions between multiple, 801fc3b37fSMaxime Ripard * concurrent calls to start and stop. 811fc3b37fSMaxime Ripard */ 821fc3b37fSMaxime Ripard struct mutex lock; 831fc3b37fSMaxime Ripard 841fc3b37fSMaxime Ripard void __iomem *base; 851fc3b37fSMaxime Ripard struct clk *sys_clk; 861fc3b37fSMaxime Ripard struct clk *p_clk; 871fc3b37fSMaxime Ripard struct clk *pixel_clk[CSI2RX_STREAMS_MAX]; 88e0b9ce38SJack Zhu struct reset_control *sys_rst; 89e0b9ce38SJack Zhu struct reset_control *p_rst; 90e0b9ce38SJack Zhu struct reset_control *pixel_rst[CSI2RX_STREAMS_MAX]; 911fc3b37fSMaxime Ripard struct phy *dphy; 921fc3b37fSMaxime Ripard 931fc3b37fSMaxime Ripard u8 lanes[CSI2RX_LANES_MAX]; 941fc3b37fSMaxime Ripard u8 num_lanes; 951fc3b37fSMaxime Ripard u8 max_lanes; 961fc3b37fSMaxime Ripard u8 max_streams; 971fc3b37fSMaxime Ripard bool has_internal_dphy; 981fc3b37fSMaxime Ripard 991fc3b37fSMaxime Ripard struct v4l2_subdev subdev; 1001fc3b37fSMaxime Ripard struct v4l2_async_notifier notifier; 1011fc3b37fSMaxime Ripard struct media_pad pads[CSI2RX_PAD_MAX]; 1021fc3b37fSMaxime Ripard 1031fc3b37fSMaxime Ripard /* Remote source */ 1041fc3b37fSMaxime Ripard struct v4l2_subdev *source_subdev; 1051fc3b37fSMaxime Ripard int source_pad; 1061fc3b37fSMaxime Ripard }; 1071fc3b37fSMaxime Ripard 108dbca7b3cSPratyush Yadav static const struct csi2rx_fmt formats[] = { 109dbca7b3cSPratyush Yadav { .code = MEDIA_BUS_FMT_YUYV8_1X16, .bpp = 16, }, 110dbca7b3cSPratyush Yadav { .code = MEDIA_BUS_FMT_UYVY8_1X16, .bpp = 16, }, 111dbca7b3cSPratyush Yadav { .code = MEDIA_BUS_FMT_YVYU8_1X16, .bpp = 16, }, 112dbca7b3cSPratyush Yadav { .code = MEDIA_BUS_FMT_VYUY8_1X16, .bpp = 16, }, 113dbca7b3cSPratyush Yadav { .code = MEDIA_BUS_FMT_SBGGR8_1X8, .bpp = 8, }, 114dbca7b3cSPratyush Yadav { .code = MEDIA_BUS_FMT_SGBRG8_1X8, .bpp = 8, }, 115dbca7b3cSPratyush Yadav { .code = MEDIA_BUS_FMT_SGRBG8_1X8, .bpp = 8, }, 116dbca7b3cSPratyush Yadav { .code = MEDIA_BUS_FMT_SRGGB8_1X8, .bpp = 8, }, 117*5b2a3687SJulien Massot { .code = MEDIA_BUS_FMT_Y8_1X8, .bpp = 8, }, 118dbca7b3cSPratyush Yadav { .code = MEDIA_BUS_FMT_SBGGR10_1X10, .bpp = 10, }, 119dbca7b3cSPratyush Yadav { .code = MEDIA_BUS_FMT_SGBRG10_1X10, .bpp = 10, }, 120dbca7b3cSPratyush Yadav { .code = MEDIA_BUS_FMT_SGRBG10_1X10, .bpp = 10, }, 121dbca7b3cSPratyush Yadav { .code = MEDIA_BUS_FMT_SRGGB10_1X10, .bpp = 10, }, 122dbca7b3cSPratyush Yadav }; 123dbca7b3cSPratyush Yadav 124dbca7b3cSPratyush Yadav static const struct csi2rx_fmt *csi2rx_get_fmt_by_code(u32 code) 125dbca7b3cSPratyush Yadav { 126dbca7b3cSPratyush Yadav unsigned int i; 127dbca7b3cSPratyush Yadav 128dbca7b3cSPratyush Yadav for (i = 0; i < ARRAY_SIZE(formats); i++) 129dbca7b3cSPratyush Yadav if (formats[i].code == code) 130dbca7b3cSPratyush Yadav return &formats[i]; 131dbca7b3cSPratyush Yadav 132dbca7b3cSPratyush Yadav return NULL; 133dbca7b3cSPratyush Yadav } 134dbca7b3cSPratyush Yadav 1351fc3b37fSMaxime Ripard static inline 1361fc3b37fSMaxime Ripard struct csi2rx_priv *v4l2_subdev_to_csi2rx(struct v4l2_subdev *subdev) 1371fc3b37fSMaxime Ripard { 1381fc3b37fSMaxime Ripard return container_of(subdev, struct csi2rx_priv, subdev); 1391fc3b37fSMaxime Ripard } 1401fc3b37fSMaxime Ripard 1411fc3b37fSMaxime Ripard static void csi2rx_reset(struct csi2rx_priv *csi2rx) 1421fc3b37fSMaxime Ripard { 1436f28a427SPratyush Yadav unsigned int i; 1446f28a427SPratyush Yadav 1456f28a427SPratyush Yadav /* Reset module */ 1461fc3b37fSMaxime Ripard writel(CSI2RX_SOFT_RESET_PROTOCOL | CSI2RX_SOFT_RESET_FRONT, 1471fc3b37fSMaxime Ripard csi2rx->base + CSI2RX_SOFT_RESET_REG); 1486f28a427SPratyush Yadav /* Reset individual streams. */ 1496f28a427SPratyush Yadav for (i = 0; i < csi2rx->max_streams; i++) { 1506f28a427SPratyush Yadav writel(CSI2RX_STREAM_CTRL_SOFT_RST, 1516f28a427SPratyush Yadav csi2rx->base + CSI2RX_STREAM_CTRL_REG(i)); 1526f28a427SPratyush Yadav } 1531fc3b37fSMaxime Ripard 1546f28a427SPratyush Yadav usleep_range(10, 20); 1551fc3b37fSMaxime Ripard 1566f28a427SPratyush Yadav /* Clear resets */ 1571fc3b37fSMaxime Ripard writel(0, csi2rx->base + CSI2RX_SOFT_RESET_REG); 1586f28a427SPratyush Yadav for (i = 0; i < csi2rx->max_streams; i++) 1596f28a427SPratyush Yadav writel(0, csi2rx->base + CSI2RX_STREAM_CTRL_REG(i)); 1601fc3b37fSMaxime Ripard } 1611fc3b37fSMaxime Ripard 1623295cf12SJack Zhu static int csi2rx_configure_ext_dphy(struct csi2rx_priv *csi2rx) 1633295cf12SJack Zhu { 1643295cf12SJack Zhu union phy_configure_opts opts = { }; 165a91d06f4SPratyush Yadav struct phy_configure_opts_mipi_dphy *cfg = &opts.mipi_dphy; 166a91d06f4SPratyush Yadav struct v4l2_subdev_format sd_fmt = { 167a91d06f4SPratyush Yadav .which = V4L2_SUBDEV_FORMAT_ACTIVE, 168a91d06f4SPratyush Yadav .pad = CSI2RX_PAD_SINK, 169a91d06f4SPratyush Yadav }; 170a91d06f4SPratyush Yadav const struct csi2rx_fmt *fmt; 171a91d06f4SPratyush Yadav s64 link_freq; 1723295cf12SJack Zhu int ret; 1733295cf12SJack Zhu 174a91d06f4SPratyush Yadav ret = v4l2_subdev_call_state_active(&csi2rx->subdev, pad, get_fmt, 175a91d06f4SPratyush Yadav &sd_fmt); 176a91d06f4SPratyush Yadav if (ret < 0) 177a91d06f4SPratyush Yadav return ret; 178a91d06f4SPratyush Yadav 179a91d06f4SPratyush Yadav fmt = csi2rx_get_fmt_by_code(sd_fmt.format.code); 180a91d06f4SPratyush Yadav 181a91d06f4SPratyush Yadav link_freq = v4l2_get_link_freq(csi2rx->source_subdev->ctrl_handler, 182a91d06f4SPratyush Yadav fmt->bpp, 2 * csi2rx->num_lanes); 183a91d06f4SPratyush Yadav if (link_freq < 0) 184a91d06f4SPratyush Yadav return link_freq; 185a91d06f4SPratyush Yadav 186a91d06f4SPratyush Yadav ret = phy_mipi_dphy_get_default_config_for_hsclk(link_freq, 187a91d06f4SPratyush Yadav csi2rx->num_lanes, cfg); 188a91d06f4SPratyush Yadav if (ret) 189a91d06f4SPratyush Yadav return ret; 190a91d06f4SPratyush Yadav 1913295cf12SJack Zhu ret = phy_power_on(csi2rx->dphy); 1923295cf12SJack Zhu if (ret) 1933295cf12SJack Zhu return ret; 1943295cf12SJack Zhu 1953295cf12SJack Zhu ret = phy_configure(csi2rx->dphy, &opts); 1963295cf12SJack Zhu if (ret) { 1973295cf12SJack Zhu phy_power_off(csi2rx->dphy); 1983295cf12SJack Zhu return ret; 1993295cf12SJack Zhu } 2003295cf12SJack Zhu 2013295cf12SJack Zhu return 0; 2023295cf12SJack Zhu } 2033295cf12SJack Zhu 2041fc3b37fSMaxime Ripard static int csi2rx_start(struct csi2rx_priv *csi2rx) 2051fc3b37fSMaxime Ripard { 2061fc3b37fSMaxime Ripard unsigned int i; 2071fc3b37fSMaxime Ripard unsigned long lanes_used = 0; 2081fc3b37fSMaxime Ripard u32 reg; 2091fc3b37fSMaxime Ripard int ret; 2101fc3b37fSMaxime Ripard 2111fc3b37fSMaxime Ripard ret = clk_prepare_enable(csi2rx->p_clk); 2121fc3b37fSMaxime Ripard if (ret) 2131fc3b37fSMaxime Ripard return ret; 2141fc3b37fSMaxime Ripard 215e0b9ce38SJack Zhu reset_control_deassert(csi2rx->p_rst); 2161fc3b37fSMaxime Ripard csi2rx_reset(csi2rx); 2171fc3b37fSMaxime Ripard 2181fc3b37fSMaxime Ripard reg = csi2rx->num_lanes << 8; 2191fc3b37fSMaxime Ripard for (i = 0; i < csi2rx->num_lanes; i++) { 2201fc3b37fSMaxime Ripard reg |= CSI2RX_STATIC_CFG_DLANE_MAP(i, csi2rx->lanes[i]); 2211fc3b37fSMaxime Ripard set_bit(csi2rx->lanes[i], &lanes_used); 2221fc3b37fSMaxime Ripard } 2231fc3b37fSMaxime Ripard 2241fc3b37fSMaxime Ripard /* 2251fc3b37fSMaxime Ripard * Even the unused lanes need to be mapped. In order to avoid 2261fc3b37fSMaxime Ripard * to map twice to the same physical lane, keep the lanes used 2271fc3b37fSMaxime Ripard * in the previous loop, and only map unused physical lanes to 2281fc3b37fSMaxime Ripard * the rest of our logical lanes. 2291fc3b37fSMaxime Ripard */ 2301fc3b37fSMaxime Ripard for (i = csi2rx->num_lanes; i < csi2rx->max_lanes; i++) { 2311fc3b37fSMaxime Ripard unsigned int idx = find_first_zero_bit(&lanes_used, 2322eca8e4cSChristophe JAILLET csi2rx->max_lanes); 2331fc3b37fSMaxime Ripard set_bit(idx, &lanes_used); 2341fc3b37fSMaxime Ripard reg |= CSI2RX_STATIC_CFG_DLANE_MAP(i, i + 1); 2351fc3b37fSMaxime Ripard } 2361fc3b37fSMaxime Ripard 2371fc3b37fSMaxime Ripard writel(reg, csi2rx->base + CSI2RX_STATIC_CFG_REG); 2381fc3b37fSMaxime Ripard 2391fc3b37fSMaxime Ripard ret = v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, true); 2401fc3b37fSMaxime Ripard if (ret) 2411fc3b37fSMaxime Ripard goto err_disable_pclk; 2421fc3b37fSMaxime Ripard 2433295cf12SJack Zhu /* Enable DPHY clk and data lanes. */ 2443295cf12SJack Zhu if (csi2rx->dphy) { 2453295cf12SJack Zhu reg = CSI2RX_DPHY_CL_EN | CSI2RX_DPHY_CL_RST; 2463295cf12SJack Zhu for (i = 0; i < csi2rx->num_lanes; i++) { 2473295cf12SJack Zhu reg |= CSI2RX_DPHY_DL_EN(csi2rx->lanes[i] - 1); 2483295cf12SJack Zhu reg |= CSI2RX_DPHY_DL_RST(csi2rx->lanes[i] - 1); 2493295cf12SJack Zhu } 2503295cf12SJack Zhu 2513295cf12SJack Zhu writel(reg, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG); 2523295cf12SJack Zhu } 2533295cf12SJack Zhu 2541fc3b37fSMaxime Ripard /* 2551fc3b37fSMaxime Ripard * Create a static mapping between the CSI virtual channels 2561fc3b37fSMaxime Ripard * and the output stream. 2571fc3b37fSMaxime Ripard * 2581fc3b37fSMaxime Ripard * This should be enhanced, but v4l2 lacks the support for 2591fc3b37fSMaxime Ripard * changing that mapping dynamically. 2601fc3b37fSMaxime Ripard * 2611fc3b37fSMaxime Ripard * We also cannot enable and disable independent streams here, 2621fc3b37fSMaxime Ripard * hence the reference counting. 2631fc3b37fSMaxime Ripard */ 2641fc3b37fSMaxime Ripard for (i = 0; i < csi2rx->max_streams; i++) { 2651fc3b37fSMaxime Ripard ret = clk_prepare_enable(csi2rx->pixel_clk[i]); 2661fc3b37fSMaxime Ripard if (ret) 2671fc3b37fSMaxime Ripard goto err_disable_pixclk; 2681fc3b37fSMaxime Ripard 269e0b9ce38SJack Zhu reset_control_deassert(csi2rx->pixel_rst[i]); 270e0b9ce38SJack Zhu 2711fc3b37fSMaxime Ripard writel(CSI2RX_STREAM_CFG_FIFO_MODE_LARGE_BUF, 2721fc3b37fSMaxime Ripard csi2rx->base + CSI2RX_STREAM_CFG_REG(i)); 2731fc3b37fSMaxime Ripard 2741dd59274SPratyush Yadav /* 2751dd59274SPratyush Yadav * Enable one virtual channel. When multiple virtual channels 2761dd59274SPratyush Yadav * are supported this will have to be changed. 2771dd59274SPratyush Yadav */ 2781dd59274SPratyush Yadav writel(CSI2RX_STREAM_DATA_CFG_VC_SELECT(0), 2791fc3b37fSMaxime Ripard csi2rx->base + CSI2RX_STREAM_DATA_CFG_REG(i)); 2801fc3b37fSMaxime Ripard 2811fc3b37fSMaxime Ripard writel(CSI2RX_STREAM_CTRL_START, 2821fc3b37fSMaxime Ripard csi2rx->base + CSI2RX_STREAM_CTRL_REG(i)); 2831fc3b37fSMaxime Ripard } 2841fc3b37fSMaxime Ripard 2851fc3b37fSMaxime Ripard ret = clk_prepare_enable(csi2rx->sys_clk); 2861fc3b37fSMaxime Ripard if (ret) 2871fc3b37fSMaxime Ripard goto err_disable_pixclk; 2881fc3b37fSMaxime Ripard 289e0b9ce38SJack Zhu reset_control_deassert(csi2rx->sys_rst); 2903295cf12SJack Zhu 2913295cf12SJack Zhu if (csi2rx->dphy) { 2923295cf12SJack Zhu ret = csi2rx_configure_ext_dphy(csi2rx); 2933295cf12SJack Zhu if (ret) { 2943295cf12SJack Zhu dev_err(csi2rx->dev, 2953295cf12SJack Zhu "Failed to configure external DPHY: %d\n", ret); 2963295cf12SJack Zhu goto err_disable_sysclk; 2973295cf12SJack Zhu } 2983295cf12SJack Zhu } 2993295cf12SJack Zhu 3001fc3b37fSMaxime Ripard clk_disable_unprepare(csi2rx->p_clk); 3011fc3b37fSMaxime Ripard 3021fc3b37fSMaxime Ripard return 0; 3031fc3b37fSMaxime Ripard 3043295cf12SJack Zhu err_disable_sysclk: 3053295cf12SJack Zhu clk_disable_unprepare(csi2rx->sys_clk); 3061fc3b37fSMaxime Ripard err_disable_pixclk: 307e0b9ce38SJack Zhu for (; i > 0; i--) { 308e0b9ce38SJack Zhu reset_control_assert(csi2rx->pixel_rst[i - 1]); 30928d42d2fSSakari Ailus clk_disable_unprepare(csi2rx->pixel_clk[i - 1]); 310e0b9ce38SJack Zhu } 3111fc3b37fSMaxime Ripard 3121fc3b37fSMaxime Ripard err_disable_pclk: 3131fc3b37fSMaxime Ripard clk_disable_unprepare(csi2rx->p_clk); 3141fc3b37fSMaxime Ripard 3151fc3b37fSMaxime Ripard return ret; 3161fc3b37fSMaxime Ripard } 3171fc3b37fSMaxime Ripard 3181fc3b37fSMaxime Ripard static void csi2rx_stop(struct csi2rx_priv *csi2rx) 3191fc3b37fSMaxime Ripard { 3201fc3b37fSMaxime Ripard unsigned int i; 321a64175faSPratyush Yadav u32 val; 322a64175faSPratyush Yadav int ret; 3231fc3b37fSMaxime Ripard 3241fc3b37fSMaxime Ripard clk_prepare_enable(csi2rx->p_clk); 325e0b9ce38SJack Zhu reset_control_assert(csi2rx->sys_rst); 3261fc3b37fSMaxime Ripard clk_disable_unprepare(csi2rx->sys_clk); 3271fc3b37fSMaxime Ripard 3281fc3b37fSMaxime Ripard for (i = 0; i < csi2rx->max_streams; i++) { 329a64175faSPratyush Yadav writel(CSI2RX_STREAM_CTRL_STOP, 330a64175faSPratyush Yadav csi2rx->base + CSI2RX_STREAM_CTRL_REG(i)); 331a64175faSPratyush Yadav 332a64175faSPratyush Yadav ret = readl_relaxed_poll_timeout(csi2rx->base + 333a64175faSPratyush Yadav CSI2RX_STREAM_STATUS_REG(i), 334a64175faSPratyush Yadav val, 335a64175faSPratyush Yadav !(val & CSI2RX_STREAM_STATUS_RDY), 336a64175faSPratyush Yadav 10, 10000); 337a64175faSPratyush Yadav if (ret) 338a64175faSPratyush Yadav dev_warn(csi2rx->dev, 339a64175faSPratyush Yadav "Failed to stop streaming on pad%u\n", i); 3401fc3b37fSMaxime Ripard 341e0b9ce38SJack Zhu reset_control_assert(csi2rx->pixel_rst[i]); 3421fc3b37fSMaxime Ripard clk_disable_unprepare(csi2rx->pixel_clk[i]); 3431fc3b37fSMaxime Ripard } 3441fc3b37fSMaxime Ripard 345e0b9ce38SJack Zhu reset_control_assert(csi2rx->p_rst); 3461fc3b37fSMaxime Ripard clk_disable_unprepare(csi2rx->p_clk); 3471fc3b37fSMaxime Ripard 3481fc3b37fSMaxime Ripard if (v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, false)) 3491fc3b37fSMaxime Ripard dev_warn(csi2rx->dev, "Couldn't disable our subdev\n"); 3503295cf12SJack Zhu 3513295cf12SJack Zhu if (csi2rx->dphy) { 3523295cf12SJack Zhu writel(0, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG); 3533295cf12SJack Zhu 3543295cf12SJack Zhu if (phy_power_off(csi2rx->dphy)) 3553295cf12SJack Zhu dev_warn(csi2rx->dev, "Couldn't power off DPHY\n"); 3563295cf12SJack Zhu } 3571fc3b37fSMaxime Ripard } 3581fc3b37fSMaxime Ripard 3591fc3b37fSMaxime Ripard static int csi2rx_s_stream(struct v4l2_subdev *subdev, int enable) 3601fc3b37fSMaxime Ripard { 3611fc3b37fSMaxime Ripard struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev); 3621fc3b37fSMaxime Ripard int ret = 0; 3631fc3b37fSMaxime Ripard 3641fc3b37fSMaxime Ripard mutex_lock(&csi2rx->lock); 3651fc3b37fSMaxime Ripard 3661fc3b37fSMaxime Ripard if (enable) { 3671fc3b37fSMaxime Ripard /* 3681fc3b37fSMaxime Ripard * If we're not the first users, there's no need to 3691fc3b37fSMaxime Ripard * enable the whole controller. 3701fc3b37fSMaxime Ripard */ 3711fc3b37fSMaxime Ripard if (!csi2rx->count) { 3721fc3b37fSMaxime Ripard ret = csi2rx_start(csi2rx); 3731fc3b37fSMaxime Ripard if (ret) 3741fc3b37fSMaxime Ripard goto out; 3751fc3b37fSMaxime Ripard } 3761fc3b37fSMaxime Ripard 3771fc3b37fSMaxime Ripard csi2rx->count++; 3781fc3b37fSMaxime Ripard } else { 3791fc3b37fSMaxime Ripard csi2rx->count--; 3801fc3b37fSMaxime Ripard 3811fc3b37fSMaxime Ripard /* 3821fc3b37fSMaxime Ripard * Let the last user turn off the lights. 3831fc3b37fSMaxime Ripard */ 3841fc3b37fSMaxime Ripard if (!csi2rx->count) 3851fc3b37fSMaxime Ripard csi2rx_stop(csi2rx); 3861fc3b37fSMaxime Ripard } 3871fc3b37fSMaxime Ripard 3881fc3b37fSMaxime Ripard out: 3891fc3b37fSMaxime Ripard mutex_unlock(&csi2rx->lock); 3901fc3b37fSMaxime Ripard return ret; 3911fc3b37fSMaxime Ripard } 3921fc3b37fSMaxime Ripard 393dbca7b3cSPratyush Yadav static int csi2rx_set_fmt(struct v4l2_subdev *subdev, 394dbca7b3cSPratyush Yadav struct v4l2_subdev_state *state, 395dbca7b3cSPratyush Yadav struct v4l2_subdev_format *format) 396dbca7b3cSPratyush Yadav { 397dbca7b3cSPratyush Yadav struct v4l2_mbus_framefmt *fmt; 398dbca7b3cSPratyush Yadav unsigned int i; 399dbca7b3cSPratyush Yadav 400dbca7b3cSPratyush Yadav /* No transcoding, source and sink formats must match. */ 401dbca7b3cSPratyush Yadav if (format->pad != CSI2RX_PAD_SINK) 402dbca7b3cSPratyush Yadav return v4l2_subdev_get_fmt(subdev, state, format); 403dbca7b3cSPratyush Yadav 404dbca7b3cSPratyush Yadav if (!csi2rx_get_fmt_by_code(format->format.code)) 405dbca7b3cSPratyush Yadav format->format.code = formats[0].code; 406dbca7b3cSPratyush Yadav 407dbca7b3cSPratyush Yadav format->format.field = V4L2_FIELD_NONE; 408dbca7b3cSPratyush Yadav 409dbca7b3cSPratyush Yadav /* Set sink format */ 410bc0e8d91SSakari Ailus fmt = v4l2_subdev_state_get_format(state, format->pad); 411dbca7b3cSPratyush Yadav *fmt = format->format; 412dbca7b3cSPratyush Yadav 413dbca7b3cSPratyush Yadav /* Propagate to source formats */ 414dbca7b3cSPratyush Yadav for (i = CSI2RX_PAD_SOURCE_STREAM0; i < CSI2RX_PAD_MAX; i++) { 415bc0e8d91SSakari Ailus fmt = v4l2_subdev_state_get_format(state, i); 416dbca7b3cSPratyush Yadav *fmt = format->format; 417dbca7b3cSPratyush Yadav } 418dbca7b3cSPratyush Yadav 419dbca7b3cSPratyush Yadav return 0; 420dbca7b3cSPratyush Yadav } 421dbca7b3cSPratyush Yadav 4225755be5fSLaurent Pinchart static int csi2rx_init_state(struct v4l2_subdev *subdev, 423dbca7b3cSPratyush Yadav struct v4l2_subdev_state *state) 424dbca7b3cSPratyush Yadav { 425dbca7b3cSPratyush Yadav struct v4l2_subdev_format format = { 426dbca7b3cSPratyush Yadav .pad = CSI2RX_PAD_SINK, 427dbca7b3cSPratyush Yadav .format = { 428dbca7b3cSPratyush Yadav .width = 640, 429dbca7b3cSPratyush Yadav .height = 480, 430dbca7b3cSPratyush Yadav .code = MEDIA_BUS_FMT_UYVY8_1X16, 431dbca7b3cSPratyush Yadav .field = V4L2_FIELD_NONE, 432dbca7b3cSPratyush Yadav .colorspace = V4L2_COLORSPACE_SRGB, 433dbca7b3cSPratyush Yadav .ycbcr_enc = V4L2_YCBCR_ENC_601, 434dbca7b3cSPratyush Yadav .quantization = V4L2_QUANTIZATION_LIM_RANGE, 435dbca7b3cSPratyush Yadav .xfer_func = V4L2_XFER_FUNC_SRGB, 436dbca7b3cSPratyush Yadav }, 437dbca7b3cSPratyush Yadav }; 438dbca7b3cSPratyush Yadav 439dbca7b3cSPratyush Yadav return csi2rx_set_fmt(subdev, state, &format); 440dbca7b3cSPratyush Yadav } 441dbca7b3cSPratyush Yadav 442dbca7b3cSPratyush Yadav static const struct v4l2_subdev_pad_ops csi2rx_pad_ops = { 443dbca7b3cSPratyush Yadav .get_fmt = v4l2_subdev_get_fmt, 444dbca7b3cSPratyush Yadav .set_fmt = csi2rx_set_fmt, 445dbca7b3cSPratyush Yadav }; 446dbca7b3cSPratyush Yadav 4471fc3b37fSMaxime Ripard static const struct v4l2_subdev_video_ops csi2rx_video_ops = { 4481fc3b37fSMaxime Ripard .s_stream = csi2rx_s_stream, 4491fc3b37fSMaxime Ripard }; 4501fc3b37fSMaxime Ripard 4511fc3b37fSMaxime Ripard static const struct v4l2_subdev_ops csi2rx_subdev_ops = { 4521fc3b37fSMaxime Ripard .video = &csi2rx_video_ops, 453dbca7b3cSPratyush Yadav .pad = &csi2rx_pad_ops, 4541fc3b37fSMaxime Ripard }; 4551fc3b37fSMaxime Ripard 4565755be5fSLaurent Pinchart static const struct v4l2_subdev_internal_ops csi2rx_internal_ops = { 4575755be5fSLaurent Pinchart .init_state = csi2rx_init_state, 4585755be5fSLaurent Pinchart }; 4595755be5fSLaurent Pinchart 460b0f46ad6SPratyush Yadav static const struct media_entity_operations csi2rx_media_ops = { 461b0f46ad6SPratyush Yadav .link_validate = v4l2_subdev_link_validate, 462b0f46ad6SPratyush Yadav }; 463b0f46ad6SPratyush Yadav 4641fc3b37fSMaxime Ripard static int csi2rx_async_bound(struct v4l2_async_notifier *notifier, 4651fc3b37fSMaxime Ripard struct v4l2_subdev *s_subdev, 466adb2dcd5SSakari Ailus struct v4l2_async_connection *asd) 4671fc3b37fSMaxime Ripard { 4681fc3b37fSMaxime Ripard struct v4l2_subdev *subdev = notifier->sd; 4691fc3b37fSMaxime Ripard struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev); 4701fc3b37fSMaxime Ripard 4711fc3b37fSMaxime Ripard csi2rx->source_pad = media_entity_get_fwnode_pad(&s_subdev->entity, 472448699c5SJulien Massot asd->match.fwnode, 4731fc3b37fSMaxime Ripard MEDIA_PAD_FL_SOURCE); 4741fc3b37fSMaxime Ripard if (csi2rx->source_pad < 0) { 4751fc3b37fSMaxime Ripard dev_err(csi2rx->dev, "Couldn't find output pad for subdev %s\n", 4761fc3b37fSMaxime Ripard s_subdev->name); 4771fc3b37fSMaxime Ripard return csi2rx->source_pad; 4781fc3b37fSMaxime Ripard } 4791fc3b37fSMaxime Ripard 4801fc3b37fSMaxime Ripard csi2rx->source_subdev = s_subdev; 4811fc3b37fSMaxime Ripard 4821fc3b37fSMaxime Ripard dev_dbg(csi2rx->dev, "Bound %s pad: %d\n", s_subdev->name, 4831fc3b37fSMaxime Ripard csi2rx->source_pad); 4841fc3b37fSMaxime Ripard 4851fc3b37fSMaxime Ripard return media_create_pad_link(&csi2rx->source_subdev->entity, 4861fc3b37fSMaxime Ripard csi2rx->source_pad, 4871fc3b37fSMaxime Ripard &csi2rx->subdev.entity, 0, 4881fc3b37fSMaxime Ripard MEDIA_LNK_FL_ENABLED | 4891fc3b37fSMaxime Ripard MEDIA_LNK_FL_IMMUTABLE); 4901fc3b37fSMaxime Ripard } 4911fc3b37fSMaxime Ripard 4921fc3b37fSMaxime Ripard static const struct v4l2_async_notifier_operations csi2rx_notifier_ops = { 4931fc3b37fSMaxime Ripard .bound = csi2rx_async_bound, 4941fc3b37fSMaxime Ripard }; 4951fc3b37fSMaxime Ripard 4961fc3b37fSMaxime Ripard static int csi2rx_get_resources(struct csi2rx_priv *csi2rx, 4971fc3b37fSMaxime Ripard struct platform_device *pdev) 4981fc3b37fSMaxime Ripard { 4991fc3b37fSMaxime Ripard unsigned char i; 5001fc3b37fSMaxime Ripard u32 dev_cfg; 501cca65f64SEvgeny Novikov int ret; 5021fc3b37fSMaxime Ripard 503f5aae241SCai Huoqing csi2rx->base = devm_platform_ioremap_resource(pdev, 0); 5041fc3b37fSMaxime Ripard if (IS_ERR(csi2rx->base)) 5051fc3b37fSMaxime Ripard return PTR_ERR(csi2rx->base); 5061fc3b37fSMaxime Ripard 5071fc3b37fSMaxime Ripard csi2rx->sys_clk = devm_clk_get(&pdev->dev, "sys_clk"); 5081fc3b37fSMaxime Ripard if (IS_ERR(csi2rx->sys_clk)) { 5091fc3b37fSMaxime Ripard dev_err(&pdev->dev, "Couldn't get sys clock\n"); 5101fc3b37fSMaxime Ripard return PTR_ERR(csi2rx->sys_clk); 5111fc3b37fSMaxime Ripard } 5121fc3b37fSMaxime Ripard 5131fc3b37fSMaxime Ripard csi2rx->p_clk = devm_clk_get(&pdev->dev, "p_clk"); 5141fc3b37fSMaxime Ripard if (IS_ERR(csi2rx->p_clk)) { 5151fc3b37fSMaxime Ripard dev_err(&pdev->dev, "Couldn't get P clock\n"); 5161fc3b37fSMaxime Ripard return PTR_ERR(csi2rx->p_clk); 5171fc3b37fSMaxime Ripard } 5181fc3b37fSMaxime Ripard 519e0b9ce38SJack Zhu csi2rx->sys_rst = devm_reset_control_get_optional_exclusive(&pdev->dev, 520e0b9ce38SJack Zhu "sys"); 521e0b9ce38SJack Zhu if (IS_ERR(csi2rx->sys_rst)) 522e0b9ce38SJack Zhu return PTR_ERR(csi2rx->sys_rst); 523e0b9ce38SJack Zhu 524e0b9ce38SJack Zhu csi2rx->p_rst = devm_reset_control_get_optional_exclusive(&pdev->dev, 525e0b9ce38SJack Zhu "reg_bank"); 526e0b9ce38SJack Zhu if (IS_ERR(csi2rx->p_rst)) 527e0b9ce38SJack Zhu return PTR_ERR(csi2rx->p_rst); 528e0b9ce38SJack Zhu 5291fc3b37fSMaxime Ripard csi2rx->dphy = devm_phy_optional_get(&pdev->dev, "dphy"); 5301fc3b37fSMaxime Ripard if (IS_ERR(csi2rx->dphy)) { 5311fc3b37fSMaxime Ripard dev_err(&pdev->dev, "Couldn't get external D-PHY\n"); 5321fc3b37fSMaxime Ripard return PTR_ERR(csi2rx->dphy); 5331fc3b37fSMaxime Ripard } 5341fc3b37fSMaxime Ripard 535cca65f64SEvgeny Novikov ret = clk_prepare_enable(csi2rx->p_clk); 536cca65f64SEvgeny Novikov if (ret) { 537cca65f64SEvgeny Novikov dev_err(&pdev->dev, "Couldn't prepare and enable P clock\n"); 538cca65f64SEvgeny Novikov return ret; 539cca65f64SEvgeny Novikov } 540cca65f64SEvgeny Novikov 5411fc3b37fSMaxime Ripard dev_cfg = readl(csi2rx->base + CSI2RX_DEVICE_CFG_REG); 5421fc3b37fSMaxime Ripard clk_disable_unprepare(csi2rx->p_clk); 5431fc3b37fSMaxime Ripard 5441fc3b37fSMaxime Ripard csi2rx->max_lanes = dev_cfg & 7; 5451fc3b37fSMaxime Ripard if (csi2rx->max_lanes > CSI2RX_LANES_MAX) { 5461fc3b37fSMaxime Ripard dev_err(&pdev->dev, "Invalid number of lanes: %u\n", 5471fc3b37fSMaxime Ripard csi2rx->max_lanes); 5481fc3b37fSMaxime Ripard return -EINVAL; 5491fc3b37fSMaxime Ripard } 5501fc3b37fSMaxime Ripard 5511fc3b37fSMaxime Ripard csi2rx->max_streams = (dev_cfg >> 4) & 7; 5521fc3b37fSMaxime Ripard if (csi2rx->max_streams > CSI2RX_STREAMS_MAX) { 5531fc3b37fSMaxime Ripard dev_err(&pdev->dev, "Invalid number of streams: %u\n", 5541fc3b37fSMaxime Ripard csi2rx->max_streams); 5551fc3b37fSMaxime Ripard return -EINVAL; 5561fc3b37fSMaxime Ripard } 5571fc3b37fSMaxime Ripard 5581fc3b37fSMaxime Ripard csi2rx->has_internal_dphy = dev_cfg & BIT(3) ? true : false; 5591fc3b37fSMaxime Ripard 5601fc3b37fSMaxime Ripard /* 5611fc3b37fSMaxime Ripard * FIXME: Once we'll have internal D-PHY support, the check 5621fc3b37fSMaxime Ripard * will need to be removed. 5631fc3b37fSMaxime Ripard */ 5643295cf12SJack Zhu if (!csi2rx->dphy && csi2rx->has_internal_dphy) { 5651fc3b37fSMaxime Ripard dev_err(&pdev->dev, "Internal D-PHY not supported yet\n"); 5661fc3b37fSMaxime Ripard return -EINVAL; 5671fc3b37fSMaxime Ripard } 5681fc3b37fSMaxime Ripard 5691fc3b37fSMaxime Ripard for (i = 0; i < csi2rx->max_streams; i++) { 570e0b9ce38SJack Zhu char name[16]; 5711fc3b37fSMaxime Ripard 572e0b9ce38SJack Zhu snprintf(name, sizeof(name), "pixel_if%u_clk", i); 573e0b9ce38SJack Zhu csi2rx->pixel_clk[i] = devm_clk_get(&pdev->dev, name); 5741fc3b37fSMaxime Ripard if (IS_ERR(csi2rx->pixel_clk[i])) { 575e0b9ce38SJack Zhu dev_err(&pdev->dev, "Couldn't get clock %s\n", name); 5761fc3b37fSMaxime Ripard return PTR_ERR(csi2rx->pixel_clk[i]); 5771fc3b37fSMaxime Ripard } 578e0b9ce38SJack Zhu 579e0b9ce38SJack Zhu snprintf(name, sizeof(name), "pixel_if%u", i); 580e0b9ce38SJack Zhu csi2rx->pixel_rst[i] = 581e0b9ce38SJack Zhu devm_reset_control_get_optional_exclusive(&pdev->dev, 582e0b9ce38SJack Zhu name); 583e0b9ce38SJack Zhu if (IS_ERR(csi2rx->pixel_rst[i])) 584e0b9ce38SJack Zhu return PTR_ERR(csi2rx->pixel_rst[i]); 5851fc3b37fSMaxime Ripard } 5861fc3b37fSMaxime Ripard 5871fc3b37fSMaxime Ripard return 0; 5881fc3b37fSMaxime Ripard } 5891fc3b37fSMaxime Ripard 5901fc3b37fSMaxime Ripard static int csi2rx_parse_dt(struct csi2rx_priv *csi2rx) 5911fc3b37fSMaxime Ripard { 59260359a28SSakari Ailus struct v4l2_fwnode_endpoint v4l2_ep = { .bus_type = 0 }; 593adb2dcd5SSakari Ailus struct v4l2_async_connection *asd; 5941fc3b37fSMaxime Ripard struct fwnode_handle *fwh; 5951fc3b37fSMaxime Ripard struct device_node *ep; 5961fc3b37fSMaxime Ripard int ret; 5971fc3b37fSMaxime Ripard 5981fc3b37fSMaxime Ripard ep = of_graph_get_endpoint_by_regs(csi2rx->dev->of_node, 0, 0); 5991fc3b37fSMaxime Ripard if (!ep) 6001fc3b37fSMaxime Ripard return -EINVAL; 6011fc3b37fSMaxime Ripard 6021fc3b37fSMaxime Ripard fwh = of_fwnode_handle(ep); 6031fc3b37fSMaxime Ripard ret = v4l2_fwnode_endpoint_parse(fwh, &v4l2_ep); 6041fc3b37fSMaxime Ripard if (ret) { 6051fc3b37fSMaxime Ripard dev_err(csi2rx->dev, "Could not parse v4l2 endpoint\n"); 6061fc3b37fSMaxime Ripard of_node_put(ep); 6071fc3b37fSMaxime Ripard return ret; 6081fc3b37fSMaxime Ripard } 6091fc3b37fSMaxime Ripard 6102d95e7edSSakari Ailus if (v4l2_ep.bus_type != V4L2_MBUS_CSI2_DPHY) { 6111fc3b37fSMaxime Ripard dev_err(csi2rx->dev, "Unsupported media bus type: 0x%x\n", 6121fc3b37fSMaxime Ripard v4l2_ep.bus_type); 6131fc3b37fSMaxime Ripard of_node_put(ep); 6141fc3b37fSMaxime Ripard return -EINVAL; 6151fc3b37fSMaxime Ripard } 6161fc3b37fSMaxime Ripard 6171fc3b37fSMaxime Ripard memcpy(csi2rx->lanes, v4l2_ep.bus.mipi_csi2.data_lanes, 6181fc3b37fSMaxime Ripard sizeof(csi2rx->lanes)); 6191fc3b37fSMaxime Ripard csi2rx->num_lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes; 6201fc3b37fSMaxime Ripard if (csi2rx->num_lanes > csi2rx->max_lanes) { 6211fc3b37fSMaxime Ripard dev_err(csi2rx->dev, "Unsupported number of data-lanes: %d\n", 6221fc3b37fSMaxime Ripard csi2rx->num_lanes); 6231fc3b37fSMaxime Ripard of_node_put(ep); 6241fc3b37fSMaxime Ripard return -EINVAL; 6251fc3b37fSMaxime Ripard } 6261fc3b37fSMaxime Ripard 627b8ec754aSSakari Ailus v4l2_async_subdev_nf_init(&csi2rx->notifier, &csi2rx->subdev); 6281fc3b37fSMaxime Ripard 6293c8c1539SSakari Ailus asd = v4l2_async_nf_add_fwnode_remote(&csi2rx->notifier, fwh, 630adb2dcd5SSakari Ailus struct v4l2_async_connection); 63188367b15SEzequiel Garcia of_node_put(ep); 632b2701715SPratyush Yadav if (IS_ERR(asd)) { 633b2701715SPratyush Yadav v4l2_async_nf_cleanup(&csi2rx->notifier); 63488367b15SEzequiel Garcia return PTR_ERR(asd); 635b2701715SPratyush Yadav } 636d079f94cSSteve Longerbeam 6371fc3b37fSMaxime Ripard csi2rx->notifier.ops = &csi2rx_notifier_ops; 6381fc3b37fSMaxime Ripard 639b8ec754aSSakari Ailus ret = v4l2_async_nf_register(&csi2rx->notifier); 640d079f94cSSteve Longerbeam if (ret) 6413c8c1539SSakari Ailus v4l2_async_nf_cleanup(&csi2rx->notifier); 642d079f94cSSteve Longerbeam 643d079f94cSSteve Longerbeam return ret; 6441fc3b37fSMaxime Ripard } 6451fc3b37fSMaxime Ripard 6461fc3b37fSMaxime Ripard static int csi2rx_probe(struct platform_device *pdev) 6471fc3b37fSMaxime Ripard { 6481fc3b37fSMaxime Ripard struct csi2rx_priv *csi2rx; 6491fc3b37fSMaxime Ripard unsigned int i; 6501fc3b37fSMaxime Ripard int ret; 6511fc3b37fSMaxime Ripard 6521fc3b37fSMaxime Ripard csi2rx = kzalloc(sizeof(*csi2rx), GFP_KERNEL); 6531fc3b37fSMaxime Ripard if (!csi2rx) 6541fc3b37fSMaxime Ripard return -ENOMEM; 6551fc3b37fSMaxime Ripard platform_set_drvdata(pdev, csi2rx); 6561fc3b37fSMaxime Ripard csi2rx->dev = &pdev->dev; 6571fc3b37fSMaxime Ripard mutex_init(&csi2rx->lock); 6581fc3b37fSMaxime Ripard 6591fc3b37fSMaxime Ripard ret = csi2rx_get_resources(csi2rx, pdev); 6601fc3b37fSMaxime Ripard if (ret) 6611fc3b37fSMaxime Ripard goto err_free_priv; 6621fc3b37fSMaxime Ripard 6631fc3b37fSMaxime Ripard ret = csi2rx_parse_dt(csi2rx); 6641fc3b37fSMaxime Ripard if (ret) 6651fc3b37fSMaxime Ripard goto err_free_priv; 6661fc3b37fSMaxime Ripard 6671fc3b37fSMaxime Ripard csi2rx->subdev.owner = THIS_MODULE; 6681fc3b37fSMaxime Ripard csi2rx->subdev.dev = &pdev->dev; 6691fc3b37fSMaxime Ripard v4l2_subdev_init(&csi2rx->subdev, &csi2rx_subdev_ops); 6705755be5fSLaurent Pinchart csi2rx->subdev.internal_ops = &csi2rx_internal_ops; 6711fc3b37fSMaxime Ripard v4l2_set_subdevdata(&csi2rx->subdev, &pdev->dev); 6728cdd708fSHans Verkuil snprintf(csi2rx->subdev.name, sizeof(csi2rx->subdev.name), 6738cdd708fSHans Verkuil "%s.%s", KBUILD_MODNAME, dev_name(&pdev->dev)); 6741fc3b37fSMaxime Ripard 6751fc3b37fSMaxime Ripard /* Create our media pads */ 6761fc3b37fSMaxime Ripard csi2rx->subdev.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; 6771fc3b37fSMaxime Ripard csi2rx->pads[CSI2RX_PAD_SINK].flags = MEDIA_PAD_FL_SINK; 6781fc3b37fSMaxime Ripard for (i = CSI2RX_PAD_SOURCE_STREAM0; i < CSI2RX_PAD_MAX; i++) 6791fc3b37fSMaxime Ripard csi2rx->pads[i].flags = MEDIA_PAD_FL_SOURCE; 680c6ed7a39SPratyush Yadav csi2rx->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 681b0f46ad6SPratyush Yadav csi2rx->subdev.entity.ops = &csi2rx_media_ops; 6821fc3b37fSMaxime Ripard 6831fc3b37fSMaxime Ripard ret = media_entity_pads_init(&csi2rx->subdev.entity, CSI2RX_PAD_MAX, 6841fc3b37fSMaxime Ripard csi2rx->pads); 6851fc3b37fSMaxime Ripard if (ret) 686d079f94cSSteve Longerbeam goto err_cleanup; 6871fc3b37fSMaxime Ripard 688dbca7b3cSPratyush Yadav ret = v4l2_subdev_init_finalize(&csi2rx->subdev); 689dbca7b3cSPratyush Yadav if (ret) 690dbca7b3cSPratyush Yadav goto err_cleanup; 691dbca7b3cSPratyush Yadav 6921fc3b37fSMaxime Ripard ret = v4l2_async_register_subdev(&csi2rx->subdev); 6931fc3b37fSMaxime Ripard if (ret < 0) 694dbca7b3cSPratyush Yadav goto err_free_state; 6951fc3b37fSMaxime Ripard 6961fc3b37fSMaxime Ripard dev_info(&pdev->dev, 6971fc3b37fSMaxime Ripard "Probed CSI2RX with %u/%u lanes, %u streams, %s D-PHY\n", 6981fc3b37fSMaxime Ripard csi2rx->num_lanes, csi2rx->max_lanes, csi2rx->max_streams, 6993295cf12SJack Zhu csi2rx->dphy ? "external" : 7001fc3b37fSMaxime Ripard csi2rx->has_internal_dphy ? "internal" : "no"); 7011fc3b37fSMaxime Ripard 7021fc3b37fSMaxime Ripard return 0; 7031fc3b37fSMaxime Ripard 704dbca7b3cSPratyush Yadav err_free_state: 705dbca7b3cSPratyush Yadav v4l2_subdev_cleanup(&csi2rx->subdev); 706d079f94cSSteve Longerbeam err_cleanup: 707b2701715SPratyush Yadav v4l2_async_nf_unregister(&csi2rx->notifier); 7083c8c1539SSakari Ailus v4l2_async_nf_cleanup(&csi2rx->notifier); 709aee5b415SPratyush Yadav media_entity_cleanup(&csi2rx->subdev.entity); 7101fc3b37fSMaxime Ripard err_free_priv: 7111fc3b37fSMaxime Ripard kfree(csi2rx); 7121fc3b37fSMaxime Ripard return ret; 7131fc3b37fSMaxime Ripard } 7141fc3b37fSMaxime Ripard 715bbb3f635SUwe Kleine-König static void csi2rx_remove(struct platform_device *pdev) 7161fc3b37fSMaxime Ripard { 7171fc3b37fSMaxime Ripard struct csi2rx_priv *csi2rx = platform_get_drvdata(pdev); 7181fc3b37fSMaxime Ripard 719b2701715SPratyush Yadav v4l2_async_nf_unregister(&csi2rx->notifier); 720b2701715SPratyush Yadav v4l2_async_nf_cleanup(&csi2rx->notifier); 7211fc3b37fSMaxime Ripard v4l2_async_unregister_subdev(&csi2rx->subdev); 722dbca7b3cSPratyush Yadav v4l2_subdev_cleanup(&csi2rx->subdev); 723aee5b415SPratyush Yadav media_entity_cleanup(&csi2rx->subdev.entity); 7241fc3b37fSMaxime Ripard kfree(csi2rx); 7251fc3b37fSMaxime Ripard } 7261fc3b37fSMaxime Ripard 7271fc3b37fSMaxime Ripard static const struct of_device_id csi2rx_of_table[] = { 72871e8d6e4SJack Zhu { .compatible = "starfive,jh7110-csi2rx" }, 7291fc3b37fSMaxime Ripard { .compatible = "cdns,csi2rx" }, 7301fc3b37fSMaxime Ripard { }, 7311fc3b37fSMaxime Ripard }; 7321fc3b37fSMaxime Ripard MODULE_DEVICE_TABLE(of, csi2rx_of_table); 7331fc3b37fSMaxime Ripard 7341fc3b37fSMaxime Ripard static struct platform_driver csi2rx_driver = { 7351fc3b37fSMaxime Ripard .probe = csi2rx_probe, 736bbb3f635SUwe Kleine-König .remove_new = csi2rx_remove, 7371fc3b37fSMaxime Ripard 7381fc3b37fSMaxime Ripard .driver = { 7391fc3b37fSMaxime Ripard .name = "cdns-csi2rx", 7401fc3b37fSMaxime Ripard .of_match_table = csi2rx_of_table, 7411fc3b37fSMaxime Ripard }, 7421fc3b37fSMaxime Ripard }; 7431fc3b37fSMaxime Ripard module_platform_driver(csi2rx_driver); 7441fc3b37fSMaxime Ripard MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin.com>"); 7451fc3b37fSMaxime Ripard MODULE_DESCRIPTION("Cadence CSI2-RX controller"); 7461fc3b37fSMaxime Ripard MODULE_LICENSE("GPL"); 747