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> 111fc3b37fSMaxime Ripard #include <linux/module.h> 121fc3b37fSMaxime Ripard #include <linux/of.h> 131fc3b37fSMaxime Ripard #include <linux/of_graph.h> 141fc3b37fSMaxime Ripard #include <linux/phy/phy.h> 151fc3b37fSMaxime Ripard #include <linux/platform_device.h> 16e0b9ce38SJack Zhu #include <linux/reset.h> 173c46ab9dSArnd Bergmann #include <linux/slab.h> 181fc3b37fSMaxime Ripard 191fc3b37fSMaxime Ripard #include <media/v4l2-ctrls.h> 201fc3b37fSMaxime Ripard #include <media/v4l2-device.h> 211fc3b37fSMaxime Ripard #include <media/v4l2-fwnode.h> 221fc3b37fSMaxime Ripard #include <media/v4l2-subdev.h> 231fc3b37fSMaxime Ripard 241fc3b37fSMaxime Ripard #define CSI2RX_DEVICE_CFG_REG 0x000 251fc3b37fSMaxime Ripard 261fc3b37fSMaxime Ripard #define CSI2RX_SOFT_RESET_REG 0x004 271fc3b37fSMaxime Ripard #define CSI2RX_SOFT_RESET_PROTOCOL BIT(1) 281fc3b37fSMaxime Ripard #define CSI2RX_SOFT_RESET_FRONT BIT(0) 291fc3b37fSMaxime Ripard 301fc3b37fSMaxime Ripard #define CSI2RX_STATIC_CFG_REG 0x008 311fc3b37fSMaxime Ripard #define CSI2RX_STATIC_CFG_DLANE_MAP(llane, plane) ((plane) << (16 + (llane) * 4)) 321fc3b37fSMaxime Ripard #define CSI2RX_STATIC_CFG_LANES_MASK GENMASK(11, 8) 331fc3b37fSMaxime Ripard 343295cf12SJack Zhu #define CSI2RX_DPHY_LANE_CTRL_REG 0x40 353295cf12SJack Zhu #define CSI2RX_DPHY_CL_RST BIT(16) 363295cf12SJack Zhu #define CSI2RX_DPHY_DL_RST(i) BIT((i) + 12) 373295cf12SJack Zhu #define CSI2RX_DPHY_CL_EN BIT(4) 383295cf12SJack Zhu #define CSI2RX_DPHY_DL_EN(i) BIT(i) 393295cf12SJack Zhu 401fc3b37fSMaxime Ripard #define CSI2RX_STREAM_BASE(n) (((n) + 1) * 0x100) 411fc3b37fSMaxime Ripard 421fc3b37fSMaxime Ripard #define CSI2RX_STREAM_CTRL_REG(n) (CSI2RX_STREAM_BASE(n) + 0x000) 431fc3b37fSMaxime Ripard #define CSI2RX_STREAM_CTRL_START BIT(0) 441fc3b37fSMaxime Ripard 451fc3b37fSMaxime Ripard #define CSI2RX_STREAM_DATA_CFG_REG(n) (CSI2RX_STREAM_BASE(n) + 0x008) 461fc3b37fSMaxime Ripard #define CSI2RX_STREAM_DATA_CFG_EN_VC_SELECT BIT(31) 471fc3b37fSMaxime Ripard #define CSI2RX_STREAM_DATA_CFG_VC_SELECT(n) BIT((n) + 16) 481fc3b37fSMaxime Ripard 491fc3b37fSMaxime Ripard #define CSI2RX_STREAM_CFG_REG(n) (CSI2RX_STREAM_BASE(n) + 0x00c) 501fc3b37fSMaxime Ripard #define CSI2RX_STREAM_CFG_FIFO_MODE_LARGE_BUF (1 << 8) 511fc3b37fSMaxime Ripard 521fc3b37fSMaxime Ripard #define CSI2RX_LANES_MAX 4 531fc3b37fSMaxime Ripard #define CSI2RX_STREAMS_MAX 4 541fc3b37fSMaxime Ripard 551fc3b37fSMaxime Ripard enum csi2rx_pads { 561fc3b37fSMaxime Ripard CSI2RX_PAD_SINK, 571fc3b37fSMaxime Ripard CSI2RX_PAD_SOURCE_STREAM0, 581fc3b37fSMaxime Ripard CSI2RX_PAD_SOURCE_STREAM1, 591fc3b37fSMaxime Ripard CSI2RX_PAD_SOURCE_STREAM2, 601fc3b37fSMaxime Ripard CSI2RX_PAD_SOURCE_STREAM3, 611fc3b37fSMaxime Ripard CSI2RX_PAD_MAX, 621fc3b37fSMaxime Ripard }; 631fc3b37fSMaxime Ripard 641fc3b37fSMaxime Ripard struct csi2rx_priv { 651fc3b37fSMaxime Ripard struct device *dev; 661fc3b37fSMaxime Ripard unsigned int count; 671fc3b37fSMaxime Ripard 681fc3b37fSMaxime Ripard /* 691fc3b37fSMaxime Ripard * Used to prevent race conditions between multiple, 701fc3b37fSMaxime Ripard * concurrent calls to start and stop. 711fc3b37fSMaxime Ripard */ 721fc3b37fSMaxime Ripard struct mutex lock; 731fc3b37fSMaxime Ripard 741fc3b37fSMaxime Ripard void __iomem *base; 751fc3b37fSMaxime Ripard struct clk *sys_clk; 761fc3b37fSMaxime Ripard struct clk *p_clk; 771fc3b37fSMaxime Ripard struct clk *pixel_clk[CSI2RX_STREAMS_MAX]; 78e0b9ce38SJack Zhu struct reset_control *sys_rst; 79e0b9ce38SJack Zhu struct reset_control *p_rst; 80e0b9ce38SJack Zhu struct reset_control *pixel_rst[CSI2RX_STREAMS_MAX]; 811fc3b37fSMaxime Ripard struct phy *dphy; 821fc3b37fSMaxime Ripard 831fc3b37fSMaxime Ripard u8 lanes[CSI2RX_LANES_MAX]; 841fc3b37fSMaxime Ripard u8 num_lanes; 851fc3b37fSMaxime Ripard u8 max_lanes; 861fc3b37fSMaxime Ripard u8 max_streams; 871fc3b37fSMaxime Ripard bool has_internal_dphy; 881fc3b37fSMaxime Ripard 891fc3b37fSMaxime Ripard struct v4l2_subdev subdev; 901fc3b37fSMaxime Ripard struct v4l2_async_notifier notifier; 911fc3b37fSMaxime Ripard struct media_pad pads[CSI2RX_PAD_MAX]; 921fc3b37fSMaxime Ripard 931fc3b37fSMaxime Ripard /* Remote source */ 941fc3b37fSMaxime Ripard struct v4l2_subdev *source_subdev; 951fc3b37fSMaxime Ripard int source_pad; 961fc3b37fSMaxime Ripard }; 971fc3b37fSMaxime Ripard 981fc3b37fSMaxime Ripard static inline 991fc3b37fSMaxime Ripard struct csi2rx_priv *v4l2_subdev_to_csi2rx(struct v4l2_subdev *subdev) 1001fc3b37fSMaxime Ripard { 1011fc3b37fSMaxime Ripard return container_of(subdev, struct csi2rx_priv, subdev); 1021fc3b37fSMaxime Ripard } 1031fc3b37fSMaxime Ripard 1041fc3b37fSMaxime Ripard static void csi2rx_reset(struct csi2rx_priv *csi2rx) 1051fc3b37fSMaxime Ripard { 1061fc3b37fSMaxime Ripard writel(CSI2RX_SOFT_RESET_PROTOCOL | CSI2RX_SOFT_RESET_FRONT, 1071fc3b37fSMaxime Ripard csi2rx->base + CSI2RX_SOFT_RESET_REG); 1081fc3b37fSMaxime Ripard 1091fc3b37fSMaxime Ripard udelay(10); 1101fc3b37fSMaxime Ripard 1111fc3b37fSMaxime Ripard writel(0, csi2rx->base + CSI2RX_SOFT_RESET_REG); 1121fc3b37fSMaxime Ripard } 1131fc3b37fSMaxime Ripard 1143295cf12SJack Zhu static int csi2rx_configure_ext_dphy(struct csi2rx_priv *csi2rx) 1153295cf12SJack Zhu { 1163295cf12SJack Zhu union phy_configure_opts opts = { }; 1173295cf12SJack Zhu int ret; 1183295cf12SJack Zhu 1193295cf12SJack Zhu ret = phy_power_on(csi2rx->dphy); 1203295cf12SJack Zhu if (ret) 1213295cf12SJack Zhu return ret; 1223295cf12SJack Zhu 1233295cf12SJack Zhu ret = phy_configure(csi2rx->dphy, &opts); 1243295cf12SJack Zhu if (ret) { 1253295cf12SJack Zhu phy_power_off(csi2rx->dphy); 1263295cf12SJack Zhu return ret; 1273295cf12SJack Zhu } 1283295cf12SJack Zhu 1293295cf12SJack Zhu return 0; 1303295cf12SJack Zhu } 1313295cf12SJack Zhu 1321fc3b37fSMaxime Ripard static int csi2rx_start(struct csi2rx_priv *csi2rx) 1331fc3b37fSMaxime Ripard { 1341fc3b37fSMaxime Ripard unsigned int i; 1351fc3b37fSMaxime Ripard unsigned long lanes_used = 0; 1361fc3b37fSMaxime Ripard u32 reg; 1371fc3b37fSMaxime Ripard int ret; 1381fc3b37fSMaxime Ripard 1391fc3b37fSMaxime Ripard ret = clk_prepare_enable(csi2rx->p_clk); 1401fc3b37fSMaxime Ripard if (ret) 1411fc3b37fSMaxime Ripard return ret; 1421fc3b37fSMaxime Ripard 143e0b9ce38SJack Zhu reset_control_deassert(csi2rx->p_rst); 1441fc3b37fSMaxime Ripard csi2rx_reset(csi2rx); 1451fc3b37fSMaxime Ripard 1461fc3b37fSMaxime Ripard reg = csi2rx->num_lanes << 8; 1471fc3b37fSMaxime Ripard for (i = 0; i < csi2rx->num_lanes; i++) { 1481fc3b37fSMaxime Ripard reg |= CSI2RX_STATIC_CFG_DLANE_MAP(i, csi2rx->lanes[i]); 1491fc3b37fSMaxime Ripard set_bit(csi2rx->lanes[i], &lanes_used); 1501fc3b37fSMaxime Ripard } 1511fc3b37fSMaxime Ripard 1521fc3b37fSMaxime Ripard /* 1531fc3b37fSMaxime Ripard * Even the unused lanes need to be mapped. In order to avoid 1541fc3b37fSMaxime Ripard * to map twice to the same physical lane, keep the lanes used 1551fc3b37fSMaxime Ripard * in the previous loop, and only map unused physical lanes to 1561fc3b37fSMaxime Ripard * the rest of our logical lanes. 1571fc3b37fSMaxime Ripard */ 1581fc3b37fSMaxime Ripard for (i = csi2rx->num_lanes; i < csi2rx->max_lanes; i++) { 1591fc3b37fSMaxime Ripard unsigned int idx = find_first_zero_bit(&lanes_used, 1602eca8e4cSChristophe JAILLET csi2rx->max_lanes); 1611fc3b37fSMaxime Ripard set_bit(idx, &lanes_used); 1621fc3b37fSMaxime Ripard reg |= CSI2RX_STATIC_CFG_DLANE_MAP(i, i + 1); 1631fc3b37fSMaxime Ripard } 1641fc3b37fSMaxime Ripard 1651fc3b37fSMaxime Ripard writel(reg, csi2rx->base + CSI2RX_STATIC_CFG_REG); 1661fc3b37fSMaxime Ripard 1671fc3b37fSMaxime Ripard ret = v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, true); 1681fc3b37fSMaxime Ripard if (ret) 1691fc3b37fSMaxime Ripard goto err_disable_pclk; 1701fc3b37fSMaxime Ripard 1713295cf12SJack Zhu /* Enable DPHY clk and data lanes. */ 1723295cf12SJack Zhu if (csi2rx->dphy) { 1733295cf12SJack Zhu reg = CSI2RX_DPHY_CL_EN | CSI2RX_DPHY_CL_RST; 1743295cf12SJack Zhu for (i = 0; i < csi2rx->num_lanes; i++) { 1753295cf12SJack Zhu reg |= CSI2RX_DPHY_DL_EN(csi2rx->lanes[i] - 1); 1763295cf12SJack Zhu reg |= CSI2RX_DPHY_DL_RST(csi2rx->lanes[i] - 1); 1773295cf12SJack Zhu } 1783295cf12SJack Zhu 1793295cf12SJack Zhu writel(reg, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG); 1803295cf12SJack Zhu } 1813295cf12SJack Zhu 1821fc3b37fSMaxime Ripard /* 1831fc3b37fSMaxime Ripard * Create a static mapping between the CSI virtual channels 1841fc3b37fSMaxime Ripard * and the output stream. 1851fc3b37fSMaxime Ripard * 1861fc3b37fSMaxime Ripard * This should be enhanced, but v4l2 lacks the support for 1871fc3b37fSMaxime Ripard * changing that mapping dynamically. 1881fc3b37fSMaxime Ripard * 1891fc3b37fSMaxime Ripard * We also cannot enable and disable independent streams here, 1901fc3b37fSMaxime Ripard * hence the reference counting. 1911fc3b37fSMaxime Ripard */ 1921fc3b37fSMaxime Ripard for (i = 0; i < csi2rx->max_streams; i++) { 1931fc3b37fSMaxime Ripard ret = clk_prepare_enable(csi2rx->pixel_clk[i]); 1941fc3b37fSMaxime Ripard if (ret) 1951fc3b37fSMaxime Ripard goto err_disable_pixclk; 1961fc3b37fSMaxime Ripard 197e0b9ce38SJack Zhu reset_control_deassert(csi2rx->pixel_rst[i]); 198e0b9ce38SJack Zhu 1991fc3b37fSMaxime Ripard writel(CSI2RX_STREAM_CFG_FIFO_MODE_LARGE_BUF, 2001fc3b37fSMaxime Ripard csi2rx->base + CSI2RX_STREAM_CFG_REG(i)); 2011fc3b37fSMaxime Ripard 2021fc3b37fSMaxime Ripard writel(CSI2RX_STREAM_DATA_CFG_EN_VC_SELECT | 2031fc3b37fSMaxime Ripard CSI2RX_STREAM_DATA_CFG_VC_SELECT(i), 2041fc3b37fSMaxime Ripard csi2rx->base + CSI2RX_STREAM_DATA_CFG_REG(i)); 2051fc3b37fSMaxime Ripard 2061fc3b37fSMaxime Ripard writel(CSI2RX_STREAM_CTRL_START, 2071fc3b37fSMaxime Ripard csi2rx->base + CSI2RX_STREAM_CTRL_REG(i)); 2081fc3b37fSMaxime Ripard } 2091fc3b37fSMaxime Ripard 2101fc3b37fSMaxime Ripard ret = clk_prepare_enable(csi2rx->sys_clk); 2111fc3b37fSMaxime Ripard if (ret) 2121fc3b37fSMaxime Ripard goto err_disable_pixclk; 2131fc3b37fSMaxime Ripard 214e0b9ce38SJack Zhu reset_control_deassert(csi2rx->sys_rst); 2153295cf12SJack Zhu 2163295cf12SJack Zhu if (csi2rx->dphy) { 2173295cf12SJack Zhu ret = csi2rx_configure_ext_dphy(csi2rx); 2183295cf12SJack Zhu if (ret) { 2193295cf12SJack Zhu dev_err(csi2rx->dev, 2203295cf12SJack Zhu "Failed to configure external DPHY: %d\n", ret); 2213295cf12SJack Zhu goto err_disable_sysclk; 2223295cf12SJack Zhu } 2233295cf12SJack Zhu } 2243295cf12SJack Zhu 2251fc3b37fSMaxime Ripard clk_disable_unprepare(csi2rx->p_clk); 2261fc3b37fSMaxime Ripard 2271fc3b37fSMaxime Ripard return 0; 2281fc3b37fSMaxime Ripard 2293295cf12SJack Zhu err_disable_sysclk: 2303295cf12SJack Zhu clk_disable_unprepare(csi2rx->sys_clk); 2311fc3b37fSMaxime Ripard err_disable_pixclk: 232e0b9ce38SJack Zhu for (; i > 0; i--) { 233e0b9ce38SJack Zhu reset_control_assert(csi2rx->pixel_rst[i - 1]); 23428d42d2fSSakari Ailus clk_disable_unprepare(csi2rx->pixel_clk[i - 1]); 235e0b9ce38SJack Zhu } 2361fc3b37fSMaxime Ripard 2371fc3b37fSMaxime Ripard err_disable_pclk: 2381fc3b37fSMaxime Ripard clk_disable_unprepare(csi2rx->p_clk); 2391fc3b37fSMaxime Ripard 2401fc3b37fSMaxime Ripard return ret; 2411fc3b37fSMaxime Ripard } 2421fc3b37fSMaxime Ripard 2431fc3b37fSMaxime Ripard static void csi2rx_stop(struct csi2rx_priv *csi2rx) 2441fc3b37fSMaxime Ripard { 2451fc3b37fSMaxime Ripard unsigned int i; 2461fc3b37fSMaxime Ripard 2471fc3b37fSMaxime Ripard clk_prepare_enable(csi2rx->p_clk); 248e0b9ce38SJack Zhu reset_control_assert(csi2rx->sys_rst); 2491fc3b37fSMaxime Ripard clk_disable_unprepare(csi2rx->sys_clk); 2501fc3b37fSMaxime Ripard 2511fc3b37fSMaxime Ripard for (i = 0; i < csi2rx->max_streams; i++) { 2521fc3b37fSMaxime Ripard writel(0, csi2rx->base + CSI2RX_STREAM_CTRL_REG(i)); 2531fc3b37fSMaxime Ripard 254e0b9ce38SJack Zhu reset_control_assert(csi2rx->pixel_rst[i]); 2551fc3b37fSMaxime Ripard clk_disable_unprepare(csi2rx->pixel_clk[i]); 2561fc3b37fSMaxime Ripard } 2571fc3b37fSMaxime Ripard 258e0b9ce38SJack Zhu reset_control_assert(csi2rx->p_rst); 2591fc3b37fSMaxime Ripard clk_disable_unprepare(csi2rx->p_clk); 2601fc3b37fSMaxime Ripard 2611fc3b37fSMaxime Ripard if (v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, false)) 2621fc3b37fSMaxime Ripard dev_warn(csi2rx->dev, "Couldn't disable our subdev\n"); 2633295cf12SJack Zhu 2643295cf12SJack Zhu if (csi2rx->dphy) { 2653295cf12SJack Zhu writel(0, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG); 2663295cf12SJack Zhu 2673295cf12SJack Zhu if (phy_power_off(csi2rx->dphy)) 2683295cf12SJack Zhu dev_warn(csi2rx->dev, "Couldn't power off DPHY\n"); 2693295cf12SJack Zhu } 2701fc3b37fSMaxime Ripard } 2711fc3b37fSMaxime Ripard 2721fc3b37fSMaxime Ripard static int csi2rx_s_stream(struct v4l2_subdev *subdev, int enable) 2731fc3b37fSMaxime Ripard { 2741fc3b37fSMaxime Ripard struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev); 2751fc3b37fSMaxime Ripard int ret = 0; 2761fc3b37fSMaxime Ripard 2771fc3b37fSMaxime Ripard mutex_lock(&csi2rx->lock); 2781fc3b37fSMaxime Ripard 2791fc3b37fSMaxime Ripard if (enable) { 2801fc3b37fSMaxime Ripard /* 2811fc3b37fSMaxime Ripard * If we're not the first users, there's no need to 2821fc3b37fSMaxime Ripard * enable the whole controller. 2831fc3b37fSMaxime Ripard */ 2841fc3b37fSMaxime Ripard if (!csi2rx->count) { 2851fc3b37fSMaxime Ripard ret = csi2rx_start(csi2rx); 2861fc3b37fSMaxime Ripard if (ret) 2871fc3b37fSMaxime Ripard goto out; 2881fc3b37fSMaxime Ripard } 2891fc3b37fSMaxime Ripard 2901fc3b37fSMaxime Ripard csi2rx->count++; 2911fc3b37fSMaxime Ripard } else { 2921fc3b37fSMaxime Ripard csi2rx->count--; 2931fc3b37fSMaxime Ripard 2941fc3b37fSMaxime Ripard /* 2951fc3b37fSMaxime Ripard * Let the last user turn off the lights. 2961fc3b37fSMaxime Ripard */ 2971fc3b37fSMaxime Ripard if (!csi2rx->count) 2981fc3b37fSMaxime Ripard csi2rx_stop(csi2rx); 2991fc3b37fSMaxime Ripard } 3001fc3b37fSMaxime Ripard 3011fc3b37fSMaxime Ripard out: 3021fc3b37fSMaxime Ripard mutex_unlock(&csi2rx->lock); 3031fc3b37fSMaxime Ripard return ret; 3041fc3b37fSMaxime Ripard } 3051fc3b37fSMaxime Ripard 3061fc3b37fSMaxime Ripard static const struct v4l2_subdev_video_ops csi2rx_video_ops = { 3071fc3b37fSMaxime Ripard .s_stream = csi2rx_s_stream, 3081fc3b37fSMaxime Ripard }; 3091fc3b37fSMaxime Ripard 3101fc3b37fSMaxime Ripard static const struct v4l2_subdev_ops csi2rx_subdev_ops = { 3111fc3b37fSMaxime Ripard .video = &csi2rx_video_ops, 3121fc3b37fSMaxime Ripard }; 3131fc3b37fSMaxime Ripard 3141fc3b37fSMaxime Ripard static int csi2rx_async_bound(struct v4l2_async_notifier *notifier, 3151fc3b37fSMaxime Ripard struct v4l2_subdev *s_subdev, 316*adb2dcd5SSakari Ailus struct v4l2_async_connection *asd) 3171fc3b37fSMaxime Ripard { 3181fc3b37fSMaxime Ripard struct v4l2_subdev *subdev = notifier->sd; 3191fc3b37fSMaxime Ripard struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev); 3201fc3b37fSMaxime Ripard 3211fc3b37fSMaxime Ripard csi2rx->source_pad = media_entity_get_fwnode_pad(&s_subdev->entity, 3221fc3b37fSMaxime Ripard s_subdev->fwnode, 3231fc3b37fSMaxime Ripard MEDIA_PAD_FL_SOURCE); 3241fc3b37fSMaxime Ripard if (csi2rx->source_pad < 0) { 3251fc3b37fSMaxime Ripard dev_err(csi2rx->dev, "Couldn't find output pad for subdev %s\n", 3261fc3b37fSMaxime Ripard s_subdev->name); 3271fc3b37fSMaxime Ripard return csi2rx->source_pad; 3281fc3b37fSMaxime Ripard } 3291fc3b37fSMaxime Ripard 3301fc3b37fSMaxime Ripard csi2rx->source_subdev = s_subdev; 3311fc3b37fSMaxime Ripard 3321fc3b37fSMaxime Ripard dev_dbg(csi2rx->dev, "Bound %s pad: %d\n", s_subdev->name, 3331fc3b37fSMaxime Ripard csi2rx->source_pad); 3341fc3b37fSMaxime Ripard 3351fc3b37fSMaxime Ripard return media_create_pad_link(&csi2rx->source_subdev->entity, 3361fc3b37fSMaxime Ripard csi2rx->source_pad, 3371fc3b37fSMaxime Ripard &csi2rx->subdev.entity, 0, 3381fc3b37fSMaxime Ripard MEDIA_LNK_FL_ENABLED | 3391fc3b37fSMaxime Ripard MEDIA_LNK_FL_IMMUTABLE); 3401fc3b37fSMaxime Ripard } 3411fc3b37fSMaxime Ripard 3421fc3b37fSMaxime Ripard static const struct v4l2_async_notifier_operations csi2rx_notifier_ops = { 3431fc3b37fSMaxime Ripard .bound = csi2rx_async_bound, 3441fc3b37fSMaxime Ripard }; 3451fc3b37fSMaxime Ripard 3461fc3b37fSMaxime Ripard static int csi2rx_get_resources(struct csi2rx_priv *csi2rx, 3471fc3b37fSMaxime Ripard struct platform_device *pdev) 3481fc3b37fSMaxime Ripard { 3491fc3b37fSMaxime Ripard unsigned char i; 3501fc3b37fSMaxime Ripard u32 dev_cfg; 351cca65f64SEvgeny Novikov int ret; 3521fc3b37fSMaxime Ripard 353f5aae241SCai Huoqing csi2rx->base = devm_platform_ioremap_resource(pdev, 0); 3541fc3b37fSMaxime Ripard if (IS_ERR(csi2rx->base)) 3551fc3b37fSMaxime Ripard return PTR_ERR(csi2rx->base); 3561fc3b37fSMaxime Ripard 3571fc3b37fSMaxime Ripard csi2rx->sys_clk = devm_clk_get(&pdev->dev, "sys_clk"); 3581fc3b37fSMaxime Ripard if (IS_ERR(csi2rx->sys_clk)) { 3591fc3b37fSMaxime Ripard dev_err(&pdev->dev, "Couldn't get sys clock\n"); 3601fc3b37fSMaxime Ripard return PTR_ERR(csi2rx->sys_clk); 3611fc3b37fSMaxime Ripard } 3621fc3b37fSMaxime Ripard 3631fc3b37fSMaxime Ripard csi2rx->p_clk = devm_clk_get(&pdev->dev, "p_clk"); 3641fc3b37fSMaxime Ripard if (IS_ERR(csi2rx->p_clk)) { 3651fc3b37fSMaxime Ripard dev_err(&pdev->dev, "Couldn't get P clock\n"); 3661fc3b37fSMaxime Ripard return PTR_ERR(csi2rx->p_clk); 3671fc3b37fSMaxime Ripard } 3681fc3b37fSMaxime Ripard 369e0b9ce38SJack Zhu csi2rx->sys_rst = devm_reset_control_get_optional_exclusive(&pdev->dev, 370e0b9ce38SJack Zhu "sys"); 371e0b9ce38SJack Zhu if (IS_ERR(csi2rx->sys_rst)) 372e0b9ce38SJack Zhu return PTR_ERR(csi2rx->sys_rst); 373e0b9ce38SJack Zhu 374e0b9ce38SJack Zhu csi2rx->p_rst = devm_reset_control_get_optional_exclusive(&pdev->dev, 375e0b9ce38SJack Zhu "reg_bank"); 376e0b9ce38SJack Zhu if (IS_ERR(csi2rx->p_rst)) 377e0b9ce38SJack Zhu return PTR_ERR(csi2rx->p_rst); 378e0b9ce38SJack Zhu 3791fc3b37fSMaxime Ripard csi2rx->dphy = devm_phy_optional_get(&pdev->dev, "dphy"); 3801fc3b37fSMaxime Ripard if (IS_ERR(csi2rx->dphy)) { 3811fc3b37fSMaxime Ripard dev_err(&pdev->dev, "Couldn't get external D-PHY\n"); 3821fc3b37fSMaxime Ripard return PTR_ERR(csi2rx->dphy); 3831fc3b37fSMaxime Ripard } 3841fc3b37fSMaxime Ripard 385cca65f64SEvgeny Novikov ret = clk_prepare_enable(csi2rx->p_clk); 386cca65f64SEvgeny Novikov if (ret) { 387cca65f64SEvgeny Novikov dev_err(&pdev->dev, "Couldn't prepare and enable P clock\n"); 388cca65f64SEvgeny Novikov return ret; 389cca65f64SEvgeny Novikov } 390cca65f64SEvgeny Novikov 3911fc3b37fSMaxime Ripard dev_cfg = readl(csi2rx->base + CSI2RX_DEVICE_CFG_REG); 3921fc3b37fSMaxime Ripard clk_disable_unprepare(csi2rx->p_clk); 3931fc3b37fSMaxime Ripard 3941fc3b37fSMaxime Ripard csi2rx->max_lanes = dev_cfg & 7; 3951fc3b37fSMaxime Ripard if (csi2rx->max_lanes > CSI2RX_LANES_MAX) { 3961fc3b37fSMaxime Ripard dev_err(&pdev->dev, "Invalid number of lanes: %u\n", 3971fc3b37fSMaxime Ripard csi2rx->max_lanes); 3981fc3b37fSMaxime Ripard return -EINVAL; 3991fc3b37fSMaxime Ripard } 4001fc3b37fSMaxime Ripard 4011fc3b37fSMaxime Ripard csi2rx->max_streams = (dev_cfg >> 4) & 7; 4021fc3b37fSMaxime Ripard if (csi2rx->max_streams > CSI2RX_STREAMS_MAX) { 4031fc3b37fSMaxime Ripard dev_err(&pdev->dev, "Invalid number of streams: %u\n", 4041fc3b37fSMaxime Ripard csi2rx->max_streams); 4051fc3b37fSMaxime Ripard return -EINVAL; 4061fc3b37fSMaxime Ripard } 4071fc3b37fSMaxime Ripard 4081fc3b37fSMaxime Ripard csi2rx->has_internal_dphy = dev_cfg & BIT(3) ? true : false; 4091fc3b37fSMaxime Ripard 4101fc3b37fSMaxime Ripard /* 4111fc3b37fSMaxime Ripard * FIXME: Once we'll have internal D-PHY support, the check 4121fc3b37fSMaxime Ripard * will need to be removed. 4131fc3b37fSMaxime Ripard */ 4143295cf12SJack Zhu if (!csi2rx->dphy && csi2rx->has_internal_dphy) { 4151fc3b37fSMaxime Ripard dev_err(&pdev->dev, "Internal D-PHY not supported yet\n"); 4161fc3b37fSMaxime Ripard return -EINVAL; 4171fc3b37fSMaxime Ripard } 4181fc3b37fSMaxime Ripard 4191fc3b37fSMaxime Ripard for (i = 0; i < csi2rx->max_streams; i++) { 420e0b9ce38SJack Zhu char name[16]; 4211fc3b37fSMaxime Ripard 422e0b9ce38SJack Zhu snprintf(name, sizeof(name), "pixel_if%u_clk", i); 423e0b9ce38SJack Zhu csi2rx->pixel_clk[i] = devm_clk_get(&pdev->dev, name); 4241fc3b37fSMaxime Ripard if (IS_ERR(csi2rx->pixel_clk[i])) { 425e0b9ce38SJack Zhu dev_err(&pdev->dev, "Couldn't get clock %s\n", name); 4261fc3b37fSMaxime Ripard return PTR_ERR(csi2rx->pixel_clk[i]); 4271fc3b37fSMaxime Ripard } 428e0b9ce38SJack Zhu 429e0b9ce38SJack Zhu snprintf(name, sizeof(name), "pixel_if%u", i); 430e0b9ce38SJack Zhu csi2rx->pixel_rst[i] = 431e0b9ce38SJack Zhu devm_reset_control_get_optional_exclusive(&pdev->dev, 432e0b9ce38SJack Zhu name); 433e0b9ce38SJack Zhu if (IS_ERR(csi2rx->pixel_rst[i])) 434e0b9ce38SJack Zhu return PTR_ERR(csi2rx->pixel_rst[i]); 4351fc3b37fSMaxime Ripard } 4361fc3b37fSMaxime Ripard 4371fc3b37fSMaxime Ripard return 0; 4381fc3b37fSMaxime Ripard } 4391fc3b37fSMaxime Ripard 4401fc3b37fSMaxime Ripard static int csi2rx_parse_dt(struct csi2rx_priv *csi2rx) 4411fc3b37fSMaxime Ripard { 44260359a28SSakari Ailus struct v4l2_fwnode_endpoint v4l2_ep = { .bus_type = 0 }; 443*adb2dcd5SSakari Ailus struct v4l2_async_connection *asd; 4441fc3b37fSMaxime Ripard struct fwnode_handle *fwh; 4451fc3b37fSMaxime Ripard struct device_node *ep; 4461fc3b37fSMaxime Ripard int ret; 4471fc3b37fSMaxime Ripard 4481fc3b37fSMaxime Ripard ep = of_graph_get_endpoint_by_regs(csi2rx->dev->of_node, 0, 0); 4491fc3b37fSMaxime Ripard if (!ep) 4501fc3b37fSMaxime Ripard return -EINVAL; 4511fc3b37fSMaxime Ripard 4521fc3b37fSMaxime Ripard fwh = of_fwnode_handle(ep); 4531fc3b37fSMaxime Ripard ret = v4l2_fwnode_endpoint_parse(fwh, &v4l2_ep); 4541fc3b37fSMaxime Ripard if (ret) { 4551fc3b37fSMaxime Ripard dev_err(csi2rx->dev, "Could not parse v4l2 endpoint\n"); 4561fc3b37fSMaxime Ripard of_node_put(ep); 4571fc3b37fSMaxime Ripard return ret; 4581fc3b37fSMaxime Ripard } 4591fc3b37fSMaxime Ripard 4602d95e7edSSakari Ailus if (v4l2_ep.bus_type != V4L2_MBUS_CSI2_DPHY) { 4611fc3b37fSMaxime Ripard dev_err(csi2rx->dev, "Unsupported media bus type: 0x%x\n", 4621fc3b37fSMaxime Ripard v4l2_ep.bus_type); 4631fc3b37fSMaxime Ripard of_node_put(ep); 4641fc3b37fSMaxime Ripard return -EINVAL; 4651fc3b37fSMaxime Ripard } 4661fc3b37fSMaxime Ripard 4671fc3b37fSMaxime Ripard memcpy(csi2rx->lanes, v4l2_ep.bus.mipi_csi2.data_lanes, 4681fc3b37fSMaxime Ripard sizeof(csi2rx->lanes)); 4691fc3b37fSMaxime Ripard csi2rx->num_lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes; 4701fc3b37fSMaxime Ripard if (csi2rx->num_lanes > csi2rx->max_lanes) { 4711fc3b37fSMaxime Ripard dev_err(csi2rx->dev, "Unsupported number of data-lanes: %d\n", 4721fc3b37fSMaxime Ripard csi2rx->num_lanes); 4731fc3b37fSMaxime Ripard of_node_put(ep); 4741fc3b37fSMaxime Ripard return -EINVAL; 4751fc3b37fSMaxime Ripard } 4761fc3b37fSMaxime Ripard 4773c8c1539SSakari Ailus v4l2_async_nf_init(&csi2rx->notifier); 4781fc3b37fSMaxime Ripard 4793c8c1539SSakari Ailus asd = v4l2_async_nf_add_fwnode_remote(&csi2rx->notifier, fwh, 480*adb2dcd5SSakari Ailus struct v4l2_async_connection); 48188367b15SEzequiel Garcia of_node_put(ep); 48288367b15SEzequiel Garcia if (IS_ERR(asd)) 48388367b15SEzequiel Garcia return PTR_ERR(asd); 484d079f94cSSteve Longerbeam 4851fc3b37fSMaxime Ripard csi2rx->notifier.ops = &csi2rx_notifier_ops; 4861fc3b37fSMaxime Ripard 4873c8c1539SSakari Ailus ret = v4l2_async_subdev_nf_register(&csi2rx->subdev, &csi2rx->notifier); 488d079f94cSSteve Longerbeam if (ret) 4893c8c1539SSakari Ailus v4l2_async_nf_cleanup(&csi2rx->notifier); 490d079f94cSSteve Longerbeam 491d079f94cSSteve Longerbeam return ret; 4921fc3b37fSMaxime Ripard } 4931fc3b37fSMaxime Ripard 4941fc3b37fSMaxime Ripard static int csi2rx_probe(struct platform_device *pdev) 4951fc3b37fSMaxime Ripard { 4961fc3b37fSMaxime Ripard struct csi2rx_priv *csi2rx; 4971fc3b37fSMaxime Ripard unsigned int i; 4981fc3b37fSMaxime Ripard int ret; 4991fc3b37fSMaxime Ripard 5001fc3b37fSMaxime Ripard csi2rx = kzalloc(sizeof(*csi2rx), GFP_KERNEL); 5011fc3b37fSMaxime Ripard if (!csi2rx) 5021fc3b37fSMaxime Ripard return -ENOMEM; 5031fc3b37fSMaxime Ripard platform_set_drvdata(pdev, csi2rx); 5041fc3b37fSMaxime Ripard csi2rx->dev = &pdev->dev; 5051fc3b37fSMaxime Ripard mutex_init(&csi2rx->lock); 5061fc3b37fSMaxime Ripard 5071fc3b37fSMaxime Ripard ret = csi2rx_get_resources(csi2rx, pdev); 5081fc3b37fSMaxime Ripard if (ret) 5091fc3b37fSMaxime Ripard goto err_free_priv; 5101fc3b37fSMaxime Ripard 5111fc3b37fSMaxime Ripard ret = csi2rx_parse_dt(csi2rx); 5121fc3b37fSMaxime Ripard if (ret) 5131fc3b37fSMaxime Ripard goto err_free_priv; 5141fc3b37fSMaxime Ripard 5151fc3b37fSMaxime Ripard csi2rx->subdev.owner = THIS_MODULE; 5161fc3b37fSMaxime Ripard csi2rx->subdev.dev = &pdev->dev; 5171fc3b37fSMaxime Ripard v4l2_subdev_init(&csi2rx->subdev, &csi2rx_subdev_ops); 5181fc3b37fSMaxime Ripard v4l2_set_subdevdata(&csi2rx->subdev, &pdev->dev); 5191fc3b37fSMaxime Ripard snprintf(csi2rx->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s.%s", 5201fc3b37fSMaxime Ripard KBUILD_MODNAME, dev_name(&pdev->dev)); 5211fc3b37fSMaxime Ripard 5221fc3b37fSMaxime Ripard /* Create our media pads */ 5231fc3b37fSMaxime Ripard csi2rx->subdev.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; 5241fc3b37fSMaxime Ripard csi2rx->pads[CSI2RX_PAD_SINK].flags = MEDIA_PAD_FL_SINK; 5251fc3b37fSMaxime Ripard for (i = CSI2RX_PAD_SOURCE_STREAM0; i < CSI2RX_PAD_MAX; i++) 5261fc3b37fSMaxime Ripard csi2rx->pads[i].flags = MEDIA_PAD_FL_SOURCE; 5271fc3b37fSMaxime Ripard 5281fc3b37fSMaxime Ripard ret = media_entity_pads_init(&csi2rx->subdev.entity, CSI2RX_PAD_MAX, 5291fc3b37fSMaxime Ripard csi2rx->pads); 5301fc3b37fSMaxime Ripard if (ret) 531d079f94cSSteve Longerbeam goto err_cleanup; 5321fc3b37fSMaxime Ripard 5331fc3b37fSMaxime Ripard ret = v4l2_async_register_subdev(&csi2rx->subdev); 5341fc3b37fSMaxime Ripard if (ret < 0) 535d079f94cSSteve Longerbeam goto err_cleanup; 5361fc3b37fSMaxime Ripard 5371fc3b37fSMaxime Ripard dev_info(&pdev->dev, 5381fc3b37fSMaxime Ripard "Probed CSI2RX with %u/%u lanes, %u streams, %s D-PHY\n", 5391fc3b37fSMaxime Ripard csi2rx->num_lanes, csi2rx->max_lanes, csi2rx->max_streams, 5403295cf12SJack Zhu csi2rx->dphy ? "external" : 5411fc3b37fSMaxime Ripard csi2rx->has_internal_dphy ? "internal" : "no"); 5421fc3b37fSMaxime Ripard 5431fc3b37fSMaxime Ripard return 0; 5441fc3b37fSMaxime Ripard 545d079f94cSSteve Longerbeam err_cleanup: 5463c8c1539SSakari Ailus v4l2_async_nf_cleanup(&csi2rx->notifier); 5471fc3b37fSMaxime Ripard err_free_priv: 5481fc3b37fSMaxime Ripard kfree(csi2rx); 5491fc3b37fSMaxime Ripard return ret; 5501fc3b37fSMaxime Ripard } 5511fc3b37fSMaxime Ripard 552bbb3f635SUwe Kleine-König static void csi2rx_remove(struct platform_device *pdev) 5531fc3b37fSMaxime Ripard { 5541fc3b37fSMaxime Ripard struct csi2rx_priv *csi2rx = platform_get_drvdata(pdev); 5551fc3b37fSMaxime Ripard 5561fc3b37fSMaxime Ripard v4l2_async_unregister_subdev(&csi2rx->subdev); 5571fc3b37fSMaxime Ripard kfree(csi2rx); 5581fc3b37fSMaxime Ripard } 5591fc3b37fSMaxime Ripard 5601fc3b37fSMaxime Ripard static const struct of_device_id csi2rx_of_table[] = { 56171e8d6e4SJack Zhu { .compatible = "starfive,jh7110-csi2rx" }, 5621fc3b37fSMaxime Ripard { .compatible = "cdns,csi2rx" }, 5631fc3b37fSMaxime Ripard { }, 5641fc3b37fSMaxime Ripard }; 5651fc3b37fSMaxime Ripard MODULE_DEVICE_TABLE(of, csi2rx_of_table); 5661fc3b37fSMaxime Ripard 5671fc3b37fSMaxime Ripard static struct platform_driver csi2rx_driver = { 5681fc3b37fSMaxime Ripard .probe = csi2rx_probe, 569bbb3f635SUwe Kleine-König .remove_new = csi2rx_remove, 5701fc3b37fSMaxime Ripard 5711fc3b37fSMaxime Ripard .driver = { 5721fc3b37fSMaxime Ripard .name = "cdns-csi2rx", 5731fc3b37fSMaxime Ripard .of_match_table = csi2rx_of_table, 5741fc3b37fSMaxime Ripard }, 5751fc3b37fSMaxime Ripard }; 5761fc3b37fSMaxime Ripard module_platform_driver(csi2rx_driver); 5771fc3b37fSMaxime Ripard MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin.com>"); 5781fc3b37fSMaxime Ripard MODULE_DESCRIPTION("Cadence CSI2-RX controller"); 5791fc3b37fSMaxime Ripard MODULE_LICENSE("GPL"); 580