Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

media: sunxi: Add support for the A83T MIPI CSI-2 controller

The A83T supports MIPI CSI-2 with a composite controller, covering
both the protocol logic and the D-PHY implementation. This controller
seems to be found on the A83T only and probably was abandoned since.

This implementation splits the protocol and D-PHY registers and
uses the PHY framework internally. The D-PHY is not registered as a
standalone PHY driver since it cannot be used with any other
controller.

There are a few notable points about the controller:
- The initialisation sequence involes writing specific magic init
values that do not seem to make any particular sense given the
concerned register fields;
- Interrupts appear to be hitting regardless of the interrupt mask
registers, which can cause a serious flood when transmission errors
occur.

Only 8-bit and 10-bit Bayer formats are currently supported.
While up to 4 internal channels to the CSI controller exist, only one
is currently supported by this implementation.

This work is based on the first version of the driver submitted by
Kévin L'hôpital, which was adapted to mainline from the Allwinner BSP.
This version integrates MIPI CSI-2 support as a standalone V4L2 subdev
instead of merging it in the sun6i-csi driver.

It was tested on a Banana Pi M3 board with an OV8865 sensor in a 4-lane
configuration.

Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
Acked-by: Maxime Ripard <mripard@kernel.org>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>

authored by

Paul Kocialkowski and committed by
Mauro Carvalho Chehab
576d196c e4afdad6

