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

atmel-sama5d2-isc.c (18169B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Microchip Image Sensor Controller (ISC) driver
      4 *
      5 * Copyright (C) 2016-2019 Microchip Technology, Inc.
      6 *
      7 * Author: Songjun Wu
      8 * Author: Eugen Hristev <eugen.hristev@microchip.com>
      9 *
     10 *
     11 * Sensor-->PFE-->WB-->CFA-->CC-->GAM-->CSC-->CBC-->SUB-->RLP-->DMA
     12 *
     13 * ISC video pipeline integrates the following submodules:
     14 * PFE: Parallel Front End to sample the camera sensor input stream
     15 *  WB: Programmable white balance in the Bayer domain
     16 * CFA: Color filter array interpolation module
     17 *  CC: Programmable color correction
     18 * GAM: Gamma correction
     19 * CSC: Programmable color space conversion
     20 * CBC: Contrast and Brightness control
     21 * SUB: This module performs YCbCr444 to YCbCr420 chrominance subsampling
     22 * RLP: This module performs rounding, range limiting
     23 *      and packing of the incoming data
     24 */
     25
     26#include <linux/clk.h>
     27#include <linux/clkdev.h>
     28#include <linux/clk-provider.h>
     29#include <linux/delay.h>
     30#include <linux/interrupt.h>
     31#include <linux/math64.h>
     32#include <linux/module.h>
     33#include <linux/of.h>
     34#include <linux/of_graph.h>
     35#include <linux/platform_device.h>
     36#include <linux/pm_runtime.h>
     37#include <linux/regmap.h>
     38#include <linux/videodev2.h>
     39
     40#include <media/v4l2-ctrls.h>
     41#include <media/v4l2-device.h>
     42#include <media/v4l2-event.h>
     43#include <media/v4l2-image-sizes.h>
     44#include <media/v4l2-ioctl.h>
     45#include <media/v4l2-fwnode.h>
     46#include <media/v4l2-subdev.h>
     47#include <media/videobuf2-dma-contig.h>
     48
     49#include "atmel-isc-regs.h"
     50#include "atmel-isc.h"
     51
     52#define ISC_SAMA5D2_MAX_SUPPORT_WIDTH   2592
     53#define ISC_SAMA5D2_MAX_SUPPORT_HEIGHT  1944
     54
     55#define ISC_SAMA5D2_PIPELINE \
     56	(WB_ENABLE | CFA_ENABLE | CC_ENABLE | GAM_ENABLES | CSC_ENABLE | \
     57	CBC_ENABLE | SUB422_ENABLE | SUB420_ENABLE)
     58
     59/* This is a list of the formats that the ISC can *output* */
     60static const struct isc_format sama5d2_controller_formats[] = {
     61	{
     62		.fourcc		= V4L2_PIX_FMT_ARGB444,
     63	}, {
     64		.fourcc		= V4L2_PIX_FMT_ARGB555,
     65	}, {
     66		.fourcc		= V4L2_PIX_FMT_RGB565,
     67	}, {
     68		.fourcc		= V4L2_PIX_FMT_ABGR32,
     69	}, {
     70		.fourcc		= V4L2_PIX_FMT_XBGR32,
     71	}, {
     72		.fourcc		= V4L2_PIX_FMT_YUV420,
     73	}, {
     74		.fourcc		= V4L2_PIX_FMT_YUYV,
     75	}, {
     76		.fourcc		= V4L2_PIX_FMT_YUV422P,
     77	}, {
     78		.fourcc		= V4L2_PIX_FMT_GREY,
     79	}, {
     80		.fourcc		= V4L2_PIX_FMT_Y10,
     81	}, {
     82		.fourcc		= V4L2_PIX_FMT_SBGGR8,
     83	}, {
     84		.fourcc		= V4L2_PIX_FMT_SGBRG8,
     85	}, {
     86		.fourcc		= V4L2_PIX_FMT_SGRBG8,
     87	}, {
     88		.fourcc		= V4L2_PIX_FMT_SRGGB8,
     89	}, {
     90		.fourcc		= V4L2_PIX_FMT_SBGGR10,
     91	}, {
     92		.fourcc		= V4L2_PIX_FMT_SGBRG10,
     93	}, {
     94		.fourcc		= V4L2_PIX_FMT_SGRBG10,
     95	}, {
     96		.fourcc		= V4L2_PIX_FMT_SRGGB10,
     97	},
     98};
     99
    100/* This is a list of formats that the ISC can receive as *input* */
    101static struct isc_format sama5d2_formats_list[] = {
    102	{
    103		.fourcc		= V4L2_PIX_FMT_SBGGR8,
    104		.mbus_code	= MEDIA_BUS_FMT_SBGGR8_1X8,
    105		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,
    106		.cfa_baycfg	= ISC_BAY_CFG_BGBG,
    107	},
    108	{
    109		.fourcc		= V4L2_PIX_FMT_SGBRG8,
    110		.mbus_code	= MEDIA_BUS_FMT_SGBRG8_1X8,
    111		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,
    112		.cfa_baycfg	= ISC_BAY_CFG_GBGB,
    113	},
    114	{
    115		.fourcc		= V4L2_PIX_FMT_SGRBG8,
    116		.mbus_code	= MEDIA_BUS_FMT_SGRBG8_1X8,
    117		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,
    118		.cfa_baycfg	= ISC_BAY_CFG_GRGR,
    119	},
    120	{
    121		.fourcc		= V4L2_PIX_FMT_SRGGB8,
    122		.mbus_code	= MEDIA_BUS_FMT_SRGGB8_1X8,
    123		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,
    124		.cfa_baycfg	= ISC_BAY_CFG_RGRG,
    125	},
    126	{
    127		.fourcc		= V4L2_PIX_FMT_SBGGR10,
    128		.mbus_code	= MEDIA_BUS_FMT_SBGGR10_1X10,
    129		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TEN,
    130		.cfa_baycfg	= ISC_BAY_CFG_RGRG,
    131	},
    132	{
    133		.fourcc		= V4L2_PIX_FMT_SGBRG10,
    134		.mbus_code	= MEDIA_BUS_FMT_SGBRG10_1X10,
    135		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TEN,
    136		.cfa_baycfg	= ISC_BAY_CFG_GBGB,
    137	},
    138	{
    139		.fourcc		= V4L2_PIX_FMT_SGRBG10,
    140		.mbus_code	= MEDIA_BUS_FMT_SGRBG10_1X10,
    141		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TEN,
    142		.cfa_baycfg	= ISC_BAY_CFG_GRGR,
    143	},
    144	{
    145		.fourcc		= V4L2_PIX_FMT_SRGGB10,
    146		.mbus_code	= MEDIA_BUS_FMT_SRGGB10_1X10,
    147		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TEN,
    148		.cfa_baycfg	= ISC_BAY_CFG_RGRG,
    149	},
    150	{
    151		.fourcc		= V4L2_PIX_FMT_SBGGR12,
    152		.mbus_code	= MEDIA_BUS_FMT_SBGGR12_1X12,
    153		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TWELVE,
    154		.cfa_baycfg	= ISC_BAY_CFG_BGBG,
    155	},
    156	{
    157		.fourcc		= V4L2_PIX_FMT_SGBRG12,
    158		.mbus_code	= MEDIA_BUS_FMT_SGBRG12_1X12,
    159		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TWELVE,
    160		.cfa_baycfg	= ISC_BAY_CFG_GBGB,
    161	},
    162	{
    163		.fourcc		= V4L2_PIX_FMT_SGRBG12,
    164		.mbus_code	= MEDIA_BUS_FMT_SGRBG12_1X12,
    165		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TWELVE,
    166		.cfa_baycfg	= ISC_BAY_CFG_GRGR,
    167	},
    168	{
    169		.fourcc		= V4L2_PIX_FMT_SRGGB12,
    170		.mbus_code	= MEDIA_BUS_FMT_SRGGB12_1X12,
    171		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TWELVE,
    172		.cfa_baycfg	= ISC_BAY_CFG_RGRG,
    173	},
    174	{
    175		.fourcc		= V4L2_PIX_FMT_GREY,
    176		.mbus_code	= MEDIA_BUS_FMT_Y8_1X8,
    177		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,
    178	},
    179	{
    180		.fourcc		= V4L2_PIX_FMT_YUYV,
    181		.mbus_code	= MEDIA_BUS_FMT_YUYV8_2X8,
    182		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,
    183	},
    184	{
    185		.fourcc		= V4L2_PIX_FMT_RGB565,
    186		.mbus_code	= MEDIA_BUS_FMT_RGB565_2X8_LE,
    187		.pfe_cfg0_bps	= ISC_PFE_CFG0_BPS_EIGHT,
    188	},
    189	{
    190		.fourcc		= V4L2_PIX_FMT_Y10,
    191		.mbus_code	= MEDIA_BUS_FMT_Y10_1X10,
    192		.pfe_cfg0_bps	= ISC_PFG_CFG0_BPS_TEN,
    193	},
    194
    195};
    196
    197static void isc_sama5d2_config_csc(struct isc_device *isc)
    198{
    199	struct regmap *regmap = isc->regmap;
    200
    201	/* Convert RGB to YUV */
    202	regmap_write(regmap, ISC_CSC_YR_YG + isc->offsets.csc,
    203		     0x42 | (0x81 << 16));
    204	regmap_write(regmap, ISC_CSC_YB_OY + isc->offsets.csc,
    205		     0x19 | (0x10 << 16));
    206	regmap_write(regmap, ISC_CSC_CBR_CBG + isc->offsets.csc,
    207		     0xFDA | (0xFB6 << 16));
    208	regmap_write(regmap, ISC_CSC_CBB_OCB + isc->offsets.csc,
    209		     0x70 | (0x80 << 16));
    210	regmap_write(regmap, ISC_CSC_CRR_CRG + isc->offsets.csc,
    211		     0x70 | (0xFA2 << 16));
    212	regmap_write(regmap, ISC_CSC_CRB_OCR + isc->offsets.csc,
    213		     0xFEE | (0x80 << 16));
    214}
    215
    216static void isc_sama5d2_config_cbc(struct isc_device *isc)
    217{
    218	struct regmap *regmap = isc->regmap;
    219
    220	regmap_write(regmap, ISC_CBC_BRIGHT + isc->offsets.cbc,
    221		     isc->ctrls.brightness);
    222	regmap_write(regmap, ISC_CBC_CONTRAST + isc->offsets.cbc,
    223		     isc->ctrls.contrast);
    224}
    225
    226static void isc_sama5d2_config_cc(struct isc_device *isc)
    227{
    228	struct regmap *regmap = isc->regmap;
    229
    230	/* Configure each register at the neutral fixed point 1.0 or 0.0 */
    231	regmap_write(regmap, ISC_CC_RR_RG, (1 << 8));
    232	regmap_write(regmap, ISC_CC_RB_OR, 0);
    233	regmap_write(regmap, ISC_CC_GR_GG, (1 << 8) << 16);
    234	regmap_write(regmap, ISC_CC_GB_OG, 0);
    235	regmap_write(regmap, ISC_CC_BR_BG, 0);
    236	regmap_write(regmap, ISC_CC_BB_OB, (1 << 8));
    237}
    238
    239static void isc_sama5d2_config_ctrls(struct isc_device *isc,
    240				     const struct v4l2_ctrl_ops *ops)
    241{
    242	struct isc_ctrls *ctrls = &isc->ctrls;
    243	struct v4l2_ctrl_handler *hdl = &ctrls->handler;
    244
    245	ctrls->contrast = 256;
    246
    247	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -2048, 2047, 1, 256);
    248}
    249
    250static void isc_sama5d2_config_dpc(struct isc_device *isc)
    251{
    252	/* This module is not present on sama5d2 pipeline */
    253}
    254
    255static void isc_sama5d2_config_gam(struct isc_device *isc)
    256{
    257	/* No specific gamma configuration */
    258}
    259
    260static void isc_sama5d2_config_rlp(struct isc_device *isc)
    261{
    262	struct regmap *regmap = isc->regmap;
    263	u32 rlp_mode = isc->config.rlp_cfg_mode;
    264
    265	/*
    266	 * In sama5d2, the YUV planar modes and the YUYV modes are treated
    267	 * in the same way in RLP register.
    268	 * Normally, YYCC mode should be Luma(n) - Color B(n) - Color R (n)
    269	 * and YCYC should be Luma(n + 1) - Color B (n) - Luma (n) - Color R (n)
    270	 * but in sama5d2, the YCYC mode does not exist, and YYCC must be
    271	 * selected for both planar and interleaved modes, as in fact
    272	 * both modes are supported.
    273	 *
    274	 * Thus, if the YCYC mode is selected, replace it with the
    275	 * sama5d2-compliant mode which is YYCC .
    276	 */
    277	if ((rlp_mode & ISC_RLP_CFG_MODE_MASK) == ISC_RLP_CFG_MODE_YCYC) {
    278		rlp_mode &= ~ISC_RLP_CFG_MODE_MASK;
    279		rlp_mode |= ISC_RLP_CFG_MODE_YYCC;
    280	}
    281
    282	regmap_update_bits(regmap, ISC_RLP_CFG + isc->offsets.rlp,
    283			   ISC_RLP_CFG_MODE_MASK, rlp_mode);
    284}
    285
    286static void isc_sama5d2_adapt_pipeline(struct isc_device *isc)
    287{
    288	isc->try_config.bits_pipeline &= ISC_SAMA5D2_PIPELINE;
    289}
    290
    291/* Gamma table with gamma 1/2.2 */
    292static const u32 isc_sama5d2_gamma_table[][GAMMA_ENTRIES] = {
    293	/* 0 --> gamma 1/1.8 */
    294	{      0x65,  0x66002F,  0x950025,  0xBB0020,  0xDB001D,  0xF8001A,
    295	  0x1130018, 0x12B0017, 0x1420016, 0x1580014, 0x16D0013, 0x1810012,
    296	  0x1940012, 0x1A60012, 0x1B80011, 0x1C90010, 0x1DA0010, 0x1EA000F,
    297	  0x1FA000F, 0x209000F, 0x218000F, 0x227000E, 0x235000E, 0x243000E,
    298	  0x251000E, 0x25F000D, 0x26C000D, 0x279000D, 0x286000D, 0x293000C,
    299	  0x2A0000C, 0x2AC000C, 0x2B8000C, 0x2C4000C, 0x2D0000B, 0x2DC000B,
    300	  0x2E7000B, 0x2F3000B, 0x2FE000B, 0x309000B, 0x314000B, 0x31F000A,
    301	  0x32A000A, 0x334000B, 0x33F000A, 0x349000A, 0x354000A, 0x35E000A,
    302	  0x368000A, 0x372000A, 0x37C000A, 0x386000A, 0x3900009, 0x399000A,
    303	  0x3A30009, 0x3AD0009, 0x3B60009, 0x3BF000A, 0x3C90009, 0x3D20009,
    304	  0x3DB0009, 0x3E40009, 0x3ED0009, 0x3F60009 },
    305
    306	/* 1 --> gamma 1/2 */
    307	{      0x7F,  0x800034,  0xB50028,  0xDE0021, 0x100001E, 0x11E001B,
    308	  0x1390019, 0x1520017, 0x16A0015, 0x1800014, 0x1940014, 0x1A80013,
    309	  0x1BB0012, 0x1CD0011, 0x1DF0010, 0x1EF0010, 0x200000F, 0x20F000F,
    310	  0x21F000E, 0x22D000F, 0x23C000E, 0x24A000E, 0x258000D, 0x265000D,
    311	  0x273000C, 0x27F000D, 0x28C000C, 0x299000C, 0x2A5000C, 0x2B1000B,
    312	  0x2BC000C, 0x2C8000B, 0x2D3000C, 0x2DF000B, 0x2EA000A, 0x2F5000A,
    313	  0x2FF000B, 0x30A000A, 0x314000B, 0x31F000A, 0x329000A, 0x333000A,
    314	  0x33D0009, 0x3470009, 0x350000A, 0x35A0009, 0x363000A, 0x36D0009,
    315	  0x3760009, 0x37F0009, 0x3880009, 0x3910009, 0x39A0009, 0x3A30009,
    316	  0x3AC0008, 0x3B40009, 0x3BD0008, 0x3C60008, 0x3CE0008, 0x3D60009,
    317	  0x3DF0008, 0x3E70008, 0x3EF0008, 0x3F70008 },
    318
    319	/* 2 --> gamma 1/2.2 */
    320	{      0x99,  0x9B0038,  0xD4002A,  0xFF0023, 0x122001F, 0x141001B,
    321	  0x15D0019, 0x1760017, 0x18E0015, 0x1A30015, 0x1B80013, 0x1CC0012,
    322	  0x1DE0011, 0x1F00010, 0x2010010, 0x2110010, 0x221000F, 0x230000F,
    323	  0x23F000E, 0x24D000E, 0x25B000D, 0x269000C, 0x276000C, 0x283000C,
    324	  0x28F000C, 0x29B000C, 0x2A7000C, 0x2B3000B, 0x2BF000B, 0x2CA000B,
    325	  0x2D5000B, 0x2E0000A, 0x2EB000A, 0x2F5000A, 0x2FF000A, 0x30A000A,
    326	  0x3140009, 0x31E0009, 0x327000A, 0x3310009, 0x33A0009, 0x3440009,
    327	  0x34D0009, 0x3560009, 0x35F0009, 0x3680008, 0x3710008, 0x3790009,
    328	  0x3820008, 0x38A0008, 0x3930008, 0x39B0008, 0x3A30008, 0x3AB0008,
    329	  0x3B30008, 0x3BB0008, 0x3C30008, 0x3CB0007, 0x3D20008, 0x3DA0007,
    330	  0x3E20007, 0x3E90007, 0x3F00008, 0x3F80007 },
    331};
    332
    333static int isc_parse_dt(struct device *dev, struct isc_device *isc)
    334{
    335	struct device_node *np = dev->of_node;
    336	struct device_node *epn = NULL;
    337	struct isc_subdev_entity *subdev_entity;
    338	unsigned int flags;
    339	int ret;
    340
    341	INIT_LIST_HEAD(&isc->subdev_entities);
    342
    343	while (1) {
    344		struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 };
    345
    346		epn = of_graph_get_next_endpoint(np, epn);
    347		if (!epn)
    348			return 0;
    349
    350		ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn),
    351						 &v4l2_epn);
    352		if (ret) {
    353			ret = -EINVAL;
    354			dev_err(dev, "Could not parse the endpoint\n");
    355			break;
    356		}
    357
    358		subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity),
    359					     GFP_KERNEL);
    360		if (!subdev_entity) {
    361			ret = -ENOMEM;
    362			break;
    363		}
    364		subdev_entity->epn = epn;
    365
    366		flags = v4l2_epn.bus.parallel.flags;
    367
    368		if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
    369			subdev_entity->pfe_cfg0 = ISC_PFE_CFG0_HPOL_LOW;
    370
    371		if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
    372			subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_VPOL_LOW;
    373
    374		if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
    375			subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_PPOL_LOW;
    376
    377		if (v4l2_epn.bus_type == V4L2_MBUS_BT656)
    378			subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_CCIR_CRC |
    379					ISC_PFE_CFG0_CCIR656;
    380
    381		list_add_tail(&subdev_entity->list, &isc->subdev_entities);
    382	}
    383	of_node_put(epn);
    384
    385	return ret;
    386}
    387
    388static int atmel_isc_probe(struct platform_device *pdev)
    389{
    390	struct device *dev = &pdev->dev;
    391	struct isc_device *isc;
    392	struct resource *res;
    393	void __iomem *io_base;
    394	struct isc_subdev_entity *subdev_entity;
    395	int irq;
    396	int ret;
    397	u32 ver;
    398
    399	isc = devm_kzalloc(dev, sizeof(*isc), GFP_KERNEL);
    400	if (!isc)
    401		return -ENOMEM;
    402
    403	platform_set_drvdata(pdev, isc);
    404	isc->dev = dev;
    405
    406	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    407	io_base = devm_ioremap_resource(dev, res);
    408	if (IS_ERR(io_base))
    409		return PTR_ERR(io_base);
    410
    411	isc->regmap = devm_regmap_init_mmio(dev, io_base, &isc_regmap_config);
    412	if (IS_ERR(isc->regmap)) {
    413		ret = PTR_ERR(isc->regmap);
    414		dev_err(dev, "failed to init register map: %d\n", ret);
    415		return ret;
    416	}
    417
    418	irq = platform_get_irq(pdev, 0);
    419	if (irq < 0)
    420		return irq;
    421
    422	ret = devm_request_irq(dev, irq, isc_interrupt, 0,
    423			       "atmel-sama5d2-isc", isc);
    424	if (ret < 0) {
    425		dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n",
    426			irq, ret);
    427		return ret;
    428	}
    429
    430	isc->gamma_table = isc_sama5d2_gamma_table;
    431	isc->gamma_max = 2;
    432
    433	isc->max_width = ISC_SAMA5D2_MAX_SUPPORT_WIDTH;
    434	isc->max_height = ISC_SAMA5D2_MAX_SUPPORT_HEIGHT;
    435
    436	isc->config_dpc = isc_sama5d2_config_dpc;
    437	isc->config_csc = isc_sama5d2_config_csc;
    438	isc->config_cbc = isc_sama5d2_config_cbc;
    439	isc->config_cc = isc_sama5d2_config_cc;
    440	isc->config_gam = isc_sama5d2_config_gam;
    441	isc->config_rlp = isc_sama5d2_config_rlp;
    442	isc->config_ctrls = isc_sama5d2_config_ctrls;
    443
    444	isc->adapt_pipeline = isc_sama5d2_adapt_pipeline;
    445
    446	isc->offsets.csc = ISC_SAMA5D2_CSC_OFFSET;
    447	isc->offsets.cbc = ISC_SAMA5D2_CBC_OFFSET;
    448	isc->offsets.sub422 = ISC_SAMA5D2_SUB422_OFFSET;
    449	isc->offsets.sub420 = ISC_SAMA5D2_SUB420_OFFSET;
    450	isc->offsets.rlp = ISC_SAMA5D2_RLP_OFFSET;
    451	isc->offsets.his = ISC_SAMA5D2_HIS_OFFSET;
    452	isc->offsets.dma = ISC_SAMA5D2_DMA_OFFSET;
    453	isc->offsets.version = ISC_SAMA5D2_VERSION_OFFSET;
    454	isc->offsets.his_entry = ISC_SAMA5D2_HIS_ENTRY_OFFSET;
    455
    456	isc->controller_formats = sama5d2_controller_formats;
    457	isc->controller_formats_size = ARRAY_SIZE(sama5d2_controller_formats);
    458	isc->formats_list = sama5d2_formats_list;
    459	isc->formats_list_size = ARRAY_SIZE(sama5d2_formats_list);
    460
    461	/* sama5d2-isc - 8 bits per beat */
    462	isc->dcfg = ISC_DCFG_YMBSIZE_BEATS8 | ISC_DCFG_CMBSIZE_BEATS8;
    463
    464	/* sama5d2-isc : ISPCK is required and mandatory */
    465	isc->ispck_required = true;
    466
    467	ret = isc_pipeline_init(isc);
    468	if (ret)
    469		return ret;
    470
    471	isc->hclock = devm_clk_get(dev, "hclock");
    472	if (IS_ERR(isc->hclock)) {
    473		ret = PTR_ERR(isc->hclock);
    474		dev_err(dev, "failed to get hclock: %d\n", ret);
    475		return ret;
    476	}
    477
    478	ret = clk_prepare_enable(isc->hclock);
    479	if (ret) {
    480		dev_err(dev, "failed to enable hclock: %d\n", ret);
    481		return ret;
    482	}
    483
    484	ret = isc_clk_init(isc);
    485	if (ret) {
    486		dev_err(dev, "failed to init isc clock: %d\n", ret);
    487		goto unprepare_hclk;
    488	}
    489	ret = v4l2_device_register(dev, &isc->v4l2_dev);
    490	if (ret) {
    491		dev_err(dev, "unable to register v4l2 device.\n");
    492		goto unprepare_clk;
    493	}
    494
    495	ret = isc_parse_dt(dev, isc);
    496	if (ret) {
    497		dev_err(dev, "fail to parse device tree\n");
    498		goto unregister_v4l2_device;
    499	}
    500
    501	if (list_empty(&isc->subdev_entities)) {
    502		dev_err(dev, "no subdev found\n");
    503		ret = -ENODEV;
    504		goto unregister_v4l2_device;
    505	}
    506
    507	list_for_each_entry(subdev_entity, &isc->subdev_entities, list) {
    508		struct v4l2_async_subdev *asd;
    509		struct fwnode_handle *fwnode =
    510			of_fwnode_handle(subdev_entity->epn);
    511
    512		v4l2_async_nf_init(&subdev_entity->notifier);
    513
    514		asd = v4l2_async_nf_add_fwnode_remote(&subdev_entity->notifier,
    515						      fwnode,
    516						      struct v4l2_async_subdev);
    517
    518		of_node_put(subdev_entity->epn);
    519		subdev_entity->epn = NULL;
    520
    521		if (IS_ERR(asd)) {
    522			ret = PTR_ERR(asd);
    523			goto cleanup_subdev;
    524		}
    525
    526		subdev_entity->notifier.ops = &isc_async_ops;
    527
    528		ret = v4l2_async_nf_register(&isc->v4l2_dev,
    529					     &subdev_entity->notifier);
    530		if (ret) {
    531			dev_err(dev, "fail to register async notifier\n");
    532			goto cleanup_subdev;
    533		}
    534
    535		if (video_is_registered(&isc->video_dev))
    536			break;
    537	}
    538
    539	pm_runtime_set_active(dev);
    540	pm_runtime_enable(dev);
    541	pm_request_idle(dev);
    542
    543	isc->ispck = isc->isc_clks[ISC_ISPCK].clk;
    544
    545	ret = clk_prepare_enable(isc->ispck);
    546	if (ret) {
    547		dev_err(dev, "failed to enable ispck: %d\n", ret);
    548		goto disable_pm;
    549	}
    550
    551	/* ispck should be greater or equal to hclock */
    552	ret = clk_set_rate(isc->ispck, clk_get_rate(isc->hclock));
    553	if (ret) {
    554		dev_err(dev, "failed to set ispck rate: %d\n", ret);
    555		goto unprepare_clk;
    556	}
    557
    558	regmap_read(isc->regmap, ISC_VERSION + isc->offsets.version, &ver);
    559	dev_info(dev, "Microchip ISC version %x\n", ver);
    560
    561	return 0;
    562
    563unprepare_clk:
    564	clk_disable_unprepare(isc->ispck);
    565
    566disable_pm:
    567	pm_runtime_disable(dev);
    568
    569cleanup_subdev:
    570	isc_subdev_cleanup(isc);
    571
    572unregister_v4l2_device:
    573	v4l2_device_unregister(&isc->v4l2_dev);
    574
    575unprepare_hclk:
    576	clk_disable_unprepare(isc->hclock);
    577
    578	isc_clk_cleanup(isc);
    579
    580	return ret;
    581}
    582
    583static int atmel_isc_remove(struct platform_device *pdev)
    584{
    585	struct isc_device *isc = platform_get_drvdata(pdev);
    586
    587	pm_runtime_disable(&pdev->dev);
    588
    589	isc_subdev_cleanup(isc);
    590
    591	v4l2_device_unregister(&isc->v4l2_dev);
    592
    593	clk_disable_unprepare(isc->ispck);
    594	clk_disable_unprepare(isc->hclock);
    595
    596	isc_clk_cleanup(isc);
    597
    598	return 0;
    599}
    600
    601static int __maybe_unused isc_runtime_suspend(struct device *dev)
    602{
    603	struct isc_device *isc = dev_get_drvdata(dev);
    604
    605	clk_disable_unprepare(isc->ispck);
    606	clk_disable_unprepare(isc->hclock);
    607
    608	return 0;
    609}
    610
    611static int __maybe_unused isc_runtime_resume(struct device *dev)
    612{
    613	struct isc_device *isc = dev_get_drvdata(dev);
    614	int ret;
    615
    616	ret = clk_prepare_enable(isc->hclock);
    617	if (ret)
    618		return ret;
    619
    620	ret = clk_prepare_enable(isc->ispck);
    621	if (ret)
    622		clk_disable_unprepare(isc->hclock);
    623
    624	return ret;
    625}
    626
    627static const struct dev_pm_ops atmel_isc_dev_pm_ops = {
    628	SET_RUNTIME_PM_OPS(isc_runtime_suspend, isc_runtime_resume, NULL)
    629};
    630
    631#if IS_ENABLED(CONFIG_OF)
    632static const struct of_device_id atmel_isc_of_match[] = {
    633	{ .compatible = "atmel,sama5d2-isc" },
    634	{ }
    635};
    636MODULE_DEVICE_TABLE(of, atmel_isc_of_match);
    637#endif
    638
    639static struct platform_driver atmel_isc_driver = {
    640	.probe	= atmel_isc_probe,
    641	.remove	= atmel_isc_remove,
    642	.driver	= {
    643		.name		= "atmel-sama5d2-isc",
    644		.pm		= &atmel_isc_dev_pm_ops,
    645		.of_match_table = of_match_ptr(atmel_isc_of_match),
    646	},
    647};
    648
    649module_platform_driver(atmel_isc_driver);
    650
    651MODULE_AUTHOR("Songjun Wu");
    652MODULE_DESCRIPTION("The V4L2 driver for Atmel-ISC");
    653MODULE_LICENSE("GPL v2");