Message ID | 20200828131737.12483-4-kevin.lhopital@bootlin.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Support of MIPI CSI-2 for A83T | expand |
Hi "Kévin, Thank you for the patch! Perhaps something to improve: [auto build test WARNING on linuxtv-media/master] [also build test WARNING on sunxi/sunxi/for-next pza/reset/next v5.9-rc2 next-20200828] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch] url: https://github.com/0day-ci/linux/commits/K-vin-L-h-pital/Support-of-MIPI-CSI-2-for-A83T/20200828-212110 base: git://linuxtv.org/media_tree.git master config: s390-allyesconfig (attached as .config) compiler: s390-linux-gcc (GCC) 9.3.0 reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # save the attached .config to linux build tree COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=s390 If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot <lkp@intel.com> All warnings (new ones prefixed by >>): >> drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.c:101:6: warning: no previous prototype for 'sun6i_mipi_csi_init' [-Wmissing-prototypes] 101 | void sun6i_mipi_csi_init(struct sun6i_csi_dev *sdev) | ^~~~~~~~~~~~~~~~~~~ # https://github.com/0day-ci/linux/commit/7e8c8e3031ceba91b0a690fe805069b7069540a3 git remote add linux-review https://github.com/0day-ci/linux git fetch --no-tags linux-review K-vin-L-h-pital/Support-of-MIPI-CSI-2-for-A83T/20200828-212110 git checkout 7e8c8e3031ceba91b0a690fe805069b7069540a3 vim +/sun6i_mipi_csi_init +101 drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.c 100 > 101 void sun6i_mipi_csi_init(struct sun6i_csi_dev *sdev) 102 { 103 regmap_update_bits(sdev->regmap, MIPI_CSI2_CTRL_REG, 104 MIPI_CSI2_CTRL_REG_RSTN, MIPI_CSI2_CTRL_REG_RSTN); 105 regmap_write(sdev->regmap, MIPI_CSI2_RX_PKT_NUM_REG, 0xb8d257f8); 106 sun6i_dphy_first_init(sdev); 107 regmap_write(sdev->regmap, MIPI_CSI2_RSVD1_REG, 108 HW_LOCK_REGISTER_VALUE_1); 109 regmap_write(sdev->regmap, MIPI_CSI2_RSVD2_REG, 110 HW_LOCK_REGISTER_VALUE_2); 111 regmap_write(sdev->regmap, MIPI_CSI2_RX_PKT_NUM_REG, 0); 112 regmap_write(sdev->regmap, MIPI_CSI2_VCDT0_REG, 0); 113 regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, 114 MIPI_CSI2_CFG_REG_SYNC_DLY_CYCLE_MASK, 115 MIPI_CSI2_CFG_REG_SYNC_DLY_CYCLE(0x11)); 116 regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, 117 MIPI_CSI2_CFG_REG_N_BYTE, 0); 118 regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, 119 MIPI_CSI2_CFG_REG_YC_SWAB, 0); 120 regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, 121 MIPI_CSI2_CFG_REG_NONE_UNPKT_RX_MODE, 122 MIPI_CSI2_CFG_REG_NONE_UNPKT_RX_MODE); 123 regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, 124 MIPI_CSI2_CFG_REG_UNPKT_EN, 125 MIPI_CSI2_CFG_REG_UNPKT_EN); 126 regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, 127 MIPI_CSI2_CFG_REG_BYPASS_ECC_EN, 128 MIPI_CSI2_CFG_REG_BYPASS_ECC_EN); 129 regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, 130 MIPI_CSI2_CFG_REG_SYNC_EN, 131 MIPI_CSI2_CFG_REG_SYNC_EN); 132 sun6i_dphy_second_init(sdev); 133 regmap_update_bits(sdev->regmap, MIPI_CSI2_CTRL_REG, 134 MIPI_CSI2_CTRL_REG_RSTN, MIPI_CSI2_CTRL_REG_RSTN); 135 regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, 136 MIPI_CSI2_CFG_REG_SYNC_DLY_CYCLE_MASK, 137 MIPI_CSI2_CFG_REG_SYNC_DLY_CYCLE(0x08)); 138 regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, 139 MIPI_CSI2_CFG_REG_NONE_UNPKT_RX_MODE, 0); 140 regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, 141 MIPI_CSI2_CFG_REG_BYPASS_ECC_EN, 0); 142 regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, 143 MIPI_CSI2_CFG_REG_SYNC_EN, 0); 144 } 145 --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
Hi Kévin, On Fri, Aug 28, 2020 at 03:17:35PM +0200, Kévin L'hôpital wrote: > This patch add the support only for the Allwinner A83T MIPI CSI-2. > Currently, the driver does not support the V3s MIPI CSI-2 controller. > On the A83T, the CSI controller is the same as the other V3s Soc, but > the MIPI CSI2 controller is not. > > It was tested with the ov8865 image sensor. > > Signed-off-by: Kévin L'hôpital <kevin.lhopital@bootlin.com> > --- > .../media/platform/sunxi/sun6i-csi/Makefile | 2 +- > .../platform/sunxi/sun6i-csi/sun6i_csi.c | 84 +++++-- > .../sunxi/sun6i-csi/sun8i_a83t_dphy.c | 39 ++++ > .../sunxi/sun6i-csi/sun8i_a83t_dphy.h | 16 ++ > .../sunxi/sun6i-csi/sun8i_a83t_dphy_reg.h | 39 ++++ > .../sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.c | 217 ++++++++++++++++++ > .../sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.h | 16 ++ > .../sun6i-csi/sun8i_a83t_mipi_csi2_reg.h | 179 +++++++++++++++ > 8 files changed, 575 insertions(+), 17 deletions(-) > create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.c > create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.h > create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy_reg.h > create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.c > create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.h > create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2_reg.h > > diff --git a/drivers/media/platform/sunxi/sun6i-csi/Makefile b/drivers/media/platform/sunxi/sun6i-csi/Makefile > index e7e315347804..0f3849790463 100644 > --- a/drivers/media/platform/sunxi/sun6i-csi/Makefile > +++ b/drivers/media/platform/sunxi/sun6i-csi/Makefile > @@ -1,4 +1,4 @@ > # SPDX-License-Identifier: GPL-2.0-only > -sun6i-csi-y += sun6i_video.o sun6i_csi.o > +sun6i-csi-y += sun6i_video.o sun6i_csi.o sun8i_a83t_mipi_csi2.o sun8i_a83t_dphy.o > > obj-$(CONFIG_VIDEO_SUN6I_CSI) += sun6i-csi.o > diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c > index 680fa31f380a..cf346e536959 100644 > --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c > +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c > @@ -26,6 +26,7 @@ > > #include "sun6i_csi.h" > #include "sun6i_csi_reg.h" > +#include "sun8i_a83t_mipi_csi2.h" > > #define MODULE_NAME "sun6i-csi" > > @@ -160,10 +161,14 @@ int sun6i_csi_set_power(struct sun6i_csi *csi, bool enable) > regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN, 0); > > clk_disable_unprepare(sdev->clk_ram); > + > if (of_device_is_compatible(dev->of_node, > "allwinner,sun50i-a64-csi")) > clk_rate_exclusive_put(sdev->clk_mod); > clk_disable_unprepare(sdev->clk_mod); > + if (csi->v4l2_ep.bus_type == V4L2_MBUS_CSI2_DPHY) > + sun6i_mipi_csi_clk_disable(csi); > + > reset_control_assert(sdev->rstc_bus); > return 0; > } > @@ -189,10 +194,18 @@ int sun6i_csi_set_power(struct sun6i_csi *csi, bool enable) > goto clk_ram_disable; > } > > + if (csi->v4l2_ep.bus_type == V4L2_MBUS_CSI2_DPHY) { > + ret = sun6i_mipi_csi_clk_enable(csi); > + if (ret) > + goto reset_control_assert; > + } > + > regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN, CSI_EN_CSI_EN); > > return 0; > > +reset_control_assert: > + reset_control_assert(sdev->rstc_bus); > clk_ram_disable: > clk_disable_unprepare(sdev->clk_ram); > clk_mod_disable: > @@ -421,27 +434,34 @@ static void sun6i_csi_setup_bus(struct sun6i_csi_dev *sdev) > if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) > cfg |= CSI_IF_CFG_CLK_POL_FALLING_EDGE; > break; > + case V4L2_MBUS_CSI2_DPHY: > + cfg |= CSI_IF_CFG_MIPI_IF_MIPI; > + sun6i_mipi_csi_setup_bus(csi); > + break; > default: > dev_warn(sdev->dev, "Unsupported bus type: %d\n", > endpoint->bus_type); > break; > } > > - switch (bus_width) { > - case 8: > - cfg |= CSI_IF_CFG_IF_DATA_WIDTH_8BIT; > - break; > - case 10: > - cfg |= CSI_IF_CFG_IF_DATA_WIDTH_10BIT; > - break; > - case 12: > - cfg |= CSI_IF_CFG_IF_DATA_WIDTH_12BIT; > - break; > - case 16: /* No need to configure DATA_WIDTH for 16bit */ > - break; > - default: > - dev_warn(sdev->dev, "Unsupported bus width: %u\n", bus_width); > - break; > + /* Bus width only applies to parallel bus. */ > + if (endpoint->bus_type != V4L2_MBUS_CSI2_DPHY) { > + switch (bus_width) { > + case 8: > + cfg |= CSI_IF_CFG_IF_DATA_WIDTH_8BIT; > + break; > + case 10: > + cfg |= CSI_IF_CFG_IF_DATA_WIDTH_10BIT; > + break; > + case 12: > + cfg |= CSI_IF_CFG_IF_DATA_WIDTH_12BIT; > + break; > + case 16: /* No need to configure DATA_WIDTH for 16bit */ > + break; > + default: > + dev_warn(sdev->dev, "Unsupported bus width: %u\n", bus_width); > + break; > + } > } > > regmap_write(sdev->regmap, CSI_IF_CFG_REG, cfg); > @@ -593,6 +613,9 @@ void sun6i_csi_set_stream(struct sun6i_csi *csi, bool enable) > struct regmap *regmap = sdev->regmap; > > if (!enable) { > + if (csi->v4l2_ep.bus_type == V4L2_MBUS_CSI2_DPHY) > + sun6i_mipi_csi_set_stream(csi, 0); > + > regmap_update_bits(regmap, CSI_CAP_REG, CSI_CAP_CH0_VCAP_ON, 0); > regmap_write(regmap, CSI_CH_INT_EN_REG, 0); > return; > @@ -609,6 +632,9 @@ void sun6i_csi_set_stream(struct sun6i_csi *csi, bool enable) > > regmap_update_bits(regmap, CSI_CAP_REG, CSI_CAP_CH0_VCAP_ON, > CSI_CAP_CH0_VCAP_ON); > + > + if (csi->v4l2_ep.bus_type == V4L2_MBUS_CSI2_DPHY) > + sun6i_mipi_csi_set_stream(csi, 1); > } > > /* ----------------------------------------------------------------------------- > @@ -685,6 +711,7 @@ static int sun6i_csi_fwnode_parse(struct device *dev, > struct v4l2_async_subdev *asd) > { > struct sun6i_csi *csi = dev_get_drvdata(dev); > + struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi); > > if (vep->base.port || vep->base.id) { > dev_warn(dev, "Only support a single port with one endpoint\n"); > @@ -692,6 +719,17 @@ static int sun6i_csi_fwnode_parse(struct device *dev, > } > > switch (vep->bus_type) { > + case V4L2_MBUS_CSI2_DPHY: > + if (!sdev->clk_mipi) { > + dev_err(sdev->dev, "Use MIPI-CSI2 device with no MIPI clock\n"); > + return -ENOTCONN; > + } > + if (!sdev->clk_misc) { > + dev_err(sdev->dev, "Use MIPI-CSI2 device with no misc clock\n"); > + return -ENOTCONN; > + } > + csi->v4l2_ep = *vep; > + return 0; > case V4L2_MBUS_PARALLEL: > case V4L2_MBUS_BT656: > csi->v4l2_ep = *vep; > @@ -812,12 +850,13 @@ static const struct regmap_config sun6i_csi_regmap_config = { > .reg_bits = 32, > .reg_stride = 4, > .val_bits = 32, > - .max_register = 0x9c, > + .max_register = 0x2000, > }; > > static int sun6i_csi_resource_request(struct sun6i_csi_dev *sdev, > struct platform_device *pdev) > { > + struct device *dev = sdev->dev; > struct resource *res; > void __iomem *io_base; > int ret; > @@ -847,6 +886,19 @@ static int sun6i_csi_resource_request(struct sun6i_csi_dev *sdev, > return PTR_ERR(sdev->clk_ram); > } > > + if (of_device_is_compatible(dev->of_node, "allwinner,sun8i-a83t-csi")) { It'd be cleaner to rely on of_device_get_match_data() attached to a compatible string instead. > + sdev->clk_mipi = devm_clk_get(&pdev->dev, "mipi"); > + if (IS_ERR(sdev->clk_mipi)) { > + sdev->clk_mipi = NULL; > + dev_warn(&pdev->dev, "Unable to acquire mipi clock. No mipi support\n"); > + } > + > + sdev->clk_misc = devm_clk_get(&pdev->dev, "misc"); > + if (IS_ERR(sdev->clk_misc)) { > + sdev->clk_misc = NULL; > + dev_warn(&pdev->dev, "Unable to acquire misc clock. No mipi support\n"); > + } > + } > sdev->rstc_bus = devm_reset_control_get_shared(&pdev->dev, NULL); > if (IS_ERR(sdev->rstc_bus)) { > dev_err(&pdev->dev, "Cannot get reset controller\n"); > diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.c b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.c > new file mode 100644 > index 000000000000..bb9599c3bde9 > --- /dev/null > +++ b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.c > @@ -0,0 +1,39 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * sun6i_dphy.c > + * Copyright Kévin L'hôpital (C) 2020 > + */ > + > +#include "sun8i_a83t_dphy.h" > +#include "sun8i_a83t_dphy_reg.h" > + > +/* First initialization to turn on the dphy for the MIPI CSI2 controller > + * initialization. > + */ > + > +void sun6i_dphy_first_init(struct sun6i_csi_dev *sdev) > +{ > + regmap_update_bits(sdev->regmap, DPHY_CTRL_REG, DPHY_CTRL_REG_DBG, > + DPHY_CTRL_REG_DBG); > + regmap_update_bits(sdev->regmap, DPHY_CTRL_REG, DPHY_CTRL_REG_SHUT, 0); > + regmap_update_bits(sdev->regmap, DPHY_CTRL_REG, DPHY_CTRL_REG_RSTN, > + DPHY_CTRL_REG_RSTN); > +} > + > +/* Second initialization to turn off the dphy and do its initialization. */ > +void sun6i_dphy_second_init(struct sun6i_csi_dev *sdev) > +{ > + regmap_update_bits(sdev->regmap, DPHY_CTRL_REG, DPHY_CTRL_REG_DBG, 0); > + regmap_update_bits(sdev->regmap, DPHY_CTRL_REG, DPHY_CTRL_REG_SHUT, > + DPHY_CTRL_REG_SHUT); > + regmap_update_bits(sdev->regmap, DPHY_CTRL_REG, DPHY_CTRL_REG_RSTN, > + DPHY_CTRL_REG_RSTN); > + regmap_update_bits(sdev->regmap, DPHY_ANA0_REG, DPHY_ANA0_REG_SNK_MASK, > + DPHY_ANA0_REG_SNK(0x02)); > + regmap_update_bits(sdev->regmap, DPHY_ANA0_REG, DPHY_ANA0_REG_RINT_MASK, > + DPHY_ANA0_REG_RINT(0x02)); > + regmap_update_bits(sdev->regmap, DPHY_ANA0_REG, DPHY_ANA0_REG_REXT, 0); > + regmap_update_bits(sdev->regmap, DPHY_ANA0_REG, DPHY_ANA0_REG_ENREXT, > + DPHY_ANA0_REG_ENREXT); > +} > + > diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.h b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.h > new file mode 100644 > index 000000000000..f776ed098cb3 > --- /dev/null > +++ b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.h > @@ -0,0 +1,16 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * sun6i_dphy.h > + * Copyright Kévin L'hôpital (C) 2020 > + */ > + > +#ifndef __SUN8I_A83T_DPHY_H__ > +#define __SUN8I_A83T_DPHY_H__ > + > +#include <linux/regmap.h> > +#include "sun6i_csi.h" > + > +void sun6i_dphy_first_init(struct sun6i_csi_dev *sdev); > +void sun6i_dphy_second_init(struct sun6i_csi_dev *sdev); > + > +#endif /* __SUN8I_A83T_DPHY_H__ */ > diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy_reg.h b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy_reg.h > new file mode 100644 > index 000000000000..815692b112d2 > --- /dev/null > +++ b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy_reg.h > @@ -0,0 +1,39 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Allwinner A83t DPHY register description > + * Copyright Kévin L'hôpital (C) 2020 > + */ > + > +#ifndef __SUN8I_A83T_DPHY_REG_H__ > +#define __SUN8I_A83T_DPHY_REG_H__ > + > + > +#define DPHY_OFFSET 0x1000 > + > +#define DPHY_CTRL_REG (DPHY_OFFSET + 0x010) > +#define DPHY_CTRL_REG_RSTN BIT(31) > +#define DPHY_CTRL_REG_SHUT BIT(15) > +#define DPHY_CTRL_REG_DBG BIT(8) > + > +#define DPHY_STATUS_REG (DPHY_OFFSET + 0x014) > +#define DPHY_STATUS_REG_CLK_STOP BIT(10) > +#define DPHY_STATUS_REG_CLK_UPLS BIT(9) > +#define DPHY_STATUS_REG_HSCLK BIT(8) > +#define DPHY_STATUS_REG_D3_STOP BIT(7) > +#define DPHY_STATUS_REG_D2_STOP BIT(6) > +#define DPHY_STATUS_REG_D1_STOP BIT(5) > +#define DPHY_STATUS_REG_D0_STOP BIT(4) > +#define DPHY_STATUS_REG_D3_UPLS BIT(3) > +#define DPHY_STATUS_REG_D2_UPLS BIT(2) > +#define DPHY_STATUS_REG_D1_UPLS BIT(1) > +#define DPHY_STATUS_REG_D0_UPLS BIT(0) > + > +#define DPHY_ANA0_REG (DPHY_OFFSET + 0x030) > +#define DPHY_ANA0_REG_ENREXT BIT(31) > +#define DPHY_ANA0_REG_REXT BIT(30) > +#define DPHY_ANA0_REG_RINT_MASK GENMASK(29, 28) > +#define DPHY_ANA0_REG_RINT(v) ((v) << 28) > +#define DPHY_ANA0_REG_SNK_MASK GENMASK(22, 20) > +#define DPHY_ANA0_REG_SNK(v) ((v) << 20) > + > +#endif /* __SUN8I_A83T_DPHY_REG_H__ */ > diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.c b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.c > new file mode 100644 > index 000000000000..2933238cbc95 > --- /dev/null > +++ b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.c > @@ -0,0 +1,217 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Allwinner A83t MIPI Camera Sensor Interface 2 driver > + * Copyright Kévin L'hôpital (C) 2020 > + */ > + > +#include <linux/clk.h> > +#include "sun8i_a83t_mipi_csi2.h" > +#include "sun8i_a83t_mipi_csi2_reg.h" > +#include "sun8i_a83t_dphy.h" > +#include <linux/delay.h> > + > +#define IS_FLAG(x, y) (((x) & (y)) == y) > + > +enum mipi_csi2_pkt_fmt { > + MIPI_FS = 0X00, > + MIPI_FE = 0X01, > + MIPI_LS = 0X02, > + MIPI_LE = 0X03, > + MIPI_SDAT0 = 0X08, > + MIPI_SDAT1 = 0X09, > + MIPI_SDAT2 = 0X0A, > + MIPI_SDAT3 = 0X0B, > + MIPI_SDAT4 = 0X0C, > + MIPI_SDAT5 = 0X0D, > + MIPI_SDAT6 = 0X0E, > + MIPI_SDAT7 = 0X0F, > + MIPI_BLK = 0X11, > + MIPI_EMBD = 0X12, > + MIPI_YUV420 = 0X18, > + MIPI_YUV420_10 = 0X19, > + MIPI_YUV420_CSP = 0X1C, > + MIPI_YUV420_CSP_10 = 0X1D, > + MIPI_YUV422 = 0X1E, > + MIPI_YUV422_10 = 0X1F, > + MIPI_RGB565 = 0X22, > + MIPI_RGB888 = 0X24, > + MIPI_RAW8 = 0X2A, > + MIPI_RAW10 = 0X2B, > + MIPI_RAW12 = 0X2C, > + MIPI_USR_DAT0 = 0X30, > + MIPI_USR_DAT1 = 0X31, > + MIPI_USR_DAT2 = 0X32, > + MIPI_USR_DAT3 = 0X33, > + MIPI_USR_DAT4 = 0X34, > + MIPI_USR_DAT5 = 0X35, > + MIPI_USR_DAT6 = 0X36, > + MIPI_USR_DAT7 = 0X37, > +}; > + > +static inline struct sun6i_csi_dev *sun6i_csi_to_dev(struct sun6i_csi *csi) > +{ > + return container_of(csi, struct sun6i_csi_dev, csi); > +} > + > +static enum mipi_csi2_pkt_fmt get_pkt_fmt(u16 bus_pix_code) > +{ > + switch (bus_pix_code) { > + case MEDIA_BUS_FMT_RGB565_1X16: > + return MIPI_RGB565; > + case MEDIA_BUS_FMT_UYVY8_2X8: > + case MEDIA_BUS_FMT_UYVY8_1X16: > + return MIPI_YUV422; > + case MEDIA_BUS_FMT_UYVY10_2X10: > + return MIPI_YUV422_10; > + case MEDIA_BUS_FMT_RGB888_1X24: > + return MIPI_RGB888; > + case MEDIA_BUS_FMT_SBGGR8_1X8: > + case MEDIA_BUS_FMT_SGBRG8_1X8: > + case MEDIA_BUS_FMT_SGRBG8_1X8: > + case MEDIA_BUS_FMT_SRGGB8_1X8: > + return MIPI_RAW8; > + case MEDIA_BUS_FMT_SBGGR10_1X10: > + case MEDIA_BUS_FMT_SGBRG10_1X10: > + case MEDIA_BUS_FMT_SGRBG10_1X10: > + case MEDIA_BUS_FMT_SRGGB10_1X10: > + return MIPI_RAW10; > + case MEDIA_BUS_FMT_SBGGR12_1X12: > + case MEDIA_BUS_FMT_SGBRG12_1X12: > + case MEDIA_BUS_FMT_SGRBG12_1X12: > + case MEDIA_BUS_FMT_SRGGB12_1X12: > + return MIPI_RAW12; > + default: > + return MIPI_RAW8; > + } > +} > + > +void sun6i_mipi_csi_set_stream(struct sun6i_csi *csi, bool enable) > +{ > + struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi); > + > + if (enable) > + regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, > + MIPI_CSI2_CFG_REG_SYNC_EN, > + MIPI_CSI2_CFG_REG_SYNC_EN); > + else > + regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, > + MIPI_CSI2_CFG_REG_SYNC_EN, 0); > +} > + > +void sun6i_mipi_csi_init(struct sun6i_csi_dev *sdev) > +{ > + regmap_update_bits(sdev->regmap, MIPI_CSI2_CTRL_REG, > + MIPI_CSI2_CTRL_REG_RSTN, MIPI_CSI2_CTRL_REG_RSTN); > + regmap_write(sdev->regmap, MIPI_CSI2_RX_PKT_NUM_REG, 0xb8d257f8); > + sun6i_dphy_first_init(sdev); > + regmap_write(sdev->regmap, MIPI_CSI2_RSVD1_REG, > + HW_LOCK_REGISTER_VALUE_1); > + regmap_write(sdev->regmap, MIPI_CSI2_RSVD2_REG, > + HW_LOCK_REGISTER_VALUE_2); > + regmap_write(sdev->regmap, MIPI_CSI2_RX_PKT_NUM_REG, 0); > + regmap_write(sdev->regmap, MIPI_CSI2_VCDT0_REG, 0); > + regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, > + MIPI_CSI2_CFG_REG_SYNC_DLY_CYCLE_MASK, > + MIPI_CSI2_CFG_REG_SYNC_DLY_CYCLE(0x11)); > + regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, > + MIPI_CSI2_CFG_REG_N_BYTE, 0); > + regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, > + MIPI_CSI2_CFG_REG_YC_SWAB, 0); > + regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, > + MIPI_CSI2_CFG_REG_NONE_UNPKT_RX_MODE, > + MIPI_CSI2_CFG_REG_NONE_UNPKT_RX_MODE); > + regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, > + MIPI_CSI2_CFG_REG_UNPKT_EN, > + MIPI_CSI2_CFG_REG_UNPKT_EN); > + regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, > + MIPI_CSI2_CFG_REG_BYPASS_ECC_EN, > + MIPI_CSI2_CFG_REG_BYPASS_ECC_EN); > + regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, > + MIPI_CSI2_CFG_REG_SYNC_EN, > + MIPI_CSI2_CFG_REG_SYNC_EN); > + sun6i_dphy_second_init(sdev); > + regmap_update_bits(sdev->regmap, MIPI_CSI2_CTRL_REG, > + MIPI_CSI2_CTRL_REG_RSTN, MIPI_CSI2_CTRL_REG_RSTN); > + regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, > + MIPI_CSI2_CFG_REG_SYNC_DLY_CYCLE_MASK, > + MIPI_CSI2_CFG_REG_SYNC_DLY_CYCLE(0x08)); > + regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, > + MIPI_CSI2_CFG_REG_NONE_UNPKT_RX_MODE, 0); > + regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, > + MIPI_CSI2_CFG_REG_BYPASS_ECC_EN, 0); > + regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, > + MIPI_CSI2_CFG_REG_SYNC_EN, 0); > +} > + > +void sun6i_mipi_csi_setup_bus(struct sun6i_csi *csi) > +{ > + struct v4l2_fwnode_endpoint *endpoint = &csi->v4l2_ep; > + struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi); > + int lane_num = endpoint->bus.mipi_csi2.num_data_lanes; > + int flags = endpoint->bus.mipi_csi2.flags; > + int total_rx_ch = 0; > + int vc; > + > + sun6i_mipi_csi_init(sdev); > + > + if (IS_FLAG(flags, V4L2_MBUS_CSI2_CHANNEL_0)) { > + vc = 0; > + total_rx_ch++; > + } > + > + if (!total_rx_ch) { > + dev_dbg(sdev->dev, > + "No receive channel assigned, using channel 0.\n"); > + vc = 0; > + total_rx_ch++; > + } > + /* Set lane. */ > + regmap_write_bits(sdev->regmap, MIPI_CSI2_CFG_REG, > + MIPI_CSI2_CFG_REG_N_LANE_MASK, (lane_num - 1) << > + MIPI_CSI2_CFG_REG_N_LANE_SHIFT); > + /* Set total channels. */ > + regmap_write_bits(sdev->regmap, MIPI_CSI2_CFG_REG, > + MIPI_CSI2_CFG_REG_N_CHANNEL_MASK, (total_rx_ch - 1) << > + MIPI_CSI2_CFG_REG_N_CHANNEL_SHIFT); > + > + regmap_write_bits(sdev->regmap, MIPI_CSI2_VCDT0_REG, > + MIPI_CSI2_VCDT0_REG_CH0_DT_MASK, > + get_pkt_fmt(csi->config.code)); > + regmap_write_bits(sdev->regmap, MIPI_CSI2_VCDT0_REG, > + MIPI_CSI2_VCDT0_REG_CH0_VC_MASK, > + vc << MIPI_CSI2_VCDT0_REG_CH0_VC_SHIFT); > +} > + > +int sun6i_mipi_csi_clk_enable(struct sun6i_csi *csi) > +{ > + struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi); > + int ret; > + > + ret = clk_prepare_enable(sdev->clk_mipi); > + if (ret) { > + dev_err(sdev->dev, "Enable clk_mipi clk err %d\n", ret); > + return ret; > + } > + > + ret = clk_prepare_enable(sdev->clk_misc); > + if (ret) { > + dev_err(sdev->dev, "Enable clk_misc clk err %d\n", ret); > + goto clk_mipi_disable; > + } > + > + return 0; > + > +clk_mipi_disable: > + clk_disable_unprepare(sdev->clk_mipi); > + return ret; > +} > + > +void sun6i_mipi_csi_clk_disable(struct sun6i_csi *csi) > +{ > + struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi); > + > + clk_disable_unprepare(sdev->clk_misc); > + clk_disable_unprepare(sdev->clk_mipi); > +} > + > + > diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.h b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.h > new file mode 100644 > index 000000000000..a94c69ccee39 > --- /dev/null > +++ b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.h > @@ -0,0 +1,16 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright Kévin L'hôpital (C) 2020 > + */ > + > +#ifndef __SUN8I_A83T_MIPI_CSI2_H__ > +#define __SUN8I_A83T_MIPI_CSI2_H__ > +#include <linux/regmap.h> > +#include "sun6i_csi.h" > + > +void sun6i_mipi_csi_set_stream(struct sun6i_csi *csi, bool enable); > +void sun6i_mipi_csi_setup_bus(struct sun6i_csi *csi); > +int sun6i_mipi_csi_clk_enable(struct sun6i_csi *csi); > +void sun6i_mipi_csi_clk_disable(struct sun6i_csi *csi); > + > +#endif /* __SUN8I_A83T_MIPI_CSI2_H__ */ > diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2_reg.h b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2_reg.h > new file mode 100644 > index 000000000000..43cc46ea1aec > --- /dev/null > +++ b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2_reg.h > @@ -0,0 +1,179 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Allwinner A83t MIPI CSI-2 register description > + * Copyright Kévin L'hôpital (C) 2020 > + */ > + > +#ifndef __SUN8I_A83T_MIPI_CSI2_REG_H__ > +#define __SUN8I_A83T_MIPI_CSI2_REG_H__ > + > + > +#define MIPI_CSI2_OFFSET 0x1000 > + > +#define MIPI_CSI2_VERSION_REG (MIPI_CSI2_OFFSET + 0x000) > +#define MIPI_CSI2_CTRL_REG (MIPI_CSI2_OFFSET + 0x004) > +#define MIPI_CSI2_CTRL_REG_RSTN BIT(31) > + > +#define MIPI_CSI2_RX_PKT_NUM_REG (MIPI_CSI2_OFFSET + 0x008) > +#define MIPI_CSI2_RSVD0_REG (MIPI_CSI2_OFFSET + 0x00c) > + > +#define MIPI_CSI2_RSVD1_REG (MIPI_CSI2_OFFSET + 0x018) > +/* Value found in the BSP and need to be present but it is not describe in the > + * datasheet. > + */ > +#define HW_LOCK_REGISTER_VALUE_1 0xb8c8a30c > +#define MIPI_CSI2_RSVD2_REG (MIPI_CSI2_OFFSET + 0x01c) > +/* Value found in the BSP and need to be present but it is not describe in the > + * datasheet. > + */ > +#define HW_LOCK_REGISTER_VALUE_2 0xb8df8ad7 > + > +#define MIPI_CSI2_INT_STA0_REG (MIPI_CSI2_OFFSET + 0x020) > +#define MIPI_CSI2_INT_STA0_REG_ECC_ERR_DBL BIT(28) > +#define MIPI_CSI2_INT_STA0_REG_LINE_CKSM_ERR_VC3 BIT(27) > +#define MIPI_CSI2_INT_STA0_REG_LINE_CKSM_ERR_VC2 BIT(26) > +#define MIPI_CSI2_INT_STA0_REG_LINE_CKSM_ERR_VC1 BIT(25) > +#define MIPI_CSI2_INT_STA0_REG_LINE_CKSM_ERR_VC0 BIT(24) > +#define MIPI_CSI2_INT_STA0_REG_LINE_SEQ_ERR_DT3 BIT(23) > +#define MIPI_CSI2_INT_STA0_REG_LINE_SEQ_ERR_DT2 BIT(22) > +#define MIPI_CSI2_INT_STA0_REG_LINE_SEQ_ERR_DT1 BIT(21) > +#define MIPI_CSI2_INT_STA0_REG_LINE_SEQ_ERR_DT0 BIT(20) > +#define MIPI_CSI2_INT_STA0_REG_LS_LE_ERR_DT3 BIT(19) > +#define MIPI_CSI2_INT_STA0_REG_LS_LE_ERR_DT2 BIT(18) > +#define MIPI_CSI2_INT_STA0_REG_LS_LE_ERR_DT1 BIT(17) > +#define MIPI_CSI2_INT_STA0_REG_LS_LE_ERR_DT0 BIT(16) > +#define MIPI_CSI2_INT_STA0_REG_CRC_ERR_VC3 BIT(15) > +#define MIPI_CSI2_INT_STA0_REG_CRC_ERR_VC2 BIT(14) > +#define MIPI_CSI2_INT_STA0_REG_CRC_ERR_VC1 BIT(13) > +#define MIPI_CSI2_INT_STA0_REG_CRC_ERR_VC0 BIT(12) > +#define MIPI_CSI2_INT_STA0_REG_FRM_SEQ_ERR_VC3 BIT(11) > +#define MIPI_CSI2_INT_STA0_REG_FRM_SEQ_ERR_VC2 BIT(10) > +#define MIPI_CSI2_INT_STA0_REG_FRM_SEQ_ERR_VC1 BIT(9) > +#define MIPI_CSI2_INT_STA0_REG_FRM_SEQ_ERR_VC0 BIT(8) > +#define MIPI_CSI2_INT_STA0_REG_FS_FE_ERR_VC3 BIT(7) > +#define MIPI_CSI2_INT_STA0_REG_FS_FE_ERR_VC2 BIT(6) > +#define MIPI_CSI2_INT_STA0_REG_FS_FE_ERR_VC1 BIT(5) > +#define MIPI_CSI2_INT_STA0_REG_FS_FE_ERR_VC0 BIT(4) > +#define MIPI_CSI2_INT_STA0_REG_SOT_SYNC_ERR_3 BIT(3) > +#define MIPI_CSI2_INT_STA0_REG_SOT_SYNC_ERR_2 BIT(2) > +#define MIPI_CSI2_INT_STA0_REG_SOT_SYNC_ERR_1 BIT(1) > +#define MIPI_CSI2_INT_STA0_REG_SOT_SYNC_ERR_0 BIT(0) > + > +#define MIPI_CSI2_INT_STA1_REG (MIPI_CSI2_OFFSET + 0x024) > +#define MIPI_CSI2_INT_STA1_REG_LINE_SEQ_ERR_DT7 BIT(23) > +#define MIPI_CSI2_INT_STA1_REG_LINE_SEQ_ERR_DT6 BIT(22) > +#define MIPI_CSI2_INT_STA1_REG_LINE_SEQ_ERR_DT5 BIT(21) > +#define MIPI_CSI2_INT_STA1_REG_LINE_SEQ_ERR_DT4 BIT(20) > +#define MIPI_CSI2_INT_STA1_REG_LS_LE_ERR_DT7 BIT(19) > +#define MIPI_CSI2_INT_STA1_REG_LS_LE_ERR_DT6 BIT(18) > +#define MIPI_CSI2_INT_STA1_REG_LS_LE_ERR_DT5 BIT(17) > +#define MIPI_CSI2_INT_STA1_REG_LS_LE_ERR_DT4 BIT(16) > +#define MIPI_CSI2_INT_STA1_REG_DT_ERR_VC3 BIT(15) > +#define MIPI_CSI2_INT_STA1_REG_DT_ERR_VC2 BIT(14) > +#define MIPI_CSI2_INT_STA1_REG_DT_ERR_VC1 BIT(13) > +#define MIPI_CSI2_INT_STA1_REG_DT_ERR_VC0 BIT(12) > +#define MIPI_CSI2_INT_STA1_REG_ECC_ERR1_VC3 BIT(11) > +#define MIPI_CSI2_INT_STA1_REG_ECC_ERR1_VC2 BIT(10) > +#define MIPI_CSI2_INT_STA1_REG_ECC_ERR1_VC1 BIT(9) > +#define MIPI_CSI2_INT_STA1_REG_ECC_ERR1_VC0 BIT(8) > +#define MIPI_CSI2_INT_STA1_REG_SOT_ERR_3 BIT(7) > +#define MIPI_CSI2_INT_STA1_REG_SOT_ERR_2 BIT(6) > +#define MIPI_CSI2_INT_STA1_REG_SOT_ERR_1 BIT(5) > +#define MIPI_CSI2_INT_STA1_REG_SOT_ERR_0 BIT(4) > +#define MIPI_CSI2_INT_STA1_REG_ESC_ENTRY_ERR_3 BIT(3) > +#define MIPI_CSI2_INT_STA1_REG_ESC_ENTRY_ERR_2 BIT(2) > +#define MIPI_CSI2_INT_STA1_REG_ESC_ENTRY_ERR_1 BIT(1) > +#define MIPI_CSI2_INT_STA1_REG_ESC_ENTRY_ERR_0 BIT(0) > + > +#define MIPI_CSI2_INT_MSK0_REG (MIPI_CSI2_OFFSET + 0x028) > +#define MIPI_CSI2_INT_MSK0_REG_ECC_ERR_DBL_MSK BIT(28) > +#define MIPI_CSI2_INT_MSK0_REG_CKSM_ERR_VC3_MSK BIT(27) > +#define MIPI_CSI2_INT_MSK0_REG_CKSM_ERR_VC2_MSK BIT(26) > +#define MIPI_CSI2_INT_MSK0_REG_CKSM_ERR_VC1_MSK BIT(25) > +#define MIPI_CSI2_INT_MSK0_REG_CKSM_ERR_VC0_MSK BIT(24) > +#define MIPI_CSI2_INT_MSK0_REG_LINE_SEQ_ERR_DT3_MSK BIT(23) > +#define MIPI_CSI2_INT_MSK0_REG_LINE_SEQ_ERR_DT2_MSK BIT(22) > +#define MIPI_CSI2_INT_MSK0_REG_LINE_SEQ_ERR_DT1_MSK BIT(21) > +#define MIPI_CSI2_INT_MSK0_REG_LINE_SEQ_ERR_DT0_MSK BIT(20) > +#define MIPI_CSI2_INT_MSK0_REG_LS_LE_ERR_DT3_MSK BIT(19) > +#define MIPI_CSI2_INT_MSK0_REG_LS_LE_ERR_DT2_MSK BIT(18) > +#define MIPI_CSI2_INT_MSK0_REG_LS_LE_ERR_DT1_MSK BIT(17) > +#define MIPI_CSI2_INT_MSK0_REG_LS_LE_ERR_DT0_MSK BIT(16) > +#define MIPI_CSI2_INT_MSK0_REG_CRC_ERR_VC3_MSK BIT(15) > +#define MIPI_CSI2_INT_MSK0_REG_CRC_ERR_VC2_MSK BIT(14) > +#define MIPI_CSI2_INT_MSK0_REG_CRC_ERR_VC1_MSK BIT(13) > +#define MIPI_CSI2_INT_MSK0_REG_CRC_ERR_VC0_MSK BIT(12) > +#define MIPI_CSI2_INT_MSK0_REG_FRM_SEQ_ERR_VC3_MSK BIT(11) > +#define MIPI_CSI2_INT_MSK0_REG_FRM_SEQ_ERR_VC2_MSK BIT(10) > +#define MIPI_CSI2_INT_MSK0_REG_FRM_SEQ_ERR_VC1_MSK BIT(9) > +#define MIPI_CSI2_INT_MSK0_REG_FRM_SEQ_ERR_VC0_MSK BIT(8) > +#define MIPI_CSI2_INT_MSK0_REG_FS_FE_ERR_VC3_MSK BIT(7) > +#define MIPI_CSI2_INT_MSK0_REG_FS_FE_ERR_VC2_MSK BIT(6) > +#define MIPI_CSI2_INT_MSK0_REG_FS_FE_ERR_VC1_MSK BIT(5) > +#define MIPI_CSI2_INT_MSK0_REG_FS_FE_ERR_VC0_MSK BIT(4) > +#define MIPI_CSI2_INT_MSK0_REG_SOT_SYNC_ERR_3_MSK BIT(3) > +#define MIPI_CSI2_INT_MSK0_REG_SOT_SYNC_ERR_2_MSK BIT(2) > +#define MIPI_CSI2_INT_MSK0_REG_SOT_SYNC_ERR_1_MSK BIT(1) > +#define MIPI_CSI2_INT_MSK0_REG_SOT_SYNC_ERR_0_MSK BIT(0) > + > +#define MIPI_CSI2_INT_MSK1_REG (MIPI_CSI2_OFFSET + 0x02c) > +#define MIPI_CSI2_INT_MSK1_REG_DT_ERR_VC3_MSK BIT(15) > +#define MIPI_CSI2_INT_MSK1_REG_DT_ERR_VC2_MSK BIT(14) > +#define MIPI_CSI2_INT_MSK1_REG_DT_ERR_VC1_MSK BIT(13) > +#define MIPI_CSI2_INT_MSK1_REG_DT_ERR_VC0_MSK BIT(12) > +#define MIPI_CSI2_INT_MSK1_REG_ECC_ERR1_VC3_MSK BIT(11) > +#define MIPI_CSI2_INT_MSK1_REG_ECC_ERR1_VC2_MSK BIT(10) > +#define MIPI_CSI2_INT_MSK1_REG_ECC_ERR1_VC1_MSK BIT(9) > +#define MIPI_CSI2_INT_MSK1_REG_ECC_ERR1_VC0_MSK BIT(8) > +#define MIPI_CSI2_INT_MSK1_REG_SOT_ERR_3_MSK BIT(7) > +#define MIPI_CSI2_INT_MSK1_REG_SOT_ERR_2_MSK BIT(6) > +#define MIPI_CSI2_INT_MSK1_REG_SOT_ERR_1_MSK BIT(5) > +#define MIPI_CSI2_INT_MSK1_REG_SOT_ERR_0_MSK BIT(4) > +#define MIPI_CSI2_INT_MSK1_REG_ESC_ENTRY_ERR_3_MSK BIT(3) > +#define MIPI_CSI2_INT_MSK1_REG_ESC_ENTRY_ERR_2_MSK BIT(2) > +#define MIPI_CSI2_INT_MSK1_REG_ESC_ENTRY_ERR_1_MSK BIT(1) > +#define MIPI_CSI2_INT_MSK1_REG_ESC_ENTRY_ERR_0_MSK BIT(0) > + > +#define MIPI_CSI2_CFG_REG (MIPI_CSI2_OFFSET + 0x100) > +#define MIPI_CSI2_CFG_REG_SYNC_EN BIT(31) > +#define MIPI_CSI2_CFG_REG_BYPASS_ECC_EN BIT(29) > +#define MIPI_CSI2_CFG_REG_UNPKT_EN BIT(28) > +#define MIPI_CSI2_CFG_REG_NONE_UNPKT_RX_MODE BIT(27) > +#define MIPI_CSI2_CFG_REG_YC_SWAB BIT(26) > +#define MIPI_CSI2_CFG_REG_N_BYTE BIT(24) > +#define MIPI_CSI2_CFG_REG_SYNC_DLY_CYCLE(v) ((v) << 18) > +#define MIPI_CSI2_CFG_REG_SYNC_DLY_CYCLE_MASK GENMASK(22, 18) > +#define MIPI_CSI2_CFG_REG_N_CHANNEL_MASK GENMASK(17, 16) > +#define MIPI_CSI2_CFG_REG_N_CHANNEL_SHIFT 16 > +#define MIPI_CSI2_CFG_REG_N_LANE_MASK GENMASK(5, 4) > +#define MIPI_CSI2_CFG_REG_N_LANE_SHIFT 4 > + > +#define MIPI_CSI2_VCDT0_REG (MIPI_CSI2_OFFSET + 0x104) > +#define MIPI_CSI2_VCDT0_REG_DEFAULT 0xc0804000 > +#define MIPI_CSI2_VCDT0_REG_CH3_VC_MASK GENMASK(31, 30) > +#define MIPI_CSI2_VCDT0_REG_CH3_VC_SHIFT 30 > +#define MIPI_CSI2_VCDT0_REG_CH3_DT_MASK GENMASK(29, 24) > +#define MIPI_CSI2_VCDT0_REG_CH3_DT_SHIFT 24 > +#define MIPI_CSI2_VCDT0_REG_CH2_VC_MASK GENMASK(23, 22) > +#define MIPI_CSI2_VCDT0_REG_CH2_VC_SHIFT 22 > +#define MIPI_CSI2_VCDT0_REG_CH2_DT_MASK GENMASK(21, 16) > +#define MIPI_CSI2_VCDT0_REG_CH2_DT_SHIFT 16 > +#define MIPI_CSI2_VCDT0_REG_CH1_VC_MASK GENMASK(15, 14) > +#define MIPI_CSI2_VCDT0_REG_CH1_VC_SHIFT 14 > +#define MIPI_CSI2_VCDT0_REG_CH1_DT_MASK GENMASK(13, 8) > +#define MIPI_CSI2_VCDT0_REG_CH1_DT_SHIFT 8 > +#define MIPI_CSI2_VCDT0_REG_CH0_VC_MASK GENMASK(7, 6) > +#define MIPI_CSI2_VCDT0_REG_CH0_VC_SHIFT 6 > +#define MIPI_CSI2_VCDT0_REG_CH0_DT_MASK GENMASK(5, 0) > + > +#define MIPI_CSI2_VCDT1_REG (MIPI_CSI2_OFFSET + 0x108) > +#define MIPI_CSI2_VCDT1_REG_CH7_VC_MASK GENMASK(31, 30) > +#define MIPI_CSI2_VCDT1_REG_CH7_DT_MASK GENMASK(29, 24) > +#define MIPI_CSI2_VCDT1_REG_CH6_VC_MASK GENMASK(23, 22) > +#define MIPI_CSI2_VCDT1_REG_CH6_DT_MASK GENMASK(21, 16) > +#define MIPI_CSI2_VCDT1_REG_CH5_VC_MASK GENMASK(15, 14) > +#define MIPI_CSI2_VCDT1_REG_CH5_DT_MASK GENMASK(13, 8) > +#define MIPI_CSI2_VCDT1_REG_CH4_VC_MASK GENMASK(7, 6) > +#define MIPI_CSI2_VCDT1_REG_CH4_DT_MASK GENMASK(5, 0) > + > +#endif /* __SUN8I_A83T_MIPI_CSI2_REG_H__ */
diff --git a/drivers/media/platform/sunxi/sun6i-csi/Makefile b/drivers/media/platform/sunxi/sun6i-csi/Makefile index e7e315347804..0f3849790463 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/Makefile +++ b/drivers/media/platform/sunxi/sun6i-csi/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only -sun6i-csi-y += sun6i_video.o sun6i_csi.o +sun6i-csi-y += sun6i_video.o sun6i_csi.o sun8i_a83t_mipi_csi2.o sun8i_a83t_dphy.o obj-$(CONFIG_VIDEO_SUN6I_CSI) += sun6i-csi.o diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c index 680fa31f380a..cf346e536959 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c @@ -26,6 +26,7 @@ #include "sun6i_csi.h" #include "sun6i_csi_reg.h" +#include "sun8i_a83t_mipi_csi2.h" #define MODULE_NAME "sun6i-csi" @@ -160,10 +161,14 @@ int sun6i_csi_set_power(struct sun6i_csi *csi, bool enable) regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN, 0); clk_disable_unprepare(sdev->clk_ram); + if (of_device_is_compatible(dev->of_node, "allwinner,sun50i-a64-csi")) clk_rate_exclusive_put(sdev->clk_mod); clk_disable_unprepare(sdev->clk_mod); + if (csi->v4l2_ep.bus_type == V4L2_MBUS_CSI2_DPHY) + sun6i_mipi_csi_clk_disable(csi); + reset_control_assert(sdev->rstc_bus); return 0; } @@ -189,10 +194,18 @@ int sun6i_csi_set_power(struct sun6i_csi *csi, bool enable) goto clk_ram_disable; } + if (csi->v4l2_ep.bus_type == V4L2_MBUS_CSI2_DPHY) { + ret = sun6i_mipi_csi_clk_enable(csi); + if (ret) + goto reset_control_assert; + } + regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN, CSI_EN_CSI_EN); return 0; +reset_control_assert: + reset_control_assert(sdev->rstc_bus); clk_ram_disable: clk_disable_unprepare(sdev->clk_ram); clk_mod_disable: @@ -421,27 +434,34 @@ static void sun6i_csi_setup_bus(struct sun6i_csi_dev *sdev) if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) cfg |= CSI_IF_CFG_CLK_POL_FALLING_EDGE; break; + case V4L2_MBUS_CSI2_DPHY: + cfg |= CSI_IF_CFG_MIPI_IF_MIPI; + sun6i_mipi_csi_setup_bus(csi); + break; default: dev_warn(sdev->dev, "Unsupported bus type: %d\n", endpoint->bus_type); break; } - switch (bus_width) { - case 8: - cfg |= CSI_IF_CFG_IF_DATA_WIDTH_8BIT; - break; - case 10: - cfg |= CSI_IF_CFG_IF_DATA_WIDTH_10BIT; - break; - case 12: - cfg |= CSI_IF_CFG_IF_DATA_WIDTH_12BIT; - break; - case 16: /* No need to configure DATA_WIDTH for 16bit */ - break; - default: - dev_warn(sdev->dev, "Unsupported bus width: %u\n", bus_width); - break; + /* Bus width only applies to parallel bus. */ + if (endpoint->bus_type != V4L2_MBUS_CSI2_DPHY) { + switch (bus_width) { + case 8: + cfg |= CSI_IF_CFG_IF_DATA_WIDTH_8BIT; + break; + case 10: + cfg |= CSI_IF_CFG_IF_DATA_WIDTH_10BIT; + break; + case 12: + cfg |= CSI_IF_CFG_IF_DATA_WIDTH_12BIT; + break; + case 16: /* No need to configure DATA_WIDTH for 16bit */ + break; + default: + dev_warn(sdev->dev, "Unsupported bus width: %u\n", bus_width); + break; + } } regmap_write(sdev->regmap, CSI_IF_CFG_REG, cfg); @@ -593,6 +613,9 @@ void sun6i_csi_set_stream(struct sun6i_csi *csi, bool enable) struct regmap *regmap = sdev->regmap; if (!enable) { + if (csi->v4l2_ep.bus_type == V4L2_MBUS_CSI2_DPHY) + sun6i_mipi_csi_set_stream(csi, 0); + regmap_update_bits(regmap, CSI_CAP_REG, CSI_CAP_CH0_VCAP_ON, 0); regmap_write(regmap, CSI_CH_INT_EN_REG, 0); return; @@ -609,6 +632,9 @@ void sun6i_csi_set_stream(struct sun6i_csi *csi, bool enable) regmap_update_bits(regmap, CSI_CAP_REG, CSI_CAP_CH0_VCAP_ON, CSI_CAP_CH0_VCAP_ON); + + if (csi->v4l2_ep.bus_type == V4L2_MBUS_CSI2_DPHY) + sun6i_mipi_csi_set_stream(csi, 1); } /* ----------------------------------------------------------------------------- @@ -685,6 +711,7 @@ static int sun6i_csi_fwnode_parse(struct device *dev, struct v4l2_async_subdev *asd) { struct sun6i_csi *csi = dev_get_drvdata(dev); + struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi); if (vep->base.port || vep->base.id) { dev_warn(dev, "Only support a single port with one endpoint\n"); @@ -692,6 +719,17 @@ static int sun6i_csi_fwnode_parse(struct device *dev, } switch (vep->bus_type) { + case V4L2_MBUS_CSI2_DPHY: + if (!sdev->clk_mipi) { + dev_err(sdev->dev, "Use MIPI-CSI2 device with no MIPI clock\n"); + return -ENOTCONN; + } + if (!sdev->clk_misc) { + dev_err(sdev->dev, "Use MIPI-CSI2 device with no misc clock\n"); + return -ENOTCONN; + } + csi->v4l2_ep = *vep; + return 0; case V4L2_MBUS_PARALLEL: case V4L2_MBUS_BT656: csi->v4l2_ep = *vep; @@ -812,12 +850,13 @@ static const struct regmap_config sun6i_csi_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, - .max_register = 0x9c, + .max_register = 0x2000, }; static int sun6i_csi_resource_request(struct sun6i_csi_dev *sdev, struct platform_device *pdev) { + struct device *dev = sdev->dev; struct resource *res; void __iomem *io_base; int ret; @@ -847,6 +886,19 @@ static int sun6i_csi_resource_request(struct sun6i_csi_dev *sdev, return PTR_ERR(sdev->clk_ram); } + if (of_device_is_compatible(dev->of_node, "allwinner,sun8i-a83t-csi")) { + sdev->clk_mipi = devm_clk_get(&pdev->dev, "mipi"); + if (IS_ERR(sdev->clk_mipi)) { + sdev->clk_mipi = NULL; + dev_warn(&pdev->dev, "Unable to acquire mipi clock. No mipi support\n"); + } + + sdev->clk_misc = devm_clk_get(&pdev->dev, "misc"); + if (IS_ERR(sdev->clk_misc)) { + sdev->clk_misc = NULL; + dev_warn(&pdev->dev, "Unable to acquire misc clock. No mipi support\n"); + } + } sdev->rstc_bus = devm_reset_control_get_shared(&pdev->dev, NULL); if (IS_ERR(sdev->rstc_bus)) { dev_err(&pdev->dev, "Cannot get reset controller\n"); diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.c b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.c new file mode 100644 index 000000000000..bb9599c3bde9 --- /dev/null +++ b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * sun6i_dphy.c + * Copyright Kévin L'hôpital (C) 2020 + */ + +#include "sun8i_a83t_dphy.h" +#include "sun8i_a83t_dphy_reg.h" + +/* First initialization to turn on the dphy for the MIPI CSI2 controller + * initialization. + */ + +void sun6i_dphy_first_init(struct sun6i_csi_dev *sdev) +{ + regmap_update_bits(sdev->regmap, DPHY_CTRL_REG, DPHY_CTRL_REG_DBG, + DPHY_CTRL_REG_DBG); + regmap_update_bits(sdev->regmap, DPHY_CTRL_REG, DPHY_CTRL_REG_SHUT, 0); + regmap_update_bits(sdev->regmap, DPHY_CTRL_REG, DPHY_CTRL_REG_RSTN, + DPHY_CTRL_REG_RSTN); +} + +/* Second initialization to turn off the dphy and do its initialization. */ +void sun6i_dphy_second_init(struct sun6i_csi_dev *sdev) +{ + regmap_update_bits(sdev->regmap, DPHY_CTRL_REG, DPHY_CTRL_REG_DBG, 0); + regmap_update_bits(sdev->regmap, DPHY_CTRL_REG, DPHY_CTRL_REG_SHUT, + DPHY_CTRL_REG_SHUT); + regmap_update_bits(sdev->regmap, DPHY_CTRL_REG, DPHY_CTRL_REG_RSTN, + DPHY_CTRL_REG_RSTN); + regmap_update_bits(sdev->regmap, DPHY_ANA0_REG, DPHY_ANA0_REG_SNK_MASK, + DPHY_ANA0_REG_SNK(0x02)); + regmap_update_bits(sdev->regmap, DPHY_ANA0_REG, DPHY_ANA0_REG_RINT_MASK, + DPHY_ANA0_REG_RINT(0x02)); + regmap_update_bits(sdev->regmap, DPHY_ANA0_REG, DPHY_ANA0_REG_REXT, 0); + regmap_update_bits(sdev->regmap, DPHY_ANA0_REG, DPHY_ANA0_REG_ENREXT, + DPHY_ANA0_REG_ENREXT); +} + diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.h b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.h new file mode 100644 index 000000000000..f776ed098cb3 --- /dev/null +++ b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * sun6i_dphy.h + * Copyright Kévin L'hôpital (C) 2020 + */ + +#ifndef __SUN8I_A83T_DPHY_H__ +#define __SUN8I_A83T_DPHY_H__ + +#include <linux/regmap.h> +#include "sun6i_csi.h" + +void sun6i_dphy_first_init(struct sun6i_csi_dev *sdev); +void sun6i_dphy_second_init(struct sun6i_csi_dev *sdev); + +#endif /* __SUN8I_A83T_DPHY_H__ */ diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy_reg.h b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy_reg.h new file mode 100644 index 000000000000..815692b112d2 --- /dev/null +++ b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy_reg.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Allwinner A83t DPHY register description + * Copyright Kévin L'hôpital (C) 2020 + */ + +#ifndef __SUN8I_A83T_DPHY_REG_H__ +#define __SUN8I_A83T_DPHY_REG_H__ + + +#define DPHY_OFFSET 0x1000 + +#define DPHY_CTRL_REG (DPHY_OFFSET + 0x010) +#define DPHY_CTRL_REG_RSTN BIT(31) +#define DPHY_CTRL_REG_SHUT BIT(15) +#define DPHY_CTRL_REG_DBG BIT(8) + +#define DPHY_STATUS_REG (DPHY_OFFSET + 0x014) +#define DPHY_STATUS_REG_CLK_STOP BIT(10) +#define DPHY_STATUS_REG_CLK_UPLS BIT(9) +#define DPHY_STATUS_REG_HSCLK BIT(8) +#define DPHY_STATUS_REG_D3_STOP BIT(7) +#define DPHY_STATUS_REG_D2_STOP BIT(6) +#define DPHY_STATUS_REG_D1_STOP BIT(5) +#define DPHY_STATUS_REG_D0_STOP BIT(4) +#define DPHY_STATUS_REG_D3_UPLS BIT(3) +#define DPHY_STATUS_REG_D2_UPLS BIT(2) +#define DPHY_STATUS_REG_D1_UPLS BIT(1) +#define DPHY_STATUS_REG_D0_UPLS BIT(0) + +#define DPHY_ANA0_REG (DPHY_OFFSET + 0x030) +#define DPHY_ANA0_REG_ENREXT BIT(31) +#define DPHY_ANA0_REG_REXT BIT(30) +#define DPHY_ANA0_REG_RINT_MASK GENMASK(29, 28) +#define DPHY_ANA0_REG_RINT(v) ((v) << 28) +#define DPHY_ANA0_REG_SNK_MASK GENMASK(22, 20) +#define DPHY_ANA0_REG_SNK(v) ((v) << 20) + +#endif /* __SUN8I_A83T_DPHY_REG_H__ */ diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.c b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.c new file mode 100644 index 000000000000..2933238cbc95 --- /dev/null +++ b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.c @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Allwinner A83t MIPI Camera Sensor Interface 2 driver + * Copyright Kévin L'hôpital (C) 2020 + */ + +#include <linux/clk.h> +#include "sun8i_a83t_mipi_csi2.h" +#include "sun8i_a83t_mipi_csi2_reg.h" +#include "sun8i_a83t_dphy.h" +#include <linux/delay.h> + +#define IS_FLAG(x, y) (((x) & (y)) == y) + +enum mipi_csi2_pkt_fmt { + MIPI_FS = 0X00, + MIPI_FE = 0X01, + MIPI_LS = 0X02, + MIPI_LE = 0X03, + MIPI_SDAT0 = 0X08, + MIPI_SDAT1 = 0X09, + MIPI_SDAT2 = 0X0A, + MIPI_SDAT3 = 0X0B, + MIPI_SDAT4 = 0X0C, + MIPI_SDAT5 = 0X0D, + MIPI_SDAT6 = 0X0E, + MIPI_SDAT7 = 0X0F, + MIPI_BLK = 0X11, + MIPI_EMBD = 0X12, + MIPI_YUV420 = 0X18, + MIPI_YUV420_10 = 0X19, + MIPI_YUV420_CSP = 0X1C, + MIPI_YUV420_CSP_10 = 0X1D, + MIPI_YUV422 = 0X1E, + MIPI_YUV422_10 = 0X1F, + MIPI_RGB565 = 0X22, + MIPI_RGB888 = 0X24, + MIPI_RAW8 = 0X2A, + MIPI_RAW10 = 0X2B, + MIPI_RAW12 = 0X2C, + MIPI_USR_DAT0 = 0X30, + MIPI_USR_DAT1 = 0X31, + MIPI_USR_DAT2 = 0X32, + MIPI_USR_DAT3 = 0X33, + MIPI_USR_DAT4 = 0X34, + MIPI_USR_DAT5 = 0X35, + MIPI_USR_DAT6 = 0X36, + MIPI_USR_DAT7 = 0X37, +}; + +static inline struct sun6i_csi_dev *sun6i_csi_to_dev(struct sun6i_csi *csi) +{ + return container_of(csi, struct sun6i_csi_dev, csi); +} + +static enum mipi_csi2_pkt_fmt get_pkt_fmt(u16 bus_pix_code) +{ + switch (bus_pix_code) { + case MEDIA_BUS_FMT_RGB565_1X16: + return MIPI_RGB565; + case MEDIA_BUS_FMT_UYVY8_2X8: + case MEDIA_BUS_FMT_UYVY8_1X16: + return MIPI_YUV422; + case MEDIA_BUS_FMT_UYVY10_2X10: + return MIPI_YUV422_10; + case MEDIA_BUS_FMT_RGB888_1X24: + return MIPI_RGB888; + case MEDIA_BUS_FMT_SBGGR8_1X8: + case MEDIA_BUS_FMT_SGBRG8_1X8: + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SRGGB8_1X8: + return MIPI_RAW8; + case MEDIA_BUS_FMT_SBGGR10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SRGGB10_1X10: + return MIPI_RAW10; + case MEDIA_BUS_FMT_SBGGR12_1X12: + case MEDIA_BUS_FMT_SGBRG12_1X12: + case MEDIA_BUS_FMT_SGRBG12_1X12: + case MEDIA_BUS_FMT_SRGGB12_1X12: + return MIPI_RAW12; + default: + return MIPI_RAW8; + } +} + +void sun6i_mipi_csi_set_stream(struct sun6i_csi *csi, bool enable) +{ + struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi); + + if (enable) + regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, + MIPI_CSI2_CFG_REG_SYNC_EN, + MIPI_CSI2_CFG_REG_SYNC_EN); + else + regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, + MIPI_CSI2_CFG_REG_SYNC_EN, 0); +} + +void sun6i_mipi_csi_init(struct sun6i_csi_dev *sdev) +{ + regmap_update_bits(sdev->regmap, MIPI_CSI2_CTRL_REG, + MIPI_CSI2_CTRL_REG_RSTN, MIPI_CSI2_CTRL_REG_RSTN); + regmap_write(sdev->regmap, MIPI_CSI2_RX_PKT_NUM_REG, 0xb8d257f8); + sun6i_dphy_first_init(sdev); + regmap_write(sdev->regmap, MIPI_CSI2_RSVD1_REG, + HW_LOCK_REGISTER_VALUE_1); + regmap_write(sdev->regmap, MIPI_CSI2_RSVD2_REG, + HW_LOCK_REGISTER_VALUE_2); + regmap_write(sdev->regmap, MIPI_CSI2_RX_PKT_NUM_REG, 0); + regmap_write(sdev->regmap, MIPI_CSI2_VCDT0_REG, 0); + regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, + MIPI_CSI2_CFG_REG_SYNC_DLY_CYCLE_MASK, + MIPI_CSI2_CFG_REG_SYNC_DLY_CYCLE(0x11)); + regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, + MIPI_CSI2_CFG_REG_N_BYTE, 0); + regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, + MIPI_CSI2_CFG_REG_YC_SWAB, 0); + regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, + MIPI_CSI2_CFG_REG_NONE_UNPKT_RX_MODE, + MIPI_CSI2_CFG_REG_NONE_UNPKT_RX_MODE); + regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, + MIPI_CSI2_CFG_REG_UNPKT_EN, + MIPI_CSI2_CFG_REG_UNPKT_EN); + regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, + MIPI_CSI2_CFG_REG_BYPASS_ECC_EN, + MIPI_CSI2_CFG_REG_BYPASS_ECC_EN); + regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, + MIPI_CSI2_CFG_REG_SYNC_EN, + MIPI_CSI2_CFG_REG_SYNC_EN); + sun6i_dphy_second_init(sdev); + regmap_update_bits(sdev->regmap, MIPI_CSI2_CTRL_REG, + MIPI_CSI2_CTRL_REG_RSTN, MIPI_CSI2_CTRL_REG_RSTN); + regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, + MIPI_CSI2_CFG_REG_SYNC_DLY_CYCLE_MASK, + MIPI_CSI2_CFG_REG_SYNC_DLY_CYCLE(0x08)); + regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, + MIPI_CSI2_CFG_REG_NONE_UNPKT_RX_MODE, 0); + regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, + MIPI_CSI2_CFG_REG_BYPASS_ECC_EN, 0); + regmap_update_bits(sdev->regmap, MIPI_CSI2_CFG_REG, + MIPI_CSI2_CFG_REG_SYNC_EN, 0); +} + +void sun6i_mipi_csi_setup_bus(struct sun6i_csi *csi) +{ + struct v4l2_fwnode_endpoint *endpoint = &csi->v4l2_ep; + struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi); + int lane_num = endpoint->bus.mipi_csi2.num_data_lanes; + int flags = endpoint->bus.mipi_csi2.flags; + int total_rx_ch = 0; + int vc; + + sun6i_mipi_csi_init(sdev); + + if (IS_FLAG(flags, V4L2_MBUS_CSI2_CHANNEL_0)) { + vc = 0; + total_rx_ch++; + } + + if (!total_rx_ch) { + dev_dbg(sdev->dev, + "No receive channel assigned, using channel 0.\n"); + vc = 0; + total_rx_ch++; + } + /* Set lane. */ + regmap_write_bits(sdev->regmap, MIPI_CSI2_CFG_REG, + MIPI_CSI2_CFG_REG_N_LANE_MASK, (lane_num - 1) << + MIPI_CSI2_CFG_REG_N_LANE_SHIFT); + /* Set total channels. */ + regmap_write_bits(sdev->regmap, MIPI_CSI2_CFG_REG, + MIPI_CSI2_CFG_REG_N_CHANNEL_MASK, (total_rx_ch - 1) << + MIPI_CSI2_CFG_REG_N_CHANNEL_SHIFT); + + regmap_write_bits(sdev->regmap, MIPI_CSI2_VCDT0_REG, + MIPI_CSI2_VCDT0_REG_CH0_DT_MASK, + get_pkt_fmt(csi->config.code)); + regmap_write_bits(sdev->regmap, MIPI_CSI2_VCDT0_REG, + MIPI_CSI2_VCDT0_REG_CH0_VC_MASK, + vc << MIPI_CSI2_VCDT0_REG_CH0_VC_SHIFT); +} + +int sun6i_mipi_csi_clk_enable(struct sun6i_csi *csi) +{ + struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi); + int ret; + + ret = clk_prepare_enable(sdev->clk_mipi); + if (ret) { + dev_err(sdev->dev, "Enable clk_mipi clk err %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(sdev->clk_misc); + if (ret) { + dev_err(sdev->dev, "Enable clk_misc clk err %d\n", ret); + goto clk_mipi_disable; + } + + return 0; + +clk_mipi_disable: + clk_disable_unprepare(sdev->clk_mipi); + return ret; +} + +void sun6i_mipi_csi_clk_disable(struct sun6i_csi *csi) +{ + struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi); + + clk_disable_unprepare(sdev->clk_misc); + clk_disable_unprepare(sdev->clk_mipi); +} + + diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.h b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.h new file mode 100644 index 000000000000..a94c69ccee39 --- /dev/null +++ b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright Kévin L'hôpital (C) 2020 + */ + +#ifndef __SUN8I_A83T_MIPI_CSI2_H__ +#define __SUN8I_A83T_MIPI_CSI2_H__ +#include <linux/regmap.h> +#include "sun6i_csi.h" + +void sun6i_mipi_csi_set_stream(struct sun6i_csi *csi, bool enable); +void sun6i_mipi_csi_setup_bus(struct sun6i_csi *csi); +int sun6i_mipi_csi_clk_enable(struct sun6i_csi *csi); +void sun6i_mipi_csi_clk_disable(struct sun6i_csi *csi); + +#endif /* __SUN8I_A83T_MIPI_CSI2_H__ */ diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2_reg.h b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2_reg.h new file mode 100644 index 000000000000..43cc46ea1aec --- /dev/null +++ b/drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2_reg.h @@ -0,0 +1,179 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Allwinner A83t MIPI CSI-2 register description + * Copyright Kévin L'hôpital (C) 2020 + */ + +#ifndef __SUN8I_A83T_MIPI_CSI2_REG_H__ +#define __SUN8I_A83T_MIPI_CSI2_REG_H__ + + +#define MIPI_CSI2_OFFSET 0x1000 + +#define MIPI_CSI2_VERSION_REG (MIPI_CSI2_OFFSET + 0x000) +#define MIPI_CSI2_CTRL_REG (MIPI_CSI2_OFFSET + 0x004) +#define MIPI_CSI2_CTRL_REG_RSTN BIT(31) + +#define MIPI_CSI2_RX_PKT_NUM_REG (MIPI_CSI2_OFFSET + 0x008) +#define MIPI_CSI2_RSVD0_REG (MIPI_CSI2_OFFSET + 0x00c) + +#define MIPI_CSI2_RSVD1_REG (MIPI_CSI2_OFFSET + 0x018) +/* Value found in the BSP and need to be present but it is not describe in the + * datasheet. + */ +#define HW_LOCK_REGISTER_VALUE_1 0xb8c8a30c +#define MIPI_CSI2_RSVD2_REG (MIPI_CSI2_OFFSET + 0x01c) +/* Value found in the BSP and need to be present but it is not describe in the + * datasheet. + */ +#define HW_LOCK_REGISTER_VALUE_2 0xb8df8ad7 + +#define MIPI_CSI2_INT_STA0_REG (MIPI_CSI2_OFFSET + 0x020) +#define MIPI_CSI2_INT_STA0_REG_ECC_ERR_DBL BIT(28) +#define MIPI_CSI2_INT_STA0_REG_LINE_CKSM_ERR_VC3 BIT(27) +#define MIPI_CSI2_INT_STA0_REG_LINE_CKSM_ERR_VC2 BIT(26) +#define MIPI_CSI2_INT_STA0_REG_LINE_CKSM_ERR_VC1 BIT(25) +#define MIPI_CSI2_INT_STA0_REG_LINE_CKSM_ERR_VC0 BIT(24) +#define MIPI_CSI2_INT_STA0_REG_LINE_SEQ_ERR_DT3 BIT(23) +#define MIPI_CSI2_INT_STA0_REG_LINE_SEQ_ERR_DT2 BIT(22) +#define MIPI_CSI2_INT_STA0_REG_LINE_SEQ_ERR_DT1 BIT(21) +#define MIPI_CSI2_INT_STA0_REG_LINE_SEQ_ERR_DT0 BIT(20) +#define MIPI_CSI2_INT_STA0_REG_LS_LE_ERR_DT3 BIT(19) +#define MIPI_CSI2_INT_STA0_REG_LS_LE_ERR_DT2 BIT(18) +#define MIPI_CSI2_INT_STA0_REG_LS_LE_ERR_DT1 BIT(17) +#define MIPI_CSI2_INT_STA0_REG_LS_LE_ERR_DT0 BIT(16) +#define MIPI_CSI2_INT_STA0_REG_CRC_ERR_VC3 BIT(15) +#define MIPI_CSI2_INT_STA0_REG_CRC_ERR_VC2 BIT(14) +#define MIPI_CSI2_INT_STA0_REG_CRC_ERR_VC1 BIT(13) +#define MIPI_CSI2_INT_STA0_REG_CRC_ERR_VC0 BIT(12) +#define MIPI_CSI2_INT_STA0_REG_FRM_SEQ_ERR_VC3 BIT(11) +#define MIPI_CSI2_INT_STA0_REG_FRM_SEQ_ERR_VC2 BIT(10) +#define MIPI_CSI2_INT_STA0_REG_FRM_SEQ_ERR_VC1 BIT(9) +#define MIPI_CSI2_INT_STA0_REG_FRM_SEQ_ERR_VC0 BIT(8) +#define MIPI_CSI2_INT_STA0_REG_FS_FE_ERR_VC3 BIT(7) +#define MIPI_CSI2_INT_STA0_REG_FS_FE_ERR_VC2 BIT(6) +#define MIPI_CSI2_INT_STA0_REG_FS_FE_ERR_VC1 BIT(5) +#define MIPI_CSI2_INT_STA0_REG_FS_FE_ERR_VC0 BIT(4) +#define MIPI_CSI2_INT_STA0_REG_SOT_SYNC_ERR_3 BIT(3) +#define MIPI_CSI2_INT_STA0_REG_SOT_SYNC_ERR_2 BIT(2) +#define MIPI_CSI2_INT_STA0_REG_SOT_SYNC_ERR_1 BIT(1) +#define MIPI_CSI2_INT_STA0_REG_SOT_SYNC_ERR_0 BIT(0) + +#define MIPI_CSI2_INT_STA1_REG (MIPI_CSI2_OFFSET + 0x024) +#define MIPI_CSI2_INT_STA1_REG_LINE_SEQ_ERR_DT7 BIT(23) +#define MIPI_CSI2_INT_STA1_REG_LINE_SEQ_ERR_DT6 BIT(22) +#define MIPI_CSI2_INT_STA1_REG_LINE_SEQ_ERR_DT5 BIT(21) +#define MIPI_CSI2_INT_STA1_REG_LINE_SEQ_ERR_DT4 BIT(20) +#define MIPI_CSI2_INT_STA1_REG_LS_LE_ERR_DT7 BIT(19) +#define MIPI_CSI2_INT_STA1_REG_LS_LE_ERR_DT6 BIT(18) +#define MIPI_CSI2_INT_STA1_REG_LS_LE_ERR_DT5 BIT(17) +#define MIPI_CSI2_INT_STA1_REG_LS_LE_ERR_DT4 BIT(16) +#define MIPI_CSI2_INT_STA1_REG_DT_ERR_VC3 BIT(15) +#define MIPI_CSI2_INT_STA1_REG_DT_ERR_VC2 BIT(14) +#define MIPI_CSI2_INT_STA1_REG_DT_ERR_VC1 BIT(13) +#define MIPI_CSI2_INT_STA1_REG_DT_ERR_VC0 BIT(12) +#define MIPI_CSI2_INT_STA1_REG_ECC_ERR1_VC3 BIT(11) +#define MIPI_CSI2_INT_STA1_REG_ECC_ERR1_VC2 BIT(10) +#define MIPI_CSI2_INT_STA1_REG_ECC_ERR1_VC1 BIT(9) +#define MIPI_CSI2_INT_STA1_REG_ECC_ERR1_VC0 BIT(8) +#define MIPI_CSI2_INT_STA1_REG_SOT_ERR_3 BIT(7) +#define MIPI_CSI2_INT_STA1_REG_SOT_ERR_2 BIT(6) +#define MIPI_CSI2_INT_STA1_REG_SOT_ERR_1 BIT(5) +#define MIPI_CSI2_INT_STA1_REG_SOT_ERR_0 BIT(4) +#define MIPI_CSI2_INT_STA1_REG_ESC_ENTRY_ERR_3 BIT(3) +#define MIPI_CSI2_INT_STA1_REG_ESC_ENTRY_ERR_2 BIT(2) +#define MIPI_CSI2_INT_STA1_REG_ESC_ENTRY_ERR_1 BIT(1) +#define MIPI_CSI2_INT_STA1_REG_ESC_ENTRY_ERR_0 BIT(0) + +#define MIPI_CSI2_INT_MSK0_REG (MIPI_CSI2_OFFSET + 0x028) +#define MIPI_CSI2_INT_MSK0_REG_ECC_ERR_DBL_MSK BIT(28) +#define MIPI_CSI2_INT_MSK0_REG_CKSM_ERR_VC3_MSK BIT(27) +#define MIPI_CSI2_INT_MSK0_REG_CKSM_ERR_VC2_MSK BIT(26) +#define MIPI_CSI2_INT_MSK0_REG_CKSM_ERR_VC1_MSK BIT(25) +#define MIPI_CSI2_INT_MSK0_REG_CKSM_ERR_VC0_MSK BIT(24) +#define MIPI_CSI2_INT_MSK0_REG_LINE_SEQ_ERR_DT3_MSK BIT(23) +#define MIPI_CSI2_INT_MSK0_REG_LINE_SEQ_ERR_DT2_MSK BIT(22) +#define MIPI_CSI2_INT_MSK0_REG_LINE_SEQ_ERR_DT1_MSK BIT(21) +#define MIPI_CSI2_INT_MSK0_REG_LINE_SEQ_ERR_DT0_MSK BIT(20) +#define MIPI_CSI2_INT_MSK0_REG_LS_LE_ERR_DT3_MSK BIT(19) +#define MIPI_CSI2_INT_MSK0_REG_LS_LE_ERR_DT2_MSK BIT(18) +#define MIPI_CSI2_INT_MSK0_REG_LS_LE_ERR_DT1_MSK BIT(17) +#define MIPI_CSI2_INT_MSK0_REG_LS_LE_ERR_DT0_MSK BIT(16) +#define MIPI_CSI2_INT_MSK0_REG_CRC_ERR_VC3_MSK BIT(15) +#define MIPI_CSI2_INT_MSK0_REG_CRC_ERR_VC2_MSK BIT(14) +#define MIPI_CSI2_INT_MSK0_REG_CRC_ERR_VC1_MSK BIT(13) +#define MIPI_CSI2_INT_MSK0_REG_CRC_ERR_VC0_MSK BIT(12) +#define MIPI_CSI2_INT_MSK0_REG_FRM_SEQ_ERR_VC3_MSK BIT(11) +#define MIPI_CSI2_INT_MSK0_REG_FRM_SEQ_ERR_VC2_MSK BIT(10) +#define MIPI_CSI2_INT_MSK0_REG_FRM_SEQ_ERR_VC1_MSK BIT(9) +#define MIPI_CSI2_INT_MSK0_REG_FRM_SEQ_ERR_VC0_MSK BIT(8) +#define MIPI_CSI2_INT_MSK0_REG_FS_FE_ERR_VC3_MSK BIT(7) +#define MIPI_CSI2_INT_MSK0_REG_FS_FE_ERR_VC2_MSK BIT(6) +#define MIPI_CSI2_INT_MSK0_REG_FS_FE_ERR_VC1_MSK BIT(5) +#define MIPI_CSI2_INT_MSK0_REG_FS_FE_ERR_VC0_MSK BIT(4) +#define MIPI_CSI2_INT_MSK0_REG_SOT_SYNC_ERR_3_MSK BIT(3) +#define MIPI_CSI2_INT_MSK0_REG_SOT_SYNC_ERR_2_MSK BIT(2) +#define MIPI_CSI2_INT_MSK0_REG_SOT_SYNC_ERR_1_MSK BIT(1) +#define MIPI_CSI2_INT_MSK0_REG_SOT_SYNC_ERR_0_MSK BIT(0) + +#define MIPI_CSI2_INT_MSK1_REG (MIPI_CSI2_OFFSET + 0x02c) +#define MIPI_CSI2_INT_MSK1_REG_DT_ERR_VC3_MSK BIT(15) +#define MIPI_CSI2_INT_MSK1_REG_DT_ERR_VC2_MSK BIT(14) +#define MIPI_CSI2_INT_MSK1_REG_DT_ERR_VC1_MSK BIT(13) +#define MIPI_CSI2_INT_MSK1_REG_DT_ERR_VC0_MSK BIT(12) +#define MIPI_CSI2_INT_MSK1_REG_ECC_ERR1_VC3_MSK BIT(11) +#define MIPI_CSI2_INT_MSK1_REG_ECC_ERR1_VC2_MSK BIT(10) +#define MIPI_CSI2_INT_MSK1_REG_ECC_ERR1_VC1_MSK BIT(9) +#define MIPI_CSI2_INT_MSK1_REG_ECC_ERR1_VC0_MSK BIT(8) +#define MIPI_CSI2_INT_MSK1_REG_SOT_ERR_3_MSK BIT(7) +#define MIPI_CSI2_INT_MSK1_REG_SOT_ERR_2_MSK BIT(6) +#define MIPI_CSI2_INT_MSK1_REG_SOT_ERR_1_MSK BIT(5) +#define MIPI_CSI2_INT_MSK1_REG_SOT_ERR_0_MSK BIT(4) +#define MIPI_CSI2_INT_MSK1_REG_ESC_ENTRY_ERR_3_MSK BIT(3) +#define MIPI_CSI2_INT_MSK1_REG_ESC_ENTRY_ERR_2_MSK BIT(2) +#define MIPI_CSI2_INT_MSK1_REG_ESC_ENTRY_ERR_1_MSK BIT(1) +#define MIPI_CSI2_INT_MSK1_REG_ESC_ENTRY_ERR_0_MSK BIT(0) + +#define MIPI_CSI2_CFG_REG (MIPI_CSI2_OFFSET + 0x100) +#define MIPI_CSI2_CFG_REG_SYNC_EN BIT(31) +#define MIPI_CSI2_CFG_REG_BYPASS_ECC_EN BIT(29) +#define MIPI_CSI2_CFG_REG_UNPKT_EN BIT(28) +#define MIPI_CSI2_CFG_REG_NONE_UNPKT_RX_MODE BIT(27) +#define MIPI_CSI2_CFG_REG_YC_SWAB BIT(26) +#define MIPI_CSI2_CFG_REG_N_BYTE BIT(24) +#define MIPI_CSI2_CFG_REG_SYNC_DLY_CYCLE(v) ((v) << 18) +#define MIPI_CSI2_CFG_REG_SYNC_DLY_CYCLE_MASK GENMASK(22, 18) +#define MIPI_CSI2_CFG_REG_N_CHANNEL_MASK GENMASK(17, 16) +#define MIPI_CSI2_CFG_REG_N_CHANNEL_SHIFT 16 +#define MIPI_CSI2_CFG_REG_N_LANE_MASK GENMASK(5, 4) +#define MIPI_CSI2_CFG_REG_N_LANE_SHIFT 4 + +#define MIPI_CSI2_VCDT0_REG (MIPI_CSI2_OFFSET + 0x104) +#define MIPI_CSI2_VCDT0_REG_DEFAULT 0xc0804000 +#define MIPI_CSI2_VCDT0_REG_CH3_VC_MASK GENMASK(31, 30) +#define MIPI_CSI2_VCDT0_REG_CH3_VC_SHIFT 30 +#define MIPI_CSI2_VCDT0_REG_CH3_DT_MASK GENMASK(29, 24) +#define MIPI_CSI2_VCDT0_REG_CH3_DT_SHIFT 24 +#define MIPI_CSI2_VCDT0_REG_CH2_VC_MASK GENMASK(23, 22) +#define MIPI_CSI2_VCDT0_REG_CH2_VC_SHIFT 22 +#define MIPI_CSI2_VCDT0_REG_CH2_DT_MASK GENMASK(21, 16) +#define MIPI_CSI2_VCDT0_REG_CH2_DT_SHIFT 16 +#define MIPI_CSI2_VCDT0_REG_CH1_VC_MASK GENMASK(15, 14) +#define MIPI_CSI2_VCDT0_REG_CH1_VC_SHIFT 14 +#define MIPI_CSI2_VCDT0_REG_CH1_DT_MASK GENMASK(13, 8) +#define MIPI_CSI2_VCDT0_REG_CH1_DT_SHIFT 8 +#define MIPI_CSI2_VCDT0_REG_CH0_VC_MASK GENMASK(7, 6) +#define MIPI_CSI2_VCDT0_REG_CH0_VC_SHIFT 6 +#define MIPI_CSI2_VCDT0_REG_CH0_DT_MASK GENMASK(5, 0) + +#define MIPI_CSI2_VCDT1_REG (MIPI_CSI2_OFFSET + 0x108) +#define MIPI_CSI2_VCDT1_REG_CH7_VC_MASK GENMASK(31, 30) +#define MIPI_CSI2_VCDT1_REG_CH7_DT_MASK GENMASK(29, 24) +#define MIPI_CSI2_VCDT1_REG_CH6_VC_MASK GENMASK(23, 22) +#define MIPI_CSI2_VCDT1_REG_CH6_DT_MASK GENMASK(21, 16) +#define MIPI_CSI2_VCDT1_REG_CH5_VC_MASK GENMASK(15, 14) +#define MIPI_CSI2_VCDT1_REG_CH5_DT_MASK GENMASK(13, 8) +#define MIPI_CSI2_VCDT1_REG_CH4_VC_MASK GENMASK(7, 6) +#define MIPI_CSI2_VCDT1_REG_CH4_DT_MASK GENMASK(5, 0) + +#endif /* __SUN8I_A83T_MIPI_CSI2_REG_H__ */
This patch add the support only for the Allwinner A83T MIPI CSI-2. Currently, the driver does not support the V3s MIPI CSI-2 controller. On the A83T, the CSI controller is the same as the other V3s Soc, but the MIPI CSI2 controller is not. It was tested with the ov8865 image sensor. Signed-off-by: Kévin L'hôpital <kevin.lhopital@bootlin.com> --- .../media/platform/sunxi/sun6i-csi/Makefile | 2 +- .../platform/sunxi/sun6i-csi/sun6i_csi.c | 84 +++++-- .../sunxi/sun6i-csi/sun8i_a83t_dphy.c | 39 ++++ .../sunxi/sun6i-csi/sun8i_a83t_dphy.h | 16 ++ .../sunxi/sun6i-csi/sun8i_a83t_dphy_reg.h | 39 ++++ .../sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.c | 217 ++++++++++++++++++ .../sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.h | 16 ++ .../sun6i-csi/sun8i_a83t_mipi_csi2_reg.h | 179 +++++++++++++++ 8 files changed, 575 insertions(+), 17 deletions(-) create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.c create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy.h create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_dphy_reg.h create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.c create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2.h create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun8i_a83t_mipi_csi2_reg.h