+1150
+1
drivers/media/platform/sunxi/Kconfig
··· 5 5 source "drivers/media/platform/sunxi/sun4i-csi/Kconfig" 6 6 source "drivers/media/platform/sunxi/sun6i-csi/Kconfig" 7 7 source "drivers/media/platform/sunxi/sun6i-mipi-csi2/Kconfig" 8 + source "drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Kconfig" 8 9 source "drivers/media/platform/sunxi/sun8i-di/Kconfig" 9 10 source "drivers/media/platform/sunxi/sun8i-rotate/Kconfig"
+1
drivers/media/platform/sunxi/Makefile
··· 3 3 obj-y += sun4i-csi/ 4 4 obj-y += sun6i-csi/ 5 5 obj-y += sun6i-mipi-csi2/ 6 + obj-y += sun8i-a83t-mipi-csi2/ 6 7 obj-y += sun8i-di/ 7 8 obj-y += sun8i-rotate/
+12
drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Kconfig
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + config VIDEO_SUN8I_A83T_MIPI_CSI2 3 + tristate "Allwinner A83T MIPI CSI-2 Controller and D-PHY Driver" 4 + depends on V4L_PLATFORM_DRIVERS && VIDEO_DEV 5 + depends on ARCH_SUNXI || COMPILE_TEST 6 + depends on PM && COMMON_CLK 7 + select MEDIA_CONTROLLER 8 + select VIDEO_V4L2_SUBDEV_API 9 + select V4L2_FWNODE 10 + select REGMAP_MMIO 11 + help 12 + Support for the Allwinner A83T MIPI CSI-2 controller and D-PHY.
+4
drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + sun8i-a83t-mipi-csi2-y += sun8i_a83t_mipi_csi2.o sun8i_a83t_dphy.o 3 + 4 + obj-$(CONFIG_VIDEO_SUN8I_A83T_MIPI_CSI2) += sun8i-a83t-mipi-csi2.o
+72
drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_dphy.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + /* 3 + * Copyright 2020-2022 Bootlin 4 + * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com> 5 + */ 6 + 7 + #include <linux/phy/phy.h> 8 + #include <linux/regmap.h> 9 + 10 + #include "sun8i_a83t_dphy.h" 11 + #include "sun8i_a83t_mipi_csi2.h" 12 + 13 + static int sun8i_a83t_dphy_configure(struct phy *dphy, 14 + union phy_configure_opts *opts) 15 + { 16 + return phy_mipi_dphy_config_validate(&opts->mipi_dphy); 17 + } 18 + 19 + static int sun8i_a83t_dphy_power_on(struct phy *dphy) 20 + { 21 + struct sun8i_a83t_mipi_csi2_device *csi2_dev = phy_get_drvdata(dphy); 22 + struct regmap *regmap = csi2_dev->regmap; 23 + 24 + regmap_write(regmap, SUN8I_A83T_DPHY_CTRL_REG, 25 + SUN8I_A83T_DPHY_CTRL_RESET_N | 26 + SUN8I_A83T_DPHY_CTRL_SHUTDOWN_N); 27 + 28 + regmap_write(regmap, SUN8I_A83T_DPHY_ANA0_REG, 29 + SUN8I_A83T_DPHY_ANA0_REXT_EN | 30 + SUN8I_A83T_DPHY_ANA0_RINT(2) | 31 + SUN8I_A83T_DPHY_ANA0_SNK(2)); 32 + 33 + return 0; 34 + }; 35 + 36 + static int sun8i_a83t_dphy_power_off(struct phy *dphy) 37 + { 38 + struct sun8i_a83t_mipi_csi2_device *csi2_dev = phy_get_drvdata(dphy); 39 + struct regmap *regmap = csi2_dev->regmap; 40 + 41 + regmap_write(regmap, SUN8I_A83T_DPHY_CTRL_REG, 0); 42 + 43 + return 0; 44 + }; 45 + 46 + static const struct phy_ops sun8i_a83t_dphy_ops = { 47 + .configure = sun8i_a83t_dphy_configure, 48 + .power_on = sun8i_a83t_dphy_power_on, 49 + .power_off = sun8i_a83t_dphy_power_off, 50 + }; 51 + 52 + int sun8i_a83t_dphy_register(struct sun8i_a83t_mipi_csi2_device *csi2_dev) 53 + { 54 + struct device *dev = csi2_dev->dev; 55 + struct phy_provider *phy_provider; 56 + 57 + csi2_dev->dphy = devm_phy_create(dev, NULL, &sun8i_a83t_dphy_ops); 58 + if (IS_ERR(csi2_dev->dphy)) { 59 + dev_err(dev, "failed to create D-PHY\n"); 60 + return PTR_ERR(csi2_dev->dphy); 61 + } 62 + 63 + phy_set_drvdata(csi2_dev->dphy, csi2_dev); 64 + 65 + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 66 + if (IS_ERR(phy_provider)) { 67 + dev_err(dev, "failed to register D-PHY provider\n"); 68 + return PTR_ERR(phy_provider); 69 + } 70 + 71 + return 0; 72 + }
+39
drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_dphy.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0+ */ 2 + /* 3 + * Copyright 2020 Kévin L'hôpital <kevin.lhopital@bootlin.com> 4 + * Copyright 2020-2022 Bootlin 5 + * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com> 6 + */ 7 + 8 + #ifndef _SUN8I_A83T_DPHY_H_ 9 + #define _SUN8I_A83T_DPHY_H_ 10 + 11 + #include "sun8i_a83t_mipi_csi2.h" 12 + 13 + #define SUN8I_A83T_DPHY_CTRL_REG 0x10 14 + #define SUN8I_A83T_DPHY_CTRL_INIT_VALUE 0xb8df698e 15 + #define SUN8I_A83T_DPHY_CTRL_RESET_N BIT(31) 16 + #define SUN8I_A83T_DPHY_CTRL_SHUTDOWN_N BIT(15) 17 + #define SUN8I_A83T_DPHY_CTRL_DEBUG BIT(8) 18 + #define SUN8I_A83T_DPHY_STATUS_REG 0x14 19 + #define SUN8I_A83T_DPHY_STATUS_CLK_STOP BIT(10) 20 + #define SUN8I_A83T_DPHY_STATUS_CLK_ULPS BIT(9) 21 + #define SUN8I_A83T_DPHY_STATUS_HSCLK BIT(8) 22 + #define SUN8I_A83T_DPHY_STATUS_D3_STOP BIT(7) 23 + #define SUN8I_A83T_DPHY_STATUS_D2_STOP BIT(6) 24 + #define SUN8I_A83T_DPHY_STATUS_D1_STOP BIT(5) 25 + #define SUN8I_A83T_DPHY_STATUS_D0_STOP BIT(4) 26 + #define SUN8I_A83T_DPHY_STATUS_D3_ULPS BIT(3) 27 + #define SUN8I_A83T_DPHY_STATUS_D2_ULPS BIT(2) 28 + #define SUN8I_A83T_DPHY_STATUS_D1_ULPS BIT(1) 29 + #define SUN8I_A83T_DPHY_STATUS_D0_ULPS BIT(0) 30 + 31 + #define SUN8I_A83T_DPHY_ANA0_REG 0x30 32 + #define SUN8I_A83T_DPHY_ANA0_REXT_EN BIT(31) 33 + #define SUN8I_A83T_DPHY_ANA0_REXT BIT(30) 34 + #define SUN8I_A83T_DPHY_ANA0_RINT(v) (((v) << 28) & GENMASK(29, 28)) 35 + #define SUN8I_A83T_DPHY_ANA0_SNK(v) (((v) << 20) & GENMASK(22, 20)) 36 + 37 + int sun8i_a83t_dphy_register(struct sun8i_a83t_mipi_csi2_device *csi2_dev); 38 + 39 + #endif
+815
drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + /* 3 + * Copyright 2020 Kévin L'hôpital <kevin.lhopital@bootlin.com> 4 + * Copyright 2020-2022 Bootlin 5 + * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com> 6 + */ 7 + 8 + #include <linux/clk.h> 9 + #include <linux/module.h> 10 + #include <linux/of.h> 11 + #include <linux/of_device.h> 12 + #include <linux/phy/phy.h> 13 + #include <linux/platform_device.h> 14 + #include <linux/pm_runtime.h> 15 + #include <linux/regmap.h> 16 + #include <linux/reset.h> 17 + #include <media/mipi-csi2.h> 18 + #include <media/v4l2-ctrls.h> 19 + #include <media/v4l2-device.h> 20 + #include <media/v4l2-fwnode.h> 21 + 22 + #include "sun8i_a83t_dphy.h" 23 + #include "sun8i_a83t_mipi_csi2.h" 24 + #include "sun8i_a83t_mipi_csi2_reg.h" 25 + 26 + /* Format */ 27 + 28 + static const struct sun8i_a83t_mipi_csi2_format 29 + sun8i_a83t_mipi_csi2_formats[] = { 30 + { 31 + .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, 32 + .data_type = MIPI_CSI2_DT_RAW8, 33 + .bpp = 8, 34 + }, 35 + { 36 + .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8, 37 + .data_type = MIPI_CSI2_DT_RAW8, 38 + .bpp = 8, 39 + }, 40 + { 41 + .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8, 42 + .data_type = MIPI_CSI2_DT_RAW8, 43 + .bpp = 8, 44 + }, 45 + { 46 + .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8, 47 + .data_type = MIPI_CSI2_DT_RAW8, 48 + .bpp = 8, 49 + }, 50 + { 51 + .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, 52 + .data_type = MIPI_CSI2_DT_RAW10, 53 + .bpp = 10, 54 + }, 55 + { 56 + .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10, 57 + .data_type = MIPI_CSI2_DT_RAW10, 58 + .bpp = 10, 59 + }, 60 + { 61 + .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10, 62 + .data_type = MIPI_CSI2_DT_RAW10, 63 + .bpp = 10, 64 + }, 65 + { 66 + .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, 67 + .data_type = MIPI_CSI2_DT_RAW10, 68 + .bpp = 10, 69 + }, 70 + }; 71 + 72 + static const struct sun8i_a83t_mipi_csi2_format * 73 + sun8i_a83t_mipi_csi2_format_find(u32 mbus_code) 74 + { 75 + unsigned int i; 76 + 77 + for (i = 0; i < ARRAY_SIZE(sun8i_a83t_mipi_csi2_formats); i++) 78 + if (sun8i_a83t_mipi_csi2_formats[i].mbus_code == mbus_code) 79 + return &sun8i_a83t_mipi_csi2_formats[i]; 80 + 81 + return NULL; 82 + } 83 + 84 + /* Controller */ 85 + 86 + static void 87 + sun8i_a83t_mipi_csi2_init(struct sun8i_a83t_mipi_csi2_device *csi2_dev) 88 + { 89 + struct regmap *regmap = csi2_dev->regmap; 90 + 91 + /* 92 + * The Allwinner BSP sets various magic values on a bunch of registers. 93 + * This is apparently a necessary initialization process that will cause 94 + * the capture to fail with unsolicited interrupts hitting if skipped. 95 + * 96 + * Most of the registers are set to proper values later, except for the 97 + * two reserved registers. They are said to hold a "hardware lock" 98 + * value, without more information available. 99 + */ 100 + 101 + regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_CTRL_REG, 0); 102 + regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_CTRL_REG, 103 + SUN8I_A83T_MIPI_CSI2_CTRL_INIT_VALUE); 104 + 105 + regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_RX_PKT_NUM_REG, 0); 106 + regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_RX_PKT_NUM_REG, 107 + SUN8I_A83T_MIPI_CSI2_RX_PKT_NUM_INIT_VALUE); 108 + 109 + regmap_write(regmap, SUN8I_A83T_DPHY_CTRL_REG, 0); 110 + regmap_write(regmap, SUN8I_A83T_DPHY_CTRL_REG, 111 + SUN8I_A83T_DPHY_CTRL_INIT_VALUE); 112 + 113 + regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_RSVD1_REG, 0); 114 + regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_RSVD1_REG, 115 + SUN8I_A83T_MIPI_CSI2_RSVD1_HW_LOCK_VALUE); 116 + 117 + regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_RSVD2_REG, 0); 118 + regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_RSVD2_REG, 119 + SUN8I_A83T_MIPI_CSI2_RSVD2_HW_LOCK_VALUE); 120 + 121 + regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_CFG_REG, 0); 122 + regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_CFG_REG, 123 + SUN8I_A83T_MIPI_CSI2_CFG_INIT_VALUE); 124 + } 125 + 126 + static void 127 + sun8i_a83t_mipi_csi2_enable(struct sun8i_a83t_mipi_csi2_device *csi2_dev) 128 + { 129 + struct regmap *regmap = csi2_dev->regmap; 130 + 131 + regmap_update_bits(regmap, SUN8I_A83T_MIPI_CSI2_CFG_REG, 132 + SUN8I_A83T_MIPI_CSI2_CFG_SYNC_EN, 133 + SUN8I_A83T_MIPI_CSI2_CFG_SYNC_EN); 134 + } 135 + 136 + static void 137 + sun8i_a83t_mipi_csi2_disable(struct sun8i_a83t_mipi_csi2_device *csi2_dev) 138 + { 139 + struct regmap *regmap = csi2_dev->regmap; 140 + 141 + regmap_update_bits(regmap, SUN8I_A83T_MIPI_CSI2_CFG_REG, 142 + SUN8I_A83T_MIPI_CSI2_CFG_SYNC_EN, 0); 143 + 144 + regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_CTRL_REG, 0); 145 + } 146 + 147 + static void 148 + sun8i_a83t_mipi_csi2_configure(struct sun8i_a83t_mipi_csi2_device *csi2_dev) 149 + { 150 + struct regmap *regmap = csi2_dev->regmap; 151 + unsigned int lanes_count = 152 + csi2_dev->bridge.endpoint.bus.mipi_csi2.num_data_lanes; 153 + struct v4l2_mbus_framefmt *mbus_format = &csi2_dev->bridge.mbus_format; 154 + const struct sun8i_a83t_mipi_csi2_format *format; 155 + struct device *dev = csi2_dev->dev; 156 + u32 version = 0; 157 + 158 + format = sun8i_a83t_mipi_csi2_format_find(mbus_format->code); 159 + if (WARN_ON(!format)) 160 + return; 161 + 162 + regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_CTRL_REG, 163 + SUN8I_A83T_MIPI_CSI2_CTRL_RESET_N); 164 + 165 + regmap_read(regmap, SUN8I_A83T_MIPI_CSI2_VERSION_REG, &version); 166 + 167 + dev_dbg(dev, "A83T MIPI CSI-2 version: %04x\n", version); 168 + 169 + regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_CFG_REG, 170 + SUN8I_A83T_MIPI_CSI2_CFG_UNPKT_EN | 171 + SUN8I_A83T_MIPI_CSI2_CFG_SYNC_DLY_CYCLE(8) | 172 + SUN8I_A83T_MIPI_CSI2_CFG_N_CHANNEL(1) | 173 + SUN8I_A83T_MIPI_CSI2_CFG_N_LANE(lanes_count)); 174 + 175 + /* 176 + * Only a single virtual channel (index 0) is currently supported. 177 + * While the registers do mention multiple physical channels being 178 + * available (which can be configured to match a specific virtual 179 + * channel or data type), it's unclear whether channels > 0 are actually 180 + * connected and available and the reference source code only makes use 181 + * of channel 0. 182 + * 183 + * Using extra channels would also require matching channels to be 184 + * available on the CSI (and ISP) side, which is also unsure although 185 + * some CSI implementations are said to support multiple channels for 186 + * BT656 time-sharing. 187 + * 188 + * We still configure virtual channel numbers to ensure that virtual 189 + * channel 0 only goes to channel 0. 190 + */ 191 + 192 + regmap_write(regmap, SUN8I_A83T_MIPI_CSI2_VCDT0_REG, 193 + SUN8I_A83T_MIPI_CSI2_VCDT0_CH_VC(3, 3) | 194 + SUN8I_A83T_MIPI_CSI2_VCDT0_CH_VC(2, 2) | 195 + SUN8I_A83T_MIPI_CSI2_VCDT0_CH_VC(1, 1) | 196 + SUN8I_A83T_MIPI_CSI2_VCDT0_CH_VC(0, 0) | 197 + SUN8I_A83T_MIPI_CSI2_VCDT0_CH_DT(0, format->data_type)); 198 + } 199 + 200 + /* V4L2 Subdev */ 201 + 202 + static int sun8i_a83t_mipi_csi2_s_stream(struct v4l2_subdev *subdev, int on) 203 + { 204 + struct sun8i_a83t_mipi_csi2_device *csi2_dev = 205 + v4l2_get_subdevdata(subdev); 206 + struct v4l2_subdev *source_subdev = csi2_dev->bridge.source_subdev; 207 + union phy_configure_opts dphy_opts = { 0 }; 208 + struct phy_configure_opts_mipi_dphy *dphy_cfg = &dphy_opts.mipi_dphy; 209 + struct v4l2_mbus_framefmt *mbus_format = &csi2_dev->bridge.mbus_format; 210 + const struct sun8i_a83t_mipi_csi2_format *format; 211 + struct phy *dphy = csi2_dev->dphy; 212 + struct device *dev = csi2_dev->dev; 213 + struct v4l2_ctrl *ctrl; 214 + unsigned int lanes_count = 215 + csi2_dev->bridge.endpoint.bus.mipi_csi2.num_data_lanes; 216 + unsigned long pixel_rate; 217 + /* Initialize to 0 to use both in disable label (ret != 0) and off. */ 218 + int ret = 0; 219 + 220 + if (!source_subdev) 221 + return -ENODEV; 222 + 223 + if (!on) { 224 + v4l2_subdev_call(source_subdev, video, s_stream, 0); 225 + goto disable; 226 + } 227 + 228 + /* Runtime PM */ 229 + 230 + ret = pm_runtime_resume_and_get(dev); 231 + if (ret < 0) 232 + return ret; 233 + 234 + /* Sensor pixel rate */ 235 + 236 + ctrl = v4l2_ctrl_find(source_subdev->ctrl_handler, V4L2_CID_PIXEL_RATE); 237 + if (!ctrl) { 238 + dev_err(dev, "missing sensor pixel rate\n"); 239 + ret = -ENODEV; 240 + goto error_pm; 241 + } 242 + 243 + pixel_rate = (unsigned long)v4l2_ctrl_g_ctrl_int64(ctrl); 244 + if (!pixel_rate) { 245 + dev_err(dev, "missing (zero) sensor pixel rate\n"); 246 + ret = -ENODEV; 247 + goto error_pm; 248 + } 249 + 250 + /* D-PHY */ 251 + 252 + if (!lanes_count) { 253 + dev_err(dev, "missing (zero) MIPI CSI-2 lanes count\n"); 254 + ret = -ENODEV; 255 + goto error_pm; 256 + } 257 + 258 + format = sun8i_a83t_mipi_csi2_format_find(mbus_format->code); 259 + if (WARN_ON(!format)) { 260 + ret = -ENODEV; 261 + goto error_pm; 262 + } 263 + 264 + phy_mipi_dphy_get_default_config(pixel_rate, format->bpp, lanes_count, 265 + dphy_cfg); 266 + 267 + /* 268 + * Note that our hardware is using DDR, which is not taken in account by 269 + * phy_mipi_dphy_get_default_config when calculating hs_clk_rate from 270 + * the pixel rate, lanes count and bpp. 271 + * 272 + * The resulting clock rate is basically the symbol rate over the whole 273 + * link. The actual clock rate is calculated with division by two since 274 + * DDR samples both on rising and falling edges. 275 + */ 276 + 277 + dev_dbg(dev, "A83T MIPI CSI-2 config:\n"); 278 + dev_dbg(dev, "%ld pixels/s, %u bits/pixel, %u lanes, %lu Hz clock\n", 279 + pixel_rate, format->bpp, lanes_count, 280 + dphy_cfg->hs_clk_rate / 2); 281 + 282 + ret = phy_reset(dphy); 283 + if (ret) { 284 + dev_err(dev, "failed to reset MIPI D-PHY\n"); 285 + goto error_pm; 286 + } 287 + 288 + ret = phy_configure(dphy, &dphy_opts); 289 + if (ret) { 290 + dev_err(dev, "failed to configure MIPI D-PHY\n"); 291 + goto error_pm; 292 + } 293 + 294 + /* Controller */ 295 + 296 + sun8i_a83t_mipi_csi2_configure(csi2_dev); 297 + sun8i_a83t_mipi_csi2_enable(csi2_dev); 298 + 299 + /* D-PHY */ 300 + 301 + ret = phy_power_on(dphy); 302 + if (ret) { 303 + dev_err(dev, "failed to power on MIPI D-PHY\n"); 304 + goto error_pm; 305 + } 306 + 307 + /* Source */ 308 + 309 + ret = v4l2_subdev_call(source_subdev, video, s_stream, 1); 310 + if (ret && ret != -ENOIOCTLCMD) 311 + goto disable; 312 + 313 + return 0; 314 + 315 + disable: 316 + phy_power_off(dphy); 317 + sun8i_a83t_mipi_csi2_disable(csi2_dev); 318 + 319 + error_pm: 320 + pm_runtime_put(dev); 321 + 322 + return ret; 323 + } 324 + 325 + static const struct v4l2_subdev_video_ops 326 + sun8i_a83t_mipi_csi2_video_ops = { 327 + .s_stream = sun8i_a83t_mipi_csi2_s_stream, 328 + }; 329 + 330 + static void 331 + sun8i_a83t_mipi_csi2_mbus_format_prepare(struct v4l2_mbus_framefmt *mbus_format) 332 + { 333 + if (!sun8i_a83t_mipi_csi2_format_find(mbus_format->code)) 334 + mbus_format->code = sun8i_a83t_mipi_csi2_formats[0].mbus_code; 335 + 336 + mbus_format->field = V4L2_FIELD_NONE; 337 + mbus_format->colorspace = V4L2_COLORSPACE_RAW; 338 + mbus_format->quantization = V4L2_QUANTIZATION_DEFAULT; 339 + mbus_format->xfer_func = V4L2_XFER_FUNC_DEFAULT; 340 + } 341 + 342 + static int sun8i_a83t_mipi_csi2_init_cfg(struct v4l2_subdev *subdev, 343 + struct v4l2_subdev_state *state) 344 + { 345 + struct sun8i_a83t_mipi_csi2_device *csi2_dev = 346 + v4l2_get_subdevdata(subdev); 347 + unsigned int pad = SUN8I_A83T_MIPI_CSI2_PAD_SINK; 348 + struct v4l2_mbus_framefmt *mbus_format = 349 + v4l2_subdev_get_try_format(subdev, state, pad); 350 + struct mutex *lock = &csi2_dev->bridge.lock; 351 + 352 + mutex_lock(lock); 353 + 354 + mbus_format->code = sun8i_a83t_mipi_csi2_formats[0].mbus_code; 355 + mbus_format->width = 640; 356 + mbus_format->height = 480; 357 + 358 + sun8i_a83t_mipi_csi2_mbus_format_prepare(mbus_format); 359 + 360 + mutex_unlock(lock); 361 + 362 + return 0; 363 + } 364 + 365 + static int 366 + sun8i_a83t_mipi_csi2_enum_mbus_code(struct v4l2_subdev *subdev, 367 + struct v4l2_subdev_state *state, 368 + struct v4l2_subdev_mbus_code_enum *code_enum) 369 + { 370 + if (code_enum->index >= ARRAY_SIZE(sun8i_a83t_mipi_csi2_formats)) 371 + return -EINVAL; 372 + 373 + code_enum->code = 374 + sun8i_a83t_mipi_csi2_formats[code_enum->index].mbus_code; 375 + 376 + return 0; 377 + } 378 + 379 + static int sun8i_a83t_mipi_csi2_get_fmt(struct v4l2_subdev *subdev, 380 + struct v4l2_subdev_state *state, 381 + struct v4l2_subdev_format *format) 382 + { 383 + struct sun8i_a83t_mipi_csi2_device *csi2_dev = 384 + v4l2_get_subdevdata(subdev); 385 + struct v4l2_mbus_framefmt *mbus_format = &format->format; 386 + struct mutex *lock = &csi2_dev->bridge.lock; 387 + 388 + mutex_lock(lock); 389 + 390 + if (format->which == V4L2_SUBDEV_FORMAT_TRY) 391 + *mbus_format = *v4l2_subdev_get_try_format(subdev, state, 392 + format->pad); 393 + else 394 + *mbus_format = csi2_dev->bridge.mbus_format; 395 + 396 + mutex_unlock(lock); 397 + 398 + return 0; 399 + } 400 + 401 + static int sun8i_a83t_mipi_csi2_set_fmt(struct v4l2_subdev *subdev, 402 + struct v4l2_subdev_state *state, 403 + struct v4l2_subdev_format *format) 404 + { 405 + struct sun8i_a83t_mipi_csi2_device *csi2_dev = 406 + v4l2_get_subdevdata(subdev); 407 + struct v4l2_mbus_framefmt *mbus_format = &format->format; 408 + struct mutex *lock = &csi2_dev->bridge.lock; 409 + 410 + mutex_lock(lock); 411 + 412 + sun8i_a83t_mipi_csi2_mbus_format_prepare(mbus_format); 413 + 414 + if (format->which == V4L2_SUBDEV_FORMAT_TRY) 415 + *v4l2_subdev_get_try_format(subdev, state, format->pad) = 416 + *mbus_format; 417 + else 418 + csi2_dev->bridge.mbus_format = *mbus_format; 419 + 420 + mutex_unlock(lock); 421 + 422 + return 0; 423 + } 424 + 425 + static const struct v4l2_subdev_pad_ops sun8i_a83t_mipi_csi2_pad_ops = { 426 + .init_cfg = sun8i_a83t_mipi_csi2_init_cfg, 427 + .enum_mbus_code = sun8i_a83t_mipi_csi2_enum_mbus_code, 428 + .get_fmt = sun8i_a83t_mipi_csi2_get_fmt, 429 + .set_fmt = sun8i_a83t_mipi_csi2_set_fmt, 430 + }; 431 + 432 + static const struct v4l2_subdev_ops sun8i_a83t_mipi_csi2_subdev_ops = { 433 + .video = &sun8i_a83t_mipi_csi2_video_ops, 434 + .pad = &sun8i_a83t_mipi_csi2_pad_ops, 435 + }; 436 + 437 + /* Media Entity */ 438 + 439 + static const struct media_entity_operations sun8i_a83t_mipi_csi2_entity_ops = { 440 + .link_validate = v4l2_subdev_link_validate, 441 + }; 442 + 443 + /* V4L2 Async */ 444 + 445 + static int 446 + sun8i_a83t_mipi_csi2_notifier_bound(struct v4l2_async_notifier *notifier, 447 + struct v4l2_subdev *remote_subdev, 448 + struct v4l2_async_subdev *async_subdev) 449 + { 450 + struct v4l2_subdev *subdev = notifier->sd; 451 + struct sun8i_a83t_mipi_csi2_device *csi2_dev = 452 + container_of(notifier, struct sun8i_a83t_mipi_csi2_device, 453 + bridge.notifier); 454 + struct media_entity *sink_entity = &subdev->entity; 455 + struct media_entity *source_entity = &remote_subdev->entity; 456 + struct device *dev = csi2_dev->dev; 457 + int sink_pad_index = 0; 458 + int source_pad_index; 459 + int ret; 460 + 461 + ret = media_entity_get_fwnode_pad(source_entity, remote_subdev->fwnode, 462 + MEDIA_PAD_FL_SOURCE); 463 + if (ret < 0) { 464 + dev_err(dev, "missing source pad in external entity %s\n", 465 + source_entity->name); 466 + return -EINVAL; 467 + } 468 + 469 + source_pad_index = ret; 470 + 471 + dev_dbg(dev, "creating %s:%u -> %s:%u link\n", source_entity->name, 472 + source_pad_index, sink_entity->name, sink_pad_index); 473 + 474 + ret = media_create_pad_link(source_entity, source_pad_index, 475 + sink_entity, sink_pad_index, 476 + MEDIA_LNK_FL_ENABLED | 477 + MEDIA_LNK_FL_IMMUTABLE); 478 + if (ret) { 479 + dev_err(dev, "failed to create %s:%u -> %s:%u link\n", 480 + source_entity->name, source_pad_index, 481 + sink_entity->name, sink_pad_index); 482 + return ret; 483 + } 484 + 485 + csi2_dev->bridge.source_subdev = remote_subdev; 486 + 487 + return 0; 488 + } 489 + 490 + static const struct v4l2_async_notifier_operations 491 + sun8i_a83t_mipi_csi2_notifier_ops = { 492 + .bound = sun8i_a83t_mipi_csi2_notifier_bound, 493 + }; 494 + 495 + /* Bridge */ 496 + 497 + static int 498 + sun8i_a83t_mipi_csi2_bridge_source_setup(struct sun8i_a83t_mipi_csi2_device *csi2_dev) 499 + { 500 + struct v4l2_async_notifier *notifier = &csi2_dev->bridge.notifier; 501 + struct v4l2_fwnode_endpoint *endpoint = &csi2_dev->bridge.endpoint; 502 + struct v4l2_async_subdev *subdev_async; 503 + struct fwnode_handle *handle; 504 + struct device *dev = csi2_dev->dev; 505 + int ret; 506 + 507 + handle = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 508 + FWNODE_GRAPH_ENDPOINT_NEXT); 509 + if (!handle) 510 + return -ENODEV; 511 + 512 + endpoint->bus_type = V4L2_MBUS_CSI2_DPHY; 513 + 514 + ret = v4l2_fwnode_endpoint_parse(handle, endpoint); 515 + if (ret) 516 + goto complete; 517 + 518 + subdev_async = 519 + v4l2_async_nf_add_fwnode_remote(notifier, handle, 520 + struct v4l2_async_subdev); 521 + if (IS_ERR(subdev_async)) 522 + ret = PTR_ERR(subdev_async); 523 + 524 + complete: 525 + fwnode_handle_put(handle); 526 + 527 + return ret; 528 + } 529 + 530 + static int 531 + sun8i_a83t_mipi_csi2_bridge_setup(struct sun8i_a83t_mipi_csi2_device *csi2_dev) 532 + { 533 + struct sun8i_a83t_mipi_csi2_bridge *bridge = &csi2_dev->bridge; 534 + struct v4l2_subdev *subdev = &bridge->subdev; 535 + struct v4l2_async_notifier *notifier = &bridge->notifier; 536 + struct media_pad *pads = bridge->pads; 537 + struct device *dev = csi2_dev->dev; 538 + int ret; 539 + 540 + mutex_init(&bridge->lock); 541 + 542 + /* V4L2 Subdev */ 543 + 544 + v4l2_subdev_init(subdev, &sun8i_a83t_mipi_csi2_subdev_ops); 545 + strscpy(subdev->name, SUN8I_A83T_MIPI_CSI2_NAME, sizeof(subdev->name)); 546 + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 547 + subdev->owner = THIS_MODULE; 548 + subdev->dev = dev; 549 + 550 + v4l2_set_subdevdata(subdev, csi2_dev); 551 + 552 + /* Media Entity */ 553 + 554 + subdev->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; 555 + subdev->entity.ops = &sun8i_a83t_mipi_csi2_entity_ops; 556 + 557 + /* Media Pads */ 558 + 559 + pads[SUN8I_A83T_MIPI_CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK; 560 + pads[SUN8I_A83T_MIPI_CSI2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; 561 + 562 + ret = media_entity_pads_init(&subdev->entity, 563 + SUN8I_A83T_MIPI_CSI2_PAD_COUNT, pads); 564 + if (ret) 565 + return ret; 566 + 567 + /* V4L2 Async */ 568 + 569 + v4l2_async_nf_init(notifier); 570 + notifier->ops = &sun8i_a83t_mipi_csi2_notifier_ops; 571 + 572 + ret = sun8i_a83t_mipi_csi2_bridge_source_setup(csi2_dev); 573 + if (ret) 574 + goto error_v4l2_notifier_cleanup; 575 + 576 + ret = v4l2_async_subdev_nf_register(subdev, notifier); 577 + if (ret < 0) 578 + goto error_v4l2_notifier_cleanup; 579 + 580 + /* V4L2 Subdev */ 581 + 582 + ret = v4l2_async_register_subdev(subdev); 583 + if (ret < 0) 584 + goto error_v4l2_notifier_unregister; 585 + 586 + return 0; 587 + 588 + error_v4l2_notifier_unregister: 589 + v4l2_async_nf_unregister(notifier); 590 + 591 + error_v4l2_notifier_cleanup: 592 + v4l2_async_nf_cleanup(notifier); 593 + 594 + media_entity_cleanup(&subdev->entity); 595 + 596 + return ret; 597 + } 598 + 599 + static void 600 + sun8i_a83t_mipi_csi2_bridge_cleanup(struct sun8i_a83t_mipi_csi2_device *csi2_dev) 601 + { 602 + struct v4l2_subdev *subdev = &csi2_dev->bridge.subdev; 603 + struct v4l2_async_notifier *notifier = &csi2_dev->bridge.notifier; 604 + 605 + v4l2_async_unregister_subdev(subdev); 606 + v4l2_async_nf_unregister(notifier); 607 + v4l2_async_nf_cleanup(notifier); 608 + media_entity_cleanup(&subdev->entity); 609 + } 610 + 611 + /* Platform */ 612 + 613 + static int sun8i_a83t_mipi_csi2_suspend(struct device *dev) 614 + { 615 + struct sun8i_a83t_mipi_csi2_device *csi2_dev = dev_get_drvdata(dev); 616 + 617 + clk_disable_unprepare(csi2_dev->clock_misc); 618 + clk_disable_unprepare(csi2_dev->clock_mipi); 619 + clk_disable_unprepare(csi2_dev->clock_mod); 620 + reset_control_assert(csi2_dev->reset); 621 + 622 + return 0; 623 + } 624 + 625 + static int sun8i_a83t_mipi_csi2_resume(struct device *dev) 626 + { 627 + struct sun8i_a83t_mipi_csi2_device *csi2_dev = dev_get_drvdata(dev); 628 + int ret; 629 + 630 + ret = reset_control_deassert(csi2_dev->reset); 631 + if (ret) { 632 + dev_err(dev, "failed to deassert reset\n"); 633 + return ret; 634 + } 635 + 636 + ret = clk_prepare_enable(csi2_dev->clock_mod); 637 + if (ret) { 638 + dev_err(dev, "failed to enable module clock\n"); 639 + goto error_reset; 640 + } 641 + 642 + ret = clk_prepare_enable(csi2_dev->clock_mipi); 643 + if (ret) { 644 + dev_err(dev, "failed to enable MIPI clock\n"); 645 + goto error_clock_mod; 646 + } 647 + 648 + ret = clk_prepare_enable(csi2_dev->clock_misc); 649 + if (ret) { 650 + dev_err(dev, "failed to enable CSI misc clock\n"); 651 + goto error_clock_mipi; 652 + } 653 + 654 + sun8i_a83t_mipi_csi2_init(csi2_dev); 655 + 656 + return 0; 657 + 658 + error_clock_mipi: 659 + clk_disable_unprepare(csi2_dev->clock_mipi); 660 + 661 + error_clock_mod: 662 + clk_disable_unprepare(csi2_dev->clock_mod); 663 + 664 + error_reset: 665 + reset_control_assert(csi2_dev->reset); 666 + 667 + return ret; 668 + } 669 + 670 + static const struct dev_pm_ops sun8i_a83t_mipi_csi2_pm_ops = { 671 + .runtime_suspend = sun8i_a83t_mipi_csi2_suspend, 672 + .runtime_resume = sun8i_a83t_mipi_csi2_resume, 673 + }; 674 + 675 + static const struct regmap_config sun8i_a83t_mipi_csi2_regmap_config = { 676 + .reg_bits = 32, 677 + .reg_stride = 4, 678 + .val_bits = 32, 679 + .max_register = 0x120, 680 + }; 681 + 682 + static int 683 + sun8i_a83t_mipi_csi2_resources_setup(struct sun8i_a83t_mipi_csi2_device *csi2_dev, 684 + struct platform_device *platform_dev) 685 + { 686 + struct device *dev = csi2_dev->dev; 687 + void __iomem *io_base; 688 + int ret; 689 + 690 + /* Registers */ 691 + 692 + io_base = devm_platform_ioremap_resource(platform_dev, 0); 693 + if (IS_ERR(io_base)) 694 + return PTR_ERR(io_base); 695 + 696 + csi2_dev->regmap = 697 + devm_regmap_init_mmio_clk(dev, "bus", io_base, 698 + &sun8i_a83t_mipi_csi2_regmap_config); 699 + if (IS_ERR(csi2_dev->regmap)) { 700 + dev_err(dev, "failed to init register map\n"); 701 + return PTR_ERR(csi2_dev->regmap); 702 + } 703 + 704 + /* Clocks */ 705 + 706 + csi2_dev->clock_mod = devm_clk_get(dev, "mod"); 707 + if (IS_ERR(csi2_dev->clock_mod)) { 708 + dev_err(dev, "failed to acquire mod clock\n"); 709 + return PTR_ERR(csi2_dev->clock_mod); 710 + } 711 + 712 + ret = clk_set_rate_exclusive(csi2_dev->clock_mod, 297000000); 713 + if (ret) { 714 + dev_err(dev, "failed to set mod clock rate\n"); 715 + return ret; 716 + } 717 + 718 + csi2_dev->clock_mipi = devm_clk_get(dev, "mipi"); 719 + if (IS_ERR(csi2_dev->clock_mipi)) { 720 + dev_err(dev, "failed to acquire mipi clock\n"); 721 + return PTR_ERR(csi2_dev->clock_mipi); 722 + } 723 + 724 + csi2_dev->clock_misc = devm_clk_get(dev, "misc"); 725 + if (IS_ERR(csi2_dev->clock_misc)) { 726 + dev_err(dev, "failed to acquire misc clock\n"); 727 + return PTR_ERR(csi2_dev->clock_misc); 728 + } 729 + 730 + /* Reset */ 731 + 732 + csi2_dev->reset = devm_reset_control_get_shared(dev, NULL); 733 + if (IS_ERR(csi2_dev->reset)) { 734 + dev_err(dev, "failed to get reset controller\n"); 735 + return PTR_ERR(csi2_dev->reset); 736 + } 737 + 738 + /* D-PHY */ 739 + 740 + ret = sun8i_a83t_dphy_register(csi2_dev); 741 + if (ret) { 742 + dev_err(dev, "failed to initialize MIPI D-PHY\n"); 743 + return ret; 744 + } 745 + 746 + /* Runtime PM */ 747 + 748 + pm_runtime_enable(dev); 749 + 750 + return 0; 751 + } 752 + 753 + static void 754 + sun8i_a83t_mipi_csi2_resources_cleanup(struct sun8i_a83t_mipi_csi2_device *csi2_dev) 755 + { 756 + pm_runtime_disable(csi2_dev->dev); 757 + phy_exit(csi2_dev->dphy); 758 + clk_rate_exclusive_put(csi2_dev->clock_mod); 759 + } 760 + 761 + static int sun8i_a83t_mipi_csi2_probe(struct platform_device *platform_dev) 762 + { 763 + struct sun8i_a83t_mipi_csi2_device *csi2_dev; 764 + struct device *dev = &platform_dev->dev; 765 + int ret; 766 + 767 + csi2_dev = devm_kzalloc(dev, sizeof(*csi2_dev), GFP_KERNEL); 768 + if (!csi2_dev) 769 + return -ENOMEM; 770 + 771 + csi2_dev->dev = dev; 772 + platform_set_drvdata(platform_dev, csi2_dev); 773 + 774 + ret = sun8i_a83t_mipi_csi2_resources_setup(csi2_dev, platform_dev); 775 + if (ret) 776 + return ret; 777 + 778 + ret = sun8i_a83t_mipi_csi2_bridge_setup(csi2_dev); 779 + if (ret) 780 + return ret; 781 + 782 + return 0; 783 + } 784 + 785 + static int sun8i_a83t_mipi_csi2_remove(struct platform_device *platform_dev) 786 + { 787 + struct sun8i_a83t_mipi_csi2_device *csi2_dev = 788 + platform_get_drvdata(platform_dev); 789 + 790 + sun8i_a83t_mipi_csi2_bridge_cleanup(csi2_dev); 791 + sun8i_a83t_mipi_csi2_resources_cleanup(csi2_dev); 792 + 793 + return 0; 794 + } 795 + 796 + static const struct of_device_id sun8i_a83t_mipi_csi2_of_match[] = { 797 + { .compatible = "allwinner,sun8i-a83t-mipi-csi2" }, 798 + {}, 799 + }; 800 + MODULE_DEVICE_TABLE(of, sun8i_a83t_mipi_csi2_of_match); 801 + 802 + static struct platform_driver sun8i_a83t_mipi_csi2_platform_driver = { 803 + .probe = sun8i_a83t_mipi_csi2_probe, 804 + .remove = sun8i_a83t_mipi_csi2_remove, 805 + .driver = { 806 + .name = SUN8I_A83T_MIPI_CSI2_NAME, 807 + .of_match_table = of_match_ptr(sun8i_a83t_mipi_csi2_of_match), 808 + .pm = &sun8i_a83t_mipi_csi2_pm_ops, 809 + }, 810 + }; 811 + module_platform_driver(sun8i_a83t_mipi_csi2_platform_driver); 812 + 813 + MODULE_DESCRIPTION("Allwinner A83T MIPI CSI-2 and D-PHY Controller Driver"); 814 + MODULE_AUTHOR("Paul Kocialkowski <paul.kocialkowski@bootlin.com>"); 815 + MODULE_LICENSE("GPL");
+55
drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0+ */ 2 + /* 3 + * Copyright 2020 Kévin L'hôpital <kevin.lhopital@bootlin.com> 4 + * Copyright 2020-2022 Bootlin 5 + * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com> 6 + */ 7 + 8 + #ifndef _SUN8I_A83T_MIPI_CSI2_H_ 9 + #define _SUN8I_A83T_MIPI_CSI2_H_ 10 + 11 + #include <linux/phy/phy.h> 12 + #include <linux/regmap.h> 13 + #include <linux/reset.h> 14 + #include <media/v4l2-device.h> 15 + #include <media/v4l2-fwnode.h> 16 + 17 + #define SUN8I_A83T_MIPI_CSI2_NAME "sun8i-a83t-mipi-csi2" 18 + 19 + enum sun8i_a83t_mipi_csi2_pad { 20 + SUN8I_A83T_MIPI_CSI2_PAD_SINK = 0, 21 + SUN8I_A83T_MIPI_CSI2_PAD_SOURCE = 1, 22 + SUN8I_A83T_MIPI_CSI2_PAD_COUNT = 2, 23 + }; 24 + 25 + struct sun8i_a83t_mipi_csi2_format { 26 + u32 mbus_code; 27 + u8 data_type; 28 + u32 bpp; 29 + }; 30 + 31 + struct sun8i_a83t_mipi_csi2_bridge { 32 + struct v4l2_subdev subdev; 33 + struct media_pad pads[SUN8I_A83T_MIPI_CSI2_PAD_COUNT]; 34 + struct v4l2_fwnode_endpoint endpoint; 35 + struct v4l2_async_notifier notifier; 36 + struct v4l2_mbus_framefmt mbus_format; 37 + struct mutex lock; /* Mbus format lock. */ 38 + 39 + struct v4l2_subdev *source_subdev; 40 + }; 41 + 42 + struct sun8i_a83t_mipi_csi2_device { 43 + struct device *dev; 44 + 45 + struct regmap *regmap; 46 + struct clk *clock_mod; 47 + struct clk *clock_mipi; 48 + struct clk *clock_misc; 49 + struct reset_control *reset; 50 + struct phy *dphy; 51 + 52 + struct sun8i_a83t_mipi_csi2_bridge bridge; 53 + }; 54 + 55 + #endif
+151
drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2_reg.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0+ */ 2 + /* 3 + * Copyright 2020 Kévin L'hôpital <kevin.lhopital@bootlin.com> 4 + * Copyright 2020-2022 Bootlin 5 + * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com> 6 + */ 7 + 8 + #ifndef _SUN8I_A83T_MIPI_CSI2_REG_H_ 9 + #define _SUN8I_A83T_MIPI_CSI2_REG_H_ 10 + 11 + #define SUN8I_A83T_MIPI_CSI2_VERSION_REG 0x0 12 + #define SUN8I_A83T_MIPI_CSI2_CTRL_REG 0x4 13 + #define SUN8I_A83T_MIPI_CSI2_CTRL_INIT_VALUE 0xb8c39bec 14 + #define SUN8I_A83T_MIPI_CSI2_CTRL_RESET_N BIT(31) 15 + #define SUN8I_A83T_MIPI_CSI2_RX_PKT_NUM_REG 0x8 16 + #define SUN8I_A83T_MIPI_CSI2_RX_PKT_NUM_INIT_VALUE 0xb8d257f8 17 + #define SUN8I_A83T_MIPI_CSI2_RSVD0_REG 0xc 18 + 19 + #define SUN8I_A83T_MIPI_CSI2_RSVD1_REG 0x18 20 + #define SUN8I_A83T_MIPI_CSI2_RSVD1_HW_LOCK_VALUE 0xb8c8a30c 21 + #define SUN8I_A83T_MIPI_CSI2_RSVD2_REG 0x1c 22 + #define SUN8I_A83T_MIPI_CSI2_RSVD2_HW_LOCK_VALUE 0xb8df8ad7 23 + #define SUN8I_A83T_MIPI_CSI2_INT_STA0_REG 0x20 24 + #define SUN8I_A83T_MIPI_CSI2_INT_STA0_ECC_ERR_DBL BIT(28) 25 + #define SUN8I_A83T_MIPI_CSI2_INT_STA0_LINE_CKSM_ERR_VC3 BIT(27) 26 + #define SUN8I_A83T_MIPI_CSI2_INT_STA0_LINE_CKSM_ERR_VC2 BIT(26) 27 + #define SUN8I_A83T_MIPI_CSI2_INT_STA0_LINE_CKSM_ERR_VC1 BIT(25) 28 + #define SUN8I_A83T_MIPI_CSI2_INT_STA0_LINE_CKSM_ERR_VC0 BIT(24) 29 + #define SUN8I_A83T_MIPI_CSI2_INT_STA0_LINE_SEQ_ERR_DT3 BIT(23) 30 + #define SUN8I_A83T_MIPI_CSI2_INT_STA0_LINE_SEQ_ERR_DT2 BIT(22) 31 + #define SUN8I_A83T_MIPI_CSI2_INT_STA0_LINE_SEQ_ERR_DT1 BIT(21) 32 + #define SUN8I_A83T_MIPI_CSI2_INT_STA0_LINE_SEQ_ERR_DT0 BIT(20) 33 + #define SUN8I_A83T_MIPI_CSI2_INT_STA0_LS_LE_ERR_DT3 BIT(19) 34 + #define SUN8I_A83T_MIPI_CSI2_INT_STA0_LS_LE_ERR_DT2 BIT(18) 35 + #define SUN8I_A83T_MIPI_CSI2_INT_STA0_LS_LE_ERR_DT1 BIT(17) 36 + #define SUN8I_A83T_MIPI_CSI2_INT_STA0_LS_LE_ERR_DT0 BIT(16) 37 + #define SUN8I_A83T_MIPI_CSI2_INT_STA0_CRC_ERR_VC3 BIT(15) 38 + #define SUN8I_A83T_MIPI_CSI2_INT_STA0_CRC_ERR_VC2 BIT(14) 39 + #define SUN8I_A83T_MIPI_CSI2_INT_STA0_CRC_ERR_VC1 BIT(13) 40 + #define SUN8I_A83T_MIPI_CSI2_INT_STA0_CRC_ERR_VC0 BIT(12) 41 + #define SUN8I_A83T_MIPI_CSI2_INT_STA0_FRM_SEQ_ERR_VC3 BIT(11) 42 + #define SUN8I_A83T_MIPI_CSI2_INT_STA0_FRM_SEQ_ERR_VC2 BIT(10) 43 + #define SUN8I_A83T_MIPI_CSI2_INT_STA0_FRM_SEQ_ERR_VC1 BIT(9) 44 + #define SUN8I_A83T_MIPI_CSI2_INT_STA0_FRM_SEQ_ERR_VC0 BIT(8) 45 + #define SUN8I_A83T_MIPI_CSI2_INT_STA0_FS_FE_ERR_VC3 BIT(7) 46 + #define SUN8I_A83T_MIPI_CSI2_INT_STA0_FS_FE_ERR_VC2 BIT(6) 47 + #define SUN8I_A83T_MIPI_CSI2_INT_STA0_FS_FE_ERR_VC1 BIT(5) 48 + #define SUN8I_A83T_MIPI_CSI2_INT_STA0_FS_FE_ERR_VC0 BIT(4) 49 + #define SUN8I_A83T_MIPI_CSI2_INT_STA0_SOT_SYNC_ERR_3 BIT(3) 50 + #define SUN8I_A83T_MIPI_CSI2_INT_STA0_SOT_SYNC_ERR_2 BIT(2) 51 + #define SUN8I_A83T_MIPI_CSI2_INT_STA0_SOT_SYNC_ERR_1 BIT(1) 52 + #define SUN8I_A83T_MIPI_CSI2_INT_STA0_SOT_SYNC_ERR_0 BIT(0) 53 + #define SUN8I_A83T_MIPI_CSI2_INT_STA1_REG 0x24 54 + #define SUN8I_A83T_MIPI_CSI2_INT_STA1_LINE_SEQ_ERR_DT7 BIT(23) 55 + #define SUN8I_A83T_MIPI_CSI2_INT_STA1_LINE_SEQ_ERR_DT6 BIT(22) 56 + #define SUN8I_A83T_MIPI_CSI2_INT_STA1_LINE_SEQ_ERR_DT5 BIT(21) 57 + #define SUN8I_A83T_MIPI_CSI2_INT_STA1_LINE_SEQ_ERR_DT4 BIT(20) 58 + #define SUN8I_A83T_MIPI_CSI2_INT_STA1_LS_LE_ERR_DT7 BIT(19) 59 + #define SUN8I_A83T_MIPI_CSI2_INT_STA1_LS_LE_ERR_DT6 BIT(18) 60 + #define SUN8I_A83T_MIPI_CSI2_INT_STA1_LS_LE_ERR_DT5 BIT(17) 61 + #define SUN8I_A83T_MIPI_CSI2_INT_STA1_LS_LE_ERR_DT4 BIT(16) 62 + #define SUN8I_A83T_MIPI_CSI2_INT_STA1_DT_ERR_VC3 BIT(15) 63 + #define SUN8I_A83T_MIPI_CSI2_INT_STA1_DT_ERR_VC2 BIT(14) 64 + #define SUN8I_A83T_MIPI_CSI2_INT_STA1_DT_ERR_VC1 BIT(13) 65 + #define SUN8I_A83T_MIPI_CSI2_INT_STA1_DT_ERR_VC0 BIT(12) 66 + #define SUN8I_A83T_MIPI_CSI2_INT_STA1_ECC_ERR1_VC3 BIT(11) 67 + #define SUN8I_A83T_MIPI_CSI2_INT_STA1_ECC_ERR1_VC2 BIT(10) 68 + #define SUN8I_A83T_MIPI_CSI2_INT_STA1_ECC_ERR1_VC1 BIT(9) 69 + #define SUN8I_A83T_MIPI_CSI2_INT_STA1_ECC_ERR1_VC0 BIT(8) 70 + #define SUN8I_A83T_MIPI_CSI2_INT_STA1_SOT_ERR_3 BIT(7) 71 + #define SUN8I_A83T_MIPI_CSI2_INT_STA1_SOT_ERR_2 BIT(6) 72 + #define SUN8I_A83T_MIPI_CSI2_INT_STA1_SOT_ERR_1 BIT(5) 73 + #define SUN8I_A83T_MIPI_CSI2_INT_STA1_SOT_ERR_0 BIT(4) 74 + #define SUN8I_A83T_MIPI_CSI2_INT_STA1_ESC_ENTRY_ERR_3 BIT(3) 75 + #define SUN8I_A83T_MIPI_CSI2_INT_STA1_ESC_ENTRY_ERR_2 BIT(2) 76 + #define SUN8I_A83T_MIPI_CSI2_INT_STA1_ESC_ENTRY_ERR_1 BIT(1) 77 + #define SUN8I_A83T_MIPI_CSI2_INT_STA1_ESC_ENTRY_ERR_0 BIT(0) 78 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK0_REG 0x28 79 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK0_ECC_ERR_DBL BIT(28) 80 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK0_CKSM_ERR_VC3 BIT(27) 81 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK0_CKSM_ERR_VC2 BIT(26) 82 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK0_CKSM_ERR_VC1 BIT(25) 83 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK0_CKSM_ERR_VC0 BIT(24) 84 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK0_LINE_SEQ_ERR_DT3 BIT(23) 85 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK0_LINE_SEQ_ERR_DT2 BIT(22) 86 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK0_LINE_SEQ_ERR_DT1 BIT(21) 87 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK0_LINE_SEQ_ERR_DT0 BIT(20) 88 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK0_LS_LE_ERR_DT3 BIT(19) 89 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK0_LS_LE_ERR_DT2 BIT(18) 90 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK0_LS_LE_ERR_DT1 BIT(17) 91 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK0_LS_LE_ERR_DT0 BIT(16) 92 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK0_CRC_ERR_VC3 BIT(15) 93 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK0_CRC_ERR_VC2 BIT(14) 94 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK0_CRC_ERR_VC1 BIT(13) 95 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK0_CRC_ERR_VC0 BIT(12) 96 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK0_FRM_SEQ_ERR_VC3 BIT(11) 97 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK0_FRM_SEQ_ERR_VC2 BIT(10) 98 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK0_FRM_SEQ_ERR_VC1 BIT(9) 99 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK0_FRM_SEQ_ERR_VC0 BIT(8) 100 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK0_FS_FE_ERR_VC3 BIT(7) 101 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK0_FS_FE_ERR_VC2 BIT(6) 102 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK0_FS_FE_ERR_VC1 BIT(5) 103 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK0_FS_FE_ERR_VC0 BIT(4) 104 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK0_SOT_SYNC_ERR_3 BIT(3) 105 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK0_SOT_SYNC_ERR_2 BIT(2) 106 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK0_SOT_SYNC_ERR_1 BIT(1) 107 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK0_SOT_SYNC_ERR_0 BIT(0) 108 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK1_REG 0x2c 109 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK1_DT_ERR_VC3 BIT(15) 110 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK1_DT_ERR_VC2 BIT(14) 111 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK1_DT_ERR_VC1 BIT(13) 112 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK1_DT_ERR_VC0 BIT(12) 113 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK1_ECC_ERR1_VC3 BIT(11) 114 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK1_ECC_ERR1_VC2 BIT(10) 115 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK1_ECC_ERR1_VC1 BIT(9) 116 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK1_ECC_ERR1_VC0 BIT(8) 117 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK1_SOT_ERR_3 BIT(7) 118 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK1_SOT_ERR_2 BIT(6) 119 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK1_SOT_ERR_1 BIT(5) 120 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK1_SOT_ERR_0 BIT(4) 121 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK1_ESC_ENTRY_ERR_3 BIT(3) 122 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK1_ESC_ENTRY_ERR_2 BIT(2) 123 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK1_ESC_ENTRY_ERR_1 BIT(1) 124 + #define SUN8I_A83T_MIPI_CSI2_INT_MSK1_ESC_ENTRY_ERR_0 BIT(0) 125 + 126 + #define SUN8I_A83T_MIPI_CSI2_CFG_REG 0x100 127 + #define SUN8I_A83T_MIPI_CSI2_CFG_INIT_VALUE 0xb8c64f24 128 + #define SUN8I_A83T_MIPI_CSI2_CFG_SYNC_EN BIT(31) 129 + #define SUN8I_A83T_MIPI_CSI2_CFG_BYPASS_ECC_EN BIT(29) 130 + #define SUN8I_A83T_MIPI_CSI2_CFG_UNPKT_EN BIT(28) 131 + #define SUN8I_A83T_MIPI_CSI2_CFG_NONE_UNPKT_RX_MODE BIT(27) 132 + #define SUN8I_A83T_MIPI_CSI2_CFG_YC_SWAB BIT(26) 133 + #define SUN8I_A83T_MIPI_CSI2_CFG_N_BYTE BIT(24) 134 + #define SUN8I_A83T_MIPI_CSI2_CFG_SYNC_DLY_CYCLE(v) (((v) << 18) & \ 135 + GENMASK(22, 18)) 136 + #define SUN8I_A83T_MIPI_CSI2_CFG_N_CHANNEL(v) ((((v) - 1) << 16) & \ 137 + GENMASK(17, 16)) 138 + #define SUN8I_A83T_MIPI_CSI2_CFG_N_LANE(v) ((((v) - 1) << 4) & \ 139 + GENMASK(5, 4)) 140 + #define SUN8I_A83T_MIPI_CSI2_VCDT0_REG 0x104 141 + #define SUN8I_A83T_MIPI_CSI2_VCDT0_CH_VC(ch, vc) (((vc) & GENMASK(1, 0)) << \ 142 + ((ch) * 8 + 6)) 143 + #define SUN8I_A83T_MIPI_CSI2_VCDT0_CH_DT(ch, t) (((t) & GENMASK(5, 0)) << \ 144 + ((ch) * 8)) 145 + #define SUN8I_A83T_MIPI_CSI2_VCDT1_REG 0x108 146 + #define SUN8I_A83T_MIPI_CSI2_VCDT1_CH_VC(ch, vc) (((vc) & GENMASK(1, 0)) << \ 147 + (((ch) - 4) * 8 + 6)) 148 + #define SUN8I_A83T_MIPI_CSI2_VCDT1_CH_DT(ch, t) (((t) & GENMASK(5, 0)) << \ 149 + (((ch) - 4) * 8)) 150 + 151 + #endif