cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

sun6i_csi.c (24553B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Copyright (c) 2011-2018 Magewell Electronics Co., Ltd. (Nanjing)
      4 * All rights reserved.
      5 * Author: Yong Deng <yong.deng@magewell.com>
      6 */
      7
      8#include <linux/clk.h>
      9#include <linux/delay.h>
     10#include <linux/dma-mapping.h>
     11#include <linux/err.h>
     12#include <linux/fs.h>
     13#include <linux/interrupt.h>
     14#include <linux/io.h>
     15#include <linux/ioctl.h>
     16#include <linux/module.h>
     17#include <linux/of.h>
     18#include <linux/of_device.h>
     19#include <linux/platform_device.h>
     20#include <linux/pm_runtime.h>
     21#include <linux/regmap.h>
     22#include <linux/reset.h>
     23#include <linux/sched.h>
     24#include <linux/sizes.h>
     25#include <linux/slab.h>
     26
     27#include "sun6i_csi.h"
     28#include "sun6i_csi_reg.h"
     29
     30#define MODULE_NAME	"sun6i-csi"
     31
     32struct sun6i_csi_dev {
     33	struct sun6i_csi		csi;
     34	struct device			*dev;
     35
     36	struct regmap			*regmap;
     37	struct clk			*clk_mod;
     38	struct clk			*clk_ram;
     39	struct reset_control		*rstc_bus;
     40
     41	int				planar_offset[3];
     42};
     43
     44static inline struct sun6i_csi_dev *sun6i_csi_to_dev(struct sun6i_csi *csi)
     45{
     46	return container_of(csi, struct sun6i_csi_dev, csi);
     47}
     48
     49/* TODO add 10&12 bit YUV, RGB support */
     50bool sun6i_csi_is_format_supported(struct sun6i_csi *csi,
     51				   u32 pixformat, u32 mbus_code)
     52{
     53	struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
     54
     55	/*
     56	 * Some video receivers have the ability to be compatible with
     57	 * 8bit and 16bit bus width.
     58	 * Identify the media bus format from device tree.
     59	 */
     60	if ((sdev->csi.v4l2_ep.bus_type == V4L2_MBUS_PARALLEL
     61	     || sdev->csi.v4l2_ep.bus_type == V4L2_MBUS_BT656)
     62	     && sdev->csi.v4l2_ep.bus.parallel.bus_width == 16) {
     63		switch (pixformat) {
     64		case V4L2_PIX_FMT_NV12_16L16:
     65		case V4L2_PIX_FMT_NV12:
     66		case V4L2_PIX_FMT_NV21:
     67		case V4L2_PIX_FMT_NV16:
     68		case V4L2_PIX_FMT_NV61:
     69		case V4L2_PIX_FMT_YUV420:
     70		case V4L2_PIX_FMT_YVU420:
     71		case V4L2_PIX_FMT_YUV422P:
     72			switch (mbus_code) {
     73			case MEDIA_BUS_FMT_UYVY8_1X16:
     74			case MEDIA_BUS_FMT_VYUY8_1X16:
     75			case MEDIA_BUS_FMT_YUYV8_1X16:
     76			case MEDIA_BUS_FMT_YVYU8_1X16:
     77				return true;
     78			default:
     79				dev_dbg(sdev->dev, "Unsupported mbus code: 0x%x\n",
     80					mbus_code);
     81				break;
     82			}
     83			break;
     84		default:
     85			dev_dbg(sdev->dev, "Unsupported pixformat: 0x%x\n",
     86				pixformat);
     87			break;
     88		}
     89		return false;
     90	}
     91
     92	switch (pixformat) {
     93	case V4L2_PIX_FMT_SBGGR8:
     94		return (mbus_code == MEDIA_BUS_FMT_SBGGR8_1X8);
     95	case V4L2_PIX_FMT_SGBRG8:
     96		return (mbus_code == MEDIA_BUS_FMT_SGBRG8_1X8);
     97	case V4L2_PIX_FMT_SGRBG8:
     98		return (mbus_code == MEDIA_BUS_FMT_SGRBG8_1X8);
     99	case V4L2_PIX_FMT_SRGGB8:
    100		return (mbus_code == MEDIA_BUS_FMT_SRGGB8_1X8);
    101	case V4L2_PIX_FMT_SBGGR10:
    102		return (mbus_code == MEDIA_BUS_FMT_SBGGR10_1X10);
    103	case V4L2_PIX_FMT_SGBRG10:
    104		return (mbus_code == MEDIA_BUS_FMT_SGBRG10_1X10);
    105	case V4L2_PIX_FMT_SGRBG10:
    106		return (mbus_code == MEDIA_BUS_FMT_SGRBG10_1X10);
    107	case V4L2_PIX_FMT_SRGGB10:
    108		return (mbus_code == MEDIA_BUS_FMT_SRGGB10_1X10);
    109	case V4L2_PIX_FMT_SBGGR12:
    110		return (mbus_code == MEDIA_BUS_FMT_SBGGR12_1X12);
    111	case V4L2_PIX_FMT_SGBRG12:
    112		return (mbus_code == MEDIA_BUS_FMT_SGBRG12_1X12);
    113	case V4L2_PIX_FMT_SGRBG12:
    114		return (mbus_code == MEDIA_BUS_FMT_SGRBG12_1X12);
    115	case V4L2_PIX_FMT_SRGGB12:
    116		return (mbus_code == MEDIA_BUS_FMT_SRGGB12_1X12);
    117
    118	case V4L2_PIX_FMT_YUYV:
    119		return (mbus_code == MEDIA_BUS_FMT_YUYV8_2X8);
    120	case V4L2_PIX_FMT_YVYU:
    121		return (mbus_code == MEDIA_BUS_FMT_YVYU8_2X8);
    122	case V4L2_PIX_FMT_UYVY:
    123		return (mbus_code == MEDIA_BUS_FMT_UYVY8_2X8);
    124	case V4L2_PIX_FMT_VYUY:
    125		return (mbus_code == MEDIA_BUS_FMT_VYUY8_2X8);
    126
    127	case V4L2_PIX_FMT_NV12_16L16:
    128	case V4L2_PIX_FMT_NV12:
    129	case V4L2_PIX_FMT_NV21:
    130	case V4L2_PIX_FMT_NV16:
    131	case V4L2_PIX_FMT_NV61:
    132	case V4L2_PIX_FMT_YUV420:
    133	case V4L2_PIX_FMT_YVU420:
    134	case V4L2_PIX_FMT_YUV422P:
    135		switch (mbus_code) {
    136		case MEDIA_BUS_FMT_UYVY8_2X8:
    137		case MEDIA_BUS_FMT_VYUY8_2X8:
    138		case MEDIA_BUS_FMT_YUYV8_2X8:
    139		case MEDIA_BUS_FMT_YVYU8_2X8:
    140			return true;
    141		default:
    142			dev_dbg(sdev->dev, "Unsupported mbus code: 0x%x\n",
    143				mbus_code);
    144			break;
    145		}
    146		break;
    147
    148	case V4L2_PIX_FMT_RGB565:
    149		return (mbus_code == MEDIA_BUS_FMT_RGB565_2X8_LE);
    150	case V4L2_PIX_FMT_RGB565X:
    151		return (mbus_code == MEDIA_BUS_FMT_RGB565_2X8_BE);
    152
    153	case V4L2_PIX_FMT_JPEG:
    154		return (mbus_code == MEDIA_BUS_FMT_JPEG_1X8);
    155
    156	default:
    157		dev_dbg(sdev->dev, "Unsupported pixformat: 0x%x\n", pixformat);
    158		break;
    159	}
    160
    161	return false;
    162}
    163
    164int sun6i_csi_set_power(struct sun6i_csi *csi, bool enable)
    165{
    166	struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
    167	struct device *dev = sdev->dev;
    168	struct regmap *regmap = sdev->regmap;
    169	int ret;
    170
    171	if (!enable) {
    172		regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN, 0);
    173
    174		clk_disable_unprepare(sdev->clk_ram);
    175		if (of_device_is_compatible(dev->of_node,
    176					    "allwinner,sun50i-a64-csi"))
    177			clk_rate_exclusive_put(sdev->clk_mod);
    178		clk_disable_unprepare(sdev->clk_mod);
    179		reset_control_assert(sdev->rstc_bus);
    180		return 0;
    181	}
    182
    183	ret = clk_prepare_enable(sdev->clk_mod);
    184	if (ret) {
    185		dev_err(sdev->dev, "Enable csi clk err %d\n", ret);
    186		return ret;
    187	}
    188
    189	if (of_device_is_compatible(dev->of_node, "allwinner,sun50i-a64-csi"))
    190		clk_set_rate_exclusive(sdev->clk_mod, 300000000);
    191
    192	ret = clk_prepare_enable(sdev->clk_ram);
    193	if (ret) {
    194		dev_err(sdev->dev, "Enable clk_dram_csi clk err %d\n", ret);
    195		goto clk_mod_disable;
    196	}
    197
    198	ret = reset_control_deassert(sdev->rstc_bus);
    199	if (ret) {
    200		dev_err(sdev->dev, "reset err %d\n", ret);
    201		goto clk_ram_disable;
    202	}
    203
    204	regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN, CSI_EN_CSI_EN);
    205
    206	return 0;
    207
    208clk_ram_disable:
    209	clk_disable_unprepare(sdev->clk_ram);
    210clk_mod_disable:
    211	if (of_device_is_compatible(dev->of_node, "allwinner,sun50i-a64-csi"))
    212		clk_rate_exclusive_put(sdev->clk_mod);
    213	clk_disable_unprepare(sdev->clk_mod);
    214	return ret;
    215}
    216
    217static enum csi_input_fmt get_csi_input_format(struct sun6i_csi_dev *sdev,
    218					       u32 mbus_code, u32 pixformat)
    219{
    220	/* non-YUV */
    221	if ((mbus_code & 0xF000) != 0x2000)
    222		return CSI_INPUT_FORMAT_RAW;
    223
    224	switch (pixformat) {
    225	case V4L2_PIX_FMT_YUYV:
    226	case V4L2_PIX_FMT_YVYU:
    227	case V4L2_PIX_FMT_UYVY:
    228	case V4L2_PIX_FMT_VYUY:
    229		return CSI_INPUT_FORMAT_RAW;
    230	default:
    231		break;
    232	}
    233
    234	/* not support YUV420 input format yet */
    235	dev_dbg(sdev->dev, "Select YUV422 as default input format of CSI.\n");
    236	return CSI_INPUT_FORMAT_YUV422;
    237}
    238
    239static enum csi_output_fmt get_csi_output_format(struct sun6i_csi_dev *sdev,
    240						 u32 pixformat, u32 field)
    241{
    242	bool buf_interlaced = false;
    243
    244	if (field == V4L2_FIELD_INTERLACED
    245	    || field == V4L2_FIELD_INTERLACED_TB
    246	    || field == V4L2_FIELD_INTERLACED_BT)
    247		buf_interlaced = true;
    248
    249	switch (pixformat) {
    250	case V4L2_PIX_FMT_SBGGR8:
    251	case V4L2_PIX_FMT_SGBRG8:
    252	case V4L2_PIX_FMT_SGRBG8:
    253	case V4L2_PIX_FMT_SRGGB8:
    254		return buf_interlaced ? CSI_FRAME_RAW_8 : CSI_FIELD_RAW_8;
    255	case V4L2_PIX_FMT_SBGGR10:
    256	case V4L2_PIX_FMT_SGBRG10:
    257	case V4L2_PIX_FMT_SGRBG10:
    258	case V4L2_PIX_FMT_SRGGB10:
    259		return buf_interlaced ? CSI_FRAME_RAW_10 : CSI_FIELD_RAW_10;
    260	case V4L2_PIX_FMT_SBGGR12:
    261	case V4L2_PIX_FMT_SGBRG12:
    262	case V4L2_PIX_FMT_SGRBG12:
    263	case V4L2_PIX_FMT_SRGGB12:
    264		return buf_interlaced ? CSI_FRAME_RAW_12 : CSI_FIELD_RAW_12;
    265
    266	case V4L2_PIX_FMT_YUYV:
    267	case V4L2_PIX_FMT_YVYU:
    268	case V4L2_PIX_FMT_UYVY:
    269	case V4L2_PIX_FMT_VYUY:
    270		return buf_interlaced ? CSI_FRAME_RAW_8 : CSI_FIELD_RAW_8;
    271
    272	case V4L2_PIX_FMT_NV12_16L16:
    273		return buf_interlaced ? CSI_FRAME_MB_YUV420 :
    274					CSI_FIELD_MB_YUV420;
    275	case V4L2_PIX_FMT_NV12:
    276	case V4L2_PIX_FMT_NV21:
    277		return buf_interlaced ? CSI_FRAME_UV_CB_YUV420 :
    278					CSI_FIELD_UV_CB_YUV420;
    279	case V4L2_PIX_FMT_YUV420:
    280	case V4L2_PIX_FMT_YVU420:
    281		return buf_interlaced ? CSI_FRAME_PLANAR_YUV420 :
    282					CSI_FIELD_PLANAR_YUV420;
    283	case V4L2_PIX_FMT_NV16:
    284	case V4L2_PIX_FMT_NV61:
    285		return buf_interlaced ? CSI_FRAME_UV_CB_YUV422 :
    286					CSI_FIELD_UV_CB_YUV422;
    287	case V4L2_PIX_FMT_YUV422P:
    288		return buf_interlaced ? CSI_FRAME_PLANAR_YUV422 :
    289					CSI_FIELD_PLANAR_YUV422;
    290
    291	case V4L2_PIX_FMT_RGB565:
    292	case V4L2_PIX_FMT_RGB565X:
    293		return buf_interlaced ? CSI_FRAME_RGB565 : CSI_FIELD_RGB565;
    294
    295	case V4L2_PIX_FMT_JPEG:
    296		return buf_interlaced ? CSI_FRAME_RAW_8 : CSI_FIELD_RAW_8;
    297
    298	default:
    299		dev_warn(sdev->dev, "Unsupported pixformat: 0x%x\n", pixformat);
    300		break;
    301	}
    302
    303	return CSI_FIELD_RAW_8;
    304}
    305
    306static enum csi_input_seq get_csi_input_seq(struct sun6i_csi_dev *sdev,
    307					    u32 mbus_code, u32 pixformat)
    308{
    309	/* Input sequence does not apply to non-YUV formats */
    310	if ((mbus_code & 0xF000) != 0x2000)
    311		return 0;
    312
    313	switch (pixformat) {
    314	case V4L2_PIX_FMT_NV12_16L16:
    315	case V4L2_PIX_FMT_NV12:
    316	case V4L2_PIX_FMT_NV16:
    317	case V4L2_PIX_FMT_YUV420:
    318	case V4L2_PIX_FMT_YUV422P:
    319		switch (mbus_code) {
    320		case MEDIA_BUS_FMT_UYVY8_2X8:
    321		case MEDIA_BUS_FMT_UYVY8_1X16:
    322			return CSI_INPUT_SEQ_UYVY;
    323		case MEDIA_BUS_FMT_VYUY8_2X8:
    324		case MEDIA_BUS_FMT_VYUY8_1X16:
    325			return CSI_INPUT_SEQ_VYUY;
    326		case MEDIA_BUS_FMT_YUYV8_2X8:
    327		case MEDIA_BUS_FMT_YUYV8_1X16:
    328			return CSI_INPUT_SEQ_YUYV;
    329		case MEDIA_BUS_FMT_YVYU8_1X16:
    330		case MEDIA_BUS_FMT_YVYU8_2X8:
    331			return CSI_INPUT_SEQ_YVYU;
    332		default:
    333			dev_warn(sdev->dev, "Unsupported mbus code: 0x%x\n",
    334				 mbus_code);
    335			break;
    336		}
    337		break;
    338	case V4L2_PIX_FMT_NV21:
    339	case V4L2_PIX_FMT_NV61:
    340	case V4L2_PIX_FMT_YVU420:
    341		switch (mbus_code) {
    342		case MEDIA_BUS_FMT_UYVY8_2X8:
    343		case MEDIA_BUS_FMT_UYVY8_1X16:
    344			return CSI_INPUT_SEQ_VYUY;
    345		case MEDIA_BUS_FMT_VYUY8_2X8:
    346		case MEDIA_BUS_FMT_VYUY8_1X16:
    347			return CSI_INPUT_SEQ_UYVY;
    348		case MEDIA_BUS_FMT_YUYV8_2X8:
    349		case MEDIA_BUS_FMT_YUYV8_1X16:
    350			return CSI_INPUT_SEQ_YVYU;
    351		case MEDIA_BUS_FMT_YVYU8_1X16:
    352		case MEDIA_BUS_FMT_YVYU8_2X8:
    353			return CSI_INPUT_SEQ_YUYV;
    354		default:
    355			dev_warn(sdev->dev, "Unsupported mbus code: 0x%x\n",
    356				 mbus_code);
    357			break;
    358		}
    359		break;
    360
    361	case V4L2_PIX_FMT_YUYV:
    362		return CSI_INPUT_SEQ_YUYV;
    363
    364	default:
    365		dev_warn(sdev->dev, "Unsupported pixformat: 0x%x, defaulting to YUYV\n",
    366			 pixformat);
    367		break;
    368	}
    369
    370	return CSI_INPUT_SEQ_YUYV;
    371}
    372
    373static void sun6i_csi_setup_bus(struct sun6i_csi_dev *sdev)
    374{
    375	struct v4l2_fwnode_endpoint *endpoint = &sdev->csi.v4l2_ep;
    376	struct sun6i_csi *csi = &sdev->csi;
    377	unsigned char bus_width;
    378	u32 flags;
    379	u32 cfg;
    380	bool input_interlaced = false;
    381
    382	if (csi->config.field == V4L2_FIELD_INTERLACED
    383	    || csi->config.field == V4L2_FIELD_INTERLACED_TB
    384	    || csi->config.field == V4L2_FIELD_INTERLACED_BT)
    385		input_interlaced = true;
    386
    387	bus_width = endpoint->bus.parallel.bus_width;
    388
    389	regmap_read(sdev->regmap, CSI_IF_CFG_REG, &cfg);
    390
    391	cfg &= ~(CSI_IF_CFG_CSI_IF_MASK | CSI_IF_CFG_MIPI_IF_MASK |
    392		 CSI_IF_CFG_IF_DATA_WIDTH_MASK |
    393		 CSI_IF_CFG_CLK_POL_MASK | CSI_IF_CFG_VREF_POL_MASK |
    394		 CSI_IF_CFG_HREF_POL_MASK | CSI_IF_CFG_FIELD_MASK |
    395		 CSI_IF_CFG_SRC_TYPE_MASK);
    396
    397	if (input_interlaced)
    398		cfg |= CSI_IF_CFG_SRC_TYPE_INTERLACED;
    399	else
    400		cfg |= CSI_IF_CFG_SRC_TYPE_PROGRESSED;
    401
    402	switch (endpoint->bus_type) {
    403	case V4L2_MBUS_PARALLEL:
    404		cfg |= CSI_IF_CFG_MIPI_IF_CSI;
    405
    406		flags = endpoint->bus.parallel.flags;
    407
    408		cfg |= (bus_width == 16) ? CSI_IF_CFG_CSI_IF_YUV422_16BIT :
    409					   CSI_IF_CFG_CSI_IF_YUV422_INTLV;
    410
    411		if (flags & V4L2_MBUS_FIELD_EVEN_LOW)
    412			cfg |= CSI_IF_CFG_FIELD_POSITIVE;
    413
    414		if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
    415			cfg |= CSI_IF_CFG_VREF_POL_POSITIVE;
    416		if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
    417			cfg |= CSI_IF_CFG_HREF_POL_POSITIVE;
    418
    419		if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
    420			cfg |= CSI_IF_CFG_CLK_POL_FALLING_EDGE;
    421		break;
    422	case V4L2_MBUS_BT656:
    423		cfg |= CSI_IF_CFG_MIPI_IF_CSI;
    424
    425		flags = endpoint->bus.parallel.flags;
    426
    427		cfg |= (bus_width == 16) ? CSI_IF_CFG_CSI_IF_BT1120 :
    428					   CSI_IF_CFG_CSI_IF_BT656;
    429
    430		if (flags & V4L2_MBUS_FIELD_EVEN_LOW)
    431			cfg |= CSI_IF_CFG_FIELD_POSITIVE;
    432
    433		if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
    434			cfg |= CSI_IF_CFG_CLK_POL_FALLING_EDGE;
    435		break;
    436	default:
    437		dev_warn(sdev->dev, "Unsupported bus type: %d\n",
    438			 endpoint->bus_type);
    439		break;
    440	}
    441
    442	switch (bus_width) {
    443	case 8:
    444		cfg |= CSI_IF_CFG_IF_DATA_WIDTH_8BIT;
    445		break;
    446	case 10:
    447		cfg |= CSI_IF_CFG_IF_DATA_WIDTH_10BIT;
    448		break;
    449	case 12:
    450		cfg |= CSI_IF_CFG_IF_DATA_WIDTH_12BIT;
    451		break;
    452	case 16: /* No need to configure DATA_WIDTH for 16bit */
    453		break;
    454	default:
    455		dev_warn(sdev->dev, "Unsupported bus width: %u\n", bus_width);
    456		break;
    457	}
    458
    459	regmap_write(sdev->regmap, CSI_IF_CFG_REG, cfg);
    460}
    461
    462static void sun6i_csi_set_format(struct sun6i_csi_dev *sdev)
    463{
    464	struct sun6i_csi *csi = &sdev->csi;
    465	u32 cfg;
    466	u32 val;
    467
    468	regmap_read(sdev->regmap, CSI_CH_CFG_REG, &cfg);
    469
    470	cfg &= ~(CSI_CH_CFG_INPUT_FMT_MASK |
    471		 CSI_CH_CFG_OUTPUT_FMT_MASK | CSI_CH_CFG_VFLIP_EN |
    472		 CSI_CH_CFG_HFLIP_EN | CSI_CH_CFG_FIELD_SEL_MASK |
    473		 CSI_CH_CFG_INPUT_SEQ_MASK);
    474
    475	val = get_csi_input_format(sdev, csi->config.code,
    476				   csi->config.pixelformat);
    477	cfg |= CSI_CH_CFG_INPUT_FMT(val);
    478
    479	val = get_csi_output_format(sdev, csi->config.pixelformat,
    480				    csi->config.field);
    481	cfg |= CSI_CH_CFG_OUTPUT_FMT(val);
    482
    483	val = get_csi_input_seq(sdev, csi->config.code,
    484				csi->config.pixelformat);
    485	cfg |= CSI_CH_CFG_INPUT_SEQ(val);
    486
    487	if (csi->config.field == V4L2_FIELD_TOP)
    488		cfg |= CSI_CH_CFG_FIELD_SEL_FIELD0;
    489	else if (csi->config.field == V4L2_FIELD_BOTTOM)
    490		cfg |= CSI_CH_CFG_FIELD_SEL_FIELD1;
    491	else
    492		cfg |= CSI_CH_CFG_FIELD_SEL_BOTH;
    493
    494	regmap_write(sdev->regmap, CSI_CH_CFG_REG, cfg);
    495}
    496
    497static void sun6i_csi_set_window(struct sun6i_csi_dev *sdev)
    498{
    499	struct sun6i_csi_config *config = &sdev->csi.config;
    500	u32 bytesperline_y;
    501	u32 bytesperline_c;
    502	int *planar_offset = sdev->planar_offset;
    503	u32 width = config->width;
    504	u32 height = config->height;
    505	u32 hor_len = width;
    506
    507	switch (config->pixelformat) {
    508	case V4L2_PIX_FMT_YUYV:
    509	case V4L2_PIX_FMT_YVYU:
    510	case V4L2_PIX_FMT_UYVY:
    511	case V4L2_PIX_FMT_VYUY:
    512		dev_dbg(sdev->dev,
    513			"Horizontal length should be 2 times of width for packed YUV formats!\n");
    514		hor_len = width * 2;
    515		break;
    516	default:
    517		break;
    518	}
    519
    520	regmap_write(sdev->regmap, CSI_CH_HSIZE_REG,
    521		     CSI_CH_HSIZE_HOR_LEN(hor_len) |
    522		     CSI_CH_HSIZE_HOR_START(0));
    523	regmap_write(sdev->regmap, CSI_CH_VSIZE_REG,
    524		     CSI_CH_VSIZE_VER_LEN(height) |
    525		     CSI_CH_VSIZE_VER_START(0));
    526
    527	planar_offset[0] = 0;
    528	switch (config->pixelformat) {
    529	case V4L2_PIX_FMT_NV12_16L16:
    530	case V4L2_PIX_FMT_NV12:
    531	case V4L2_PIX_FMT_NV21:
    532	case V4L2_PIX_FMT_NV16:
    533	case V4L2_PIX_FMT_NV61:
    534		bytesperline_y = width;
    535		bytesperline_c = width;
    536		planar_offset[1] = bytesperline_y * height;
    537		planar_offset[2] = -1;
    538		break;
    539	case V4L2_PIX_FMT_YUV420:
    540	case V4L2_PIX_FMT_YVU420:
    541		bytesperline_y = width;
    542		bytesperline_c = width / 2;
    543		planar_offset[1] = bytesperline_y * height;
    544		planar_offset[2] = planar_offset[1] +
    545				bytesperline_c * height / 2;
    546		break;
    547	case V4L2_PIX_FMT_YUV422P:
    548		bytesperline_y = width;
    549		bytesperline_c = width / 2;
    550		planar_offset[1] = bytesperline_y * height;
    551		planar_offset[2] = planar_offset[1] +
    552				bytesperline_c * height;
    553		break;
    554	default: /* raw */
    555		dev_dbg(sdev->dev,
    556			"Calculating pixelformat(0x%x)'s bytesperline as a packed format\n",
    557			config->pixelformat);
    558		bytesperline_y = (sun6i_csi_get_bpp(config->pixelformat) *
    559				  config->width) / 8;
    560		bytesperline_c = 0;
    561		planar_offset[1] = -1;
    562		planar_offset[2] = -1;
    563		break;
    564	}
    565
    566	regmap_write(sdev->regmap, CSI_CH_BUF_LEN_REG,
    567		     CSI_CH_BUF_LEN_BUF_LEN_C(bytesperline_c) |
    568		     CSI_CH_BUF_LEN_BUF_LEN_Y(bytesperline_y));
    569}
    570
    571int sun6i_csi_update_config(struct sun6i_csi *csi,
    572			    struct sun6i_csi_config *config)
    573{
    574	struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
    575
    576	if (!config)
    577		return -EINVAL;
    578
    579	memcpy(&csi->config, config, sizeof(csi->config));
    580
    581	sun6i_csi_setup_bus(sdev);
    582	sun6i_csi_set_format(sdev);
    583	sun6i_csi_set_window(sdev);
    584
    585	return 0;
    586}
    587
    588void sun6i_csi_update_buf_addr(struct sun6i_csi *csi, dma_addr_t addr)
    589{
    590	struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
    591
    592	regmap_write(sdev->regmap, CSI_CH_F0_BUFA_REG,
    593		     (addr + sdev->planar_offset[0]) >> 2);
    594	if (sdev->planar_offset[1] != -1)
    595		regmap_write(sdev->regmap, CSI_CH_F1_BUFA_REG,
    596			     (addr + sdev->planar_offset[1]) >> 2);
    597	if (sdev->planar_offset[2] != -1)
    598		regmap_write(sdev->regmap, CSI_CH_F2_BUFA_REG,
    599			     (addr + sdev->planar_offset[2]) >> 2);
    600}
    601
    602void sun6i_csi_set_stream(struct sun6i_csi *csi, bool enable)
    603{
    604	struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
    605	struct regmap *regmap = sdev->regmap;
    606
    607	if (!enable) {
    608		regmap_update_bits(regmap, CSI_CAP_REG, CSI_CAP_CH0_VCAP_ON, 0);
    609		regmap_write(regmap, CSI_CH_INT_EN_REG, 0);
    610		return;
    611	}
    612
    613	regmap_write(regmap, CSI_CH_INT_STA_REG, 0xFF);
    614	regmap_write(regmap, CSI_CH_INT_EN_REG,
    615		     CSI_CH_INT_EN_HB_OF_INT_EN |
    616		     CSI_CH_INT_EN_FIFO2_OF_INT_EN |
    617		     CSI_CH_INT_EN_FIFO1_OF_INT_EN |
    618		     CSI_CH_INT_EN_FIFO0_OF_INT_EN |
    619		     CSI_CH_INT_EN_FD_INT_EN |
    620		     CSI_CH_INT_EN_CD_INT_EN);
    621
    622	regmap_update_bits(regmap, CSI_CAP_REG, CSI_CAP_CH0_VCAP_ON,
    623			   CSI_CAP_CH0_VCAP_ON);
    624}
    625
    626/* -----------------------------------------------------------------------------
    627 * Media Controller and V4L2
    628 */
    629static int sun6i_csi_link_entity(struct sun6i_csi *csi,
    630				 struct media_entity *entity,
    631				 struct fwnode_handle *fwnode)
    632{
    633	struct media_entity *sink;
    634	struct media_pad *sink_pad;
    635	int src_pad_index;
    636	int ret;
    637
    638	ret = media_entity_get_fwnode_pad(entity, fwnode, MEDIA_PAD_FL_SOURCE);
    639	if (ret < 0) {
    640		dev_err(csi->dev, "%s: no source pad in external entity %s\n",
    641			__func__, entity->name);
    642		return -EINVAL;
    643	}
    644
    645	src_pad_index = ret;
    646
    647	sink = &csi->video.vdev.entity;
    648	sink_pad = &csi->video.pad;
    649
    650	dev_dbg(csi->dev, "creating %s:%u -> %s:%u link\n",
    651		entity->name, src_pad_index, sink->name, sink_pad->index);
    652	ret = media_create_pad_link(entity, src_pad_index, sink,
    653				    sink_pad->index,
    654				    MEDIA_LNK_FL_ENABLED |
    655				    MEDIA_LNK_FL_IMMUTABLE);
    656	if (ret < 0) {
    657		dev_err(csi->dev, "failed to create %s:%u -> %s:%u link\n",
    658			entity->name, src_pad_index,
    659			sink->name, sink_pad->index);
    660		return ret;
    661	}
    662
    663	return 0;
    664}
    665
    666static int sun6i_subdev_notify_complete(struct v4l2_async_notifier *notifier)
    667{
    668	struct sun6i_csi *csi = container_of(notifier, struct sun6i_csi,
    669					     notifier);
    670	struct v4l2_device *v4l2_dev = &csi->v4l2_dev;
    671	struct v4l2_subdev *sd;
    672	int ret;
    673
    674	dev_dbg(csi->dev, "notify complete, all subdevs registered\n");
    675
    676	sd = list_first_entry(&v4l2_dev->subdevs, struct v4l2_subdev, list);
    677	if (!sd)
    678		return -EINVAL;
    679
    680	ret = sun6i_csi_link_entity(csi, &sd->entity, sd->fwnode);
    681	if (ret < 0)
    682		return ret;
    683
    684	ret = v4l2_device_register_subdev_nodes(&csi->v4l2_dev);
    685	if (ret < 0)
    686		return ret;
    687
    688	return media_device_register(&csi->media_dev);
    689}
    690
    691static const struct v4l2_async_notifier_operations sun6i_csi_async_ops = {
    692	.complete = sun6i_subdev_notify_complete,
    693};
    694
    695static int sun6i_csi_fwnode_parse(struct device *dev,
    696				  struct v4l2_fwnode_endpoint *vep,
    697				  struct v4l2_async_subdev *asd)
    698{
    699	struct sun6i_csi *csi = dev_get_drvdata(dev);
    700
    701	if (vep->base.port || vep->base.id) {
    702		dev_warn(dev, "Only support a single port with one endpoint\n");
    703		return -ENOTCONN;
    704	}
    705
    706	switch (vep->bus_type) {
    707	case V4L2_MBUS_PARALLEL:
    708	case V4L2_MBUS_BT656:
    709		csi->v4l2_ep = *vep;
    710		return 0;
    711	default:
    712		dev_err(dev, "Unsupported media bus type\n");
    713		return -ENOTCONN;
    714	}
    715}
    716
    717static void sun6i_csi_v4l2_cleanup(struct sun6i_csi *csi)
    718{
    719	media_device_unregister(&csi->media_dev);
    720	v4l2_async_nf_unregister(&csi->notifier);
    721	v4l2_async_nf_cleanup(&csi->notifier);
    722	sun6i_video_cleanup(&csi->video);
    723	v4l2_device_unregister(&csi->v4l2_dev);
    724	v4l2_ctrl_handler_free(&csi->ctrl_handler);
    725	media_device_cleanup(&csi->media_dev);
    726}
    727
    728static int sun6i_csi_v4l2_init(struct sun6i_csi *csi)
    729{
    730	int ret;
    731
    732	csi->media_dev.dev = csi->dev;
    733	strscpy(csi->media_dev.model, "Allwinner Video Capture Device",
    734		sizeof(csi->media_dev.model));
    735	csi->media_dev.hw_revision = 0;
    736
    737	media_device_init(&csi->media_dev);
    738	v4l2_async_nf_init(&csi->notifier);
    739
    740	ret = v4l2_ctrl_handler_init(&csi->ctrl_handler, 0);
    741	if (ret) {
    742		dev_err(csi->dev, "V4L2 controls handler init failed (%d)\n",
    743			ret);
    744		goto clean_media;
    745	}
    746
    747	csi->v4l2_dev.mdev = &csi->media_dev;
    748	csi->v4l2_dev.ctrl_handler = &csi->ctrl_handler;
    749	ret = v4l2_device_register(csi->dev, &csi->v4l2_dev);
    750	if (ret) {
    751		dev_err(csi->dev, "V4L2 device registration failed (%d)\n",
    752			ret);
    753		goto free_ctrl;
    754	}
    755
    756	ret = sun6i_video_init(&csi->video, csi, "sun6i-csi");
    757	if (ret)
    758		goto unreg_v4l2;
    759
    760	ret = v4l2_async_nf_parse_fwnode_endpoints(csi->dev,
    761						   &csi->notifier,
    762						   sizeof(struct
    763							  v4l2_async_subdev),
    764						   sun6i_csi_fwnode_parse);
    765	if (ret)
    766		goto clean_video;
    767
    768	csi->notifier.ops = &sun6i_csi_async_ops;
    769
    770	ret = v4l2_async_nf_register(&csi->v4l2_dev, &csi->notifier);
    771	if (ret) {
    772		dev_err(csi->dev, "notifier registration failed\n");
    773		goto clean_video;
    774	}
    775
    776	return 0;
    777
    778clean_video:
    779	sun6i_video_cleanup(&csi->video);
    780unreg_v4l2:
    781	v4l2_device_unregister(&csi->v4l2_dev);
    782free_ctrl:
    783	v4l2_ctrl_handler_free(&csi->ctrl_handler);
    784clean_media:
    785	v4l2_async_nf_cleanup(&csi->notifier);
    786	media_device_cleanup(&csi->media_dev);
    787
    788	return ret;
    789}
    790
    791/* -----------------------------------------------------------------------------
    792 * Resources and IRQ
    793 */
    794static irqreturn_t sun6i_csi_isr(int irq, void *dev_id)
    795{
    796	struct sun6i_csi_dev *sdev = (struct sun6i_csi_dev *)dev_id;
    797	struct regmap *regmap = sdev->regmap;
    798	u32 status;
    799
    800	regmap_read(regmap, CSI_CH_INT_STA_REG, &status);
    801
    802	if (!(status & 0xFF))
    803		return IRQ_NONE;
    804
    805	if ((status & CSI_CH_INT_STA_FIFO0_OF_PD) ||
    806	    (status & CSI_CH_INT_STA_FIFO1_OF_PD) ||
    807	    (status & CSI_CH_INT_STA_FIFO2_OF_PD) ||
    808	    (status & CSI_CH_INT_STA_HB_OF_PD)) {
    809		regmap_write(regmap, CSI_CH_INT_STA_REG, status);
    810		regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN, 0);
    811		regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN,
    812				   CSI_EN_CSI_EN);
    813		return IRQ_HANDLED;
    814	}
    815
    816	if (status & CSI_CH_INT_STA_FD_PD)
    817		sun6i_video_frame_done(&sdev->csi.video);
    818
    819	regmap_write(regmap, CSI_CH_INT_STA_REG, status);
    820
    821	return IRQ_HANDLED;
    822}
    823
    824static const struct regmap_config sun6i_csi_regmap_config = {
    825	.reg_bits       = 32,
    826	.reg_stride     = 4,
    827	.val_bits       = 32,
    828	.max_register	= 0x9c,
    829};
    830
    831static int sun6i_csi_resource_request(struct sun6i_csi_dev *sdev,
    832				      struct platform_device *pdev)
    833{
    834	void __iomem *io_base;
    835	int ret;
    836	int irq;
    837
    838	io_base = devm_platform_ioremap_resource(pdev, 0);
    839	if (IS_ERR(io_base))
    840		return PTR_ERR(io_base);
    841
    842	sdev->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "bus", io_base,
    843						 &sun6i_csi_regmap_config);
    844	if (IS_ERR(sdev->regmap)) {
    845		dev_err(&pdev->dev, "Failed to init register map\n");
    846		return PTR_ERR(sdev->regmap);
    847	}
    848
    849	sdev->clk_mod = devm_clk_get(&pdev->dev, "mod");
    850	if (IS_ERR(sdev->clk_mod)) {
    851		dev_err(&pdev->dev, "Unable to acquire csi clock\n");
    852		return PTR_ERR(sdev->clk_mod);
    853	}
    854
    855	sdev->clk_ram = devm_clk_get(&pdev->dev, "ram");
    856	if (IS_ERR(sdev->clk_ram)) {
    857		dev_err(&pdev->dev, "Unable to acquire dram-csi clock\n");
    858		return PTR_ERR(sdev->clk_ram);
    859	}
    860
    861	sdev->rstc_bus = devm_reset_control_get_shared(&pdev->dev, NULL);
    862	if (IS_ERR(sdev->rstc_bus)) {
    863		dev_err(&pdev->dev, "Cannot get reset controller\n");
    864		return PTR_ERR(sdev->rstc_bus);
    865	}
    866
    867	irq = platform_get_irq(pdev, 0);
    868	if (irq < 0)
    869		return -ENXIO;
    870
    871	ret = devm_request_irq(&pdev->dev, irq, sun6i_csi_isr, 0, MODULE_NAME,
    872			       sdev);
    873	if (ret) {
    874		dev_err(&pdev->dev, "Cannot request csi IRQ\n");
    875		return ret;
    876	}
    877
    878	return 0;
    879}
    880
    881static int sun6i_csi_probe(struct platform_device *pdev)
    882{
    883	struct sun6i_csi_dev *sdev;
    884	int ret;
    885
    886	sdev = devm_kzalloc(&pdev->dev, sizeof(*sdev), GFP_KERNEL);
    887	if (!sdev)
    888		return -ENOMEM;
    889
    890	sdev->dev = &pdev->dev;
    891
    892	ret = sun6i_csi_resource_request(sdev, pdev);
    893	if (ret)
    894		return ret;
    895
    896	platform_set_drvdata(pdev, sdev);
    897
    898	sdev->csi.dev = &pdev->dev;
    899	return sun6i_csi_v4l2_init(&sdev->csi);
    900}
    901
    902static int sun6i_csi_remove(struct platform_device *pdev)
    903{
    904	struct sun6i_csi_dev *sdev = platform_get_drvdata(pdev);
    905
    906	sun6i_csi_v4l2_cleanup(&sdev->csi);
    907
    908	return 0;
    909}
    910
    911static const struct of_device_id sun6i_csi_of_match[] = {
    912	{ .compatible = "allwinner,sun6i-a31-csi", },
    913	{ .compatible = "allwinner,sun8i-a83t-csi", },
    914	{ .compatible = "allwinner,sun8i-h3-csi", },
    915	{ .compatible = "allwinner,sun8i-v3s-csi", },
    916	{ .compatible = "allwinner,sun50i-a64-csi", },
    917	{},
    918};
    919MODULE_DEVICE_TABLE(of, sun6i_csi_of_match);
    920
    921static struct platform_driver sun6i_csi_platform_driver = {
    922	.probe = sun6i_csi_probe,
    923	.remove = sun6i_csi_remove,
    924	.driver = {
    925		.name = MODULE_NAME,
    926		.of_match_table = of_match_ptr(sun6i_csi_of_match),
    927	},
    928};
    929module_platform_driver(sun6i_csi_platform_driver);
    930
    931MODULE_DESCRIPTION("Allwinner V3s Camera Sensor Interface driver");
    932MODULE_AUTHOR("Yong Deng <yong.deng@magewell.com>");
    933MODULE_LICENSE("GPL");