Loading...
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Microchip eXtended Image Sensor Controller (XISC) driver
4 *
5 * Copyright (C) 2019-2021 Microchip Technology, Inc. and its subsidiaries
6 *
7 * Author: Eugen Hristev <eugen.hristev@microchip.com>
8 *
9 * Sensor-->PFE-->DPC-->WB-->CFA-->CC-->GAM-->VHXS-->CSC-->CBHS-->SUB-->RLP-->DMA-->HIS
10 *
11 * ISC video pipeline integrates the following submodules:
12 * PFE: Parallel Front End to sample the camera sensor input stream
13 * DPC: Defective Pixel Correction with black offset correction, green disparity
14 * correction and defective pixel correction (3 modules total)
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 *VHXS: Vertical and Horizontal Scaler
20 * CSC: Programmable color space conversion
21 *CBHS: Contrast Brightness Hue and Saturation control
22 * SUB: This module performs YCbCr444 to YCbCr420 chrominance subsampling
23 * RLP: This module performs rounding, range limiting
24 * and packing of the incoming data
25 * DMA: This module performs DMA master accesses to write frames to external RAM
26 * HIS: Histogram module performs statistic counters on the frames
27 */
28
29#include <linux/clk.h>
30#include <linux/clkdev.h>
31#include <linux/clk-provider.h>
32#include <linux/delay.h>
33#include <linux/interrupt.h>
34#include <linux/math64.h>
35#include <linux/module.h>
36#include <linux/of.h>
37#include <linux/of_graph.h>
38#include <linux/platform_device.h>
39#include <linux/pm_runtime.h>
40#include <linux/regmap.h>
41#include <linux/videodev2.h>
42
43#include <media/v4l2-ctrls.h>
44#include <media/v4l2-device.h>
45#include <media/v4l2-event.h>
46#include <media/v4l2-image-sizes.h>
47#include <media/v4l2-ioctl.h>
48#include <media/v4l2-fwnode.h>
49#include <media/v4l2-subdev.h>
50#include <media/videobuf2-dma-contig.h>
51
52#include "microchip-isc-regs.h"
53#include "microchip-isc.h"
54
55#define ISC_SAMA7G5_MAX_SUPPORT_WIDTH 3264
56#define ISC_SAMA7G5_MAX_SUPPORT_HEIGHT 2464
57
58#define ISC_SAMA7G5_PIPELINE \
59 (WB_ENABLE | CFA_ENABLE | CC_ENABLE | GAM_ENABLES | CSC_ENABLE | \
60 CBC_ENABLE | SUB422_ENABLE | SUB420_ENABLE)
61
62/* This is a list of the formats that the ISC can *output* */
63static const struct isc_format sama7g5_controller_formats[] = {
64 {
65 .fourcc = V4L2_PIX_FMT_ARGB444,
66 }, {
67 .fourcc = V4L2_PIX_FMT_ARGB555,
68 }, {
69 .fourcc = V4L2_PIX_FMT_RGB565,
70 }, {
71 .fourcc = V4L2_PIX_FMT_ABGR32,
72 }, {
73 .fourcc = V4L2_PIX_FMT_XBGR32,
74 }, {
75 .fourcc = V4L2_PIX_FMT_YUV420,
76 }, {
77 .fourcc = V4L2_PIX_FMT_UYVY,
78 }, {
79 .fourcc = V4L2_PIX_FMT_VYUY,
80 }, {
81 .fourcc = V4L2_PIX_FMT_YUYV,
82 }, {
83 .fourcc = V4L2_PIX_FMT_YUV422P,
84 }, {
85 .fourcc = V4L2_PIX_FMT_GREY,
86 }, {
87 .fourcc = V4L2_PIX_FMT_Y10,
88 }, {
89 .fourcc = V4L2_PIX_FMT_Y16,
90 }, {
91 .fourcc = V4L2_PIX_FMT_SBGGR8,
92 .raw = true,
93 }, {
94 .fourcc = V4L2_PIX_FMT_SGBRG8,
95 .raw = true,
96 }, {
97 .fourcc = V4L2_PIX_FMT_SGRBG8,
98 .raw = true,
99 }, {
100 .fourcc = V4L2_PIX_FMT_SRGGB8,
101 .raw = true,
102 }, {
103 .fourcc = V4L2_PIX_FMT_SBGGR10,
104 .raw = true,
105 }, {
106 .fourcc = V4L2_PIX_FMT_SGBRG10,
107 .raw = true,
108 }, {
109 .fourcc = V4L2_PIX_FMT_SGRBG10,
110 .raw = true,
111 }, {
112 .fourcc = V4L2_PIX_FMT_SRGGB10,
113 .raw = true,
114 }, {
115 .fourcc = V4L2_PIX_FMT_SBGGR12,
116 .raw = true,
117 }, {
118 .fourcc = V4L2_PIX_FMT_SGBRG12,
119 .raw = true,
120 }, {
121 .fourcc = V4L2_PIX_FMT_SGRBG12,
122 .raw = true,
123 }, {
124 .fourcc = V4L2_PIX_FMT_SRGGB12,
125 .raw = true,
126 },
127};
128
129/* This is a list of formats that the ISC can receive as *input* */
130static struct isc_format sama7g5_formats_list[] = {
131 {
132 .fourcc = V4L2_PIX_FMT_SBGGR8,
133 .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
134 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
135 .cfa_baycfg = ISC_BAY_CFG_BGBG,
136 },
137 {
138 .fourcc = V4L2_PIX_FMT_SGBRG8,
139 .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8,
140 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
141 .cfa_baycfg = ISC_BAY_CFG_GBGB,
142 },
143 {
144 .fourcc = V4L2_PIX_FMT_SGRBG8,
145 .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,
146 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
147 .cfa_baycfg = ISC_BAY_CFG_GRGR,
148 },
149 {
150 .fourcc = V4L2_PIX_FMT_SRGGB8,
151 .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8,
152 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
153 .cfa_baycfg = ISC_BAY_CFG_RGRG,
154 },
155 {
156 .fourcc = V4L2_PIX_FMT_SBGGR10,
157 .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
158 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
159 .cfa_baycfg = ISC_BAY_CFG_RGRG,
160 },
161 {
162 .fourcc = V4L2_PIX_FMT_SGBRG10,
163 .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
164 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
165 .cfa_baycfg = ISC_BAY_CFG_GBGB,
166 },
167 {
168 .fourcc = V4L2_PIX_FMT_SGRBG10,
169 .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
170 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
171 .cfa_baycfg = ISC_BAY_CFG_GRGR,
172 },
173 {
174 .fourcc = V4L2_PIX_FMT_SRGGB10,
175 .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
176 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
177 .cfa_baycfg = ISC_BAY_CFG_RGRG,
178 },
179 {
180 .fourcc = V4L2_PIX_FMT_SBGGR12,
181 .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12,
182 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE,
183 .cfa_baycfg = ISC_BAY_CFG_BGBG,
184 },
185 {
186 .fourcc = V4L2_PIX_FMT_SGBRG12,
187 .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12,
188 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE,
189 .cfa_baycfg = ISC_BAY_CFG_GBGB,
190 },
191 {
192 .fourcc = V4L2_PIX_FMT_SGRBG12,
193 .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
194 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE,
195 .cfa_baycfg = ISC_BAY_CFG_GRGR,
196 },
197 {
198 .fourcc = V4L2_PIX_FMT_SRGGB12,
199 .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12,
200 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE,
201 .cfa_baycfg = ISC_BAY_CFG_RGRG,
202 },
203 {
204 .fourcc = V4L2_PIX_FMT_GREY,
205 .mbus_code = MEDIA_BUS_FMT_Y8_1X8,
206 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
207 },
208 {
209 .fourcc = V4L2_PIX_FMT_YUYV,
210 .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
211 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
212 },
213 {
214 .fourcc = V4L2_PIX_FMT_UYVY,
215 .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
216 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
217 },
218 {
219 .fourcc = V4L2_PIX_FMT_RGB565,
220 .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE,
221 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
222 },
223 {
224 .fourcc = V4L2_PIX_FMT_Y10,
225 .mbus_code = MEDIA_BUS_FMT_Y10_1X10,
226 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
227 },
228};
229
230static void isc_sama7g5_config_csc(struct isc_device *isc)
231{
232 struct regmap *regmap = isc->regmap;
233
234 /* Convert RGB to YUV */
235 regmap_write(regmap, ISC_CSC_YR_YG + isc->offsets.csc,
236 0x42 | (0x81 << 16));
237 regmap_write(regmap, ISC_CSC_YB_OY + isc->offsets.csc,
238 0x19 | (0x10 << 16));
239 regmap_write(regmap, ISC_CSC_CBR_CBG + isc->offsets.csc,
240 0xFDA | (0xFB6 << 16));
241 regmap_write(regmap, ISC_CSC_CBB_OCB + isc->offsets.csc,
242 0x70 | (0x80 << 16));
243 regmap_write(regmap, ISC_CSC_CRR_CRG + isc->offsets.csc,
244 0x70 | (0xFA2 << 16));
245 regmap_write(regmap, ISC_CSC_CRB_OCR + isc->offsets.csc,
246 0xFEE | (0x80 << 16));
247}
248
249static void isc_sama7g5_config_cbc(struct isc_device *isc)
250{
251 struct regmap *regmap = isc->regmap;
252
253 /* Configure what is set via v4l2 ctrls */
254 regmap_write(regmap, ISC_CBC_BRIGHT + isc->offsets.cbc, isc->ctrls.brightness);
255 regmap_write(regmap, ISC_CBC_CONTRAST + isc->offsets.cbc, isc->ctrls.contrast);
256 /* Configure Hue and Saturation as neutral midpoint */
257 regmap_write(regmap, ISC_CBCHS_HUE, 0);
258 regmap_write(regmap, ISC_CBCHS_SAT, (1 << 4));
259}
260
261static void isc_sama7g5_config_cc(struct isc_device *isc)
262{
263 struct regmap *regmap = isc->regmap;
264
265 /* Configure each register at the neutral fixed point 1.0 or 0.0 */
266 regmap_write(regmap, ISC_CC_RR_RG, (1 << 8));
267 regmap_write(regmap, ISC_CC_RB_OR, 0);
268 regmap_write(regmap, ISC_CC_GR_GG, (1 << 8) << 16);
269 regmap_write(regmap, ISC_CC_GB_OG, 0);
270 regmap_write(regmap, ISC_CC_BR_BG, 0);
271 regmap_write(regmap, ISC_CC_BB_OB, (1 << 8));
272}
273
274static void isc_sama7g5_config_ctrls(struct isc_device *isc,
275 const struct v4l2_ctrl_ops *ops)
276{
277 struct isc_ctrls *ctrls = &isc->ctrls;
278 struct v4l2_ctrl_handler *hdl = &ctrls->handler;
279
280 ctrls->contrast = 16;
281
282 v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -2048, 2047, 1, 16);
283}
284
285static void isc_sama7g5_config_dpc(struct isc_device *isc)
286{
287 u32 bay_cfg = isc->config.sd_format->cfa_baycfg;
288 struct regmap *regmap = isc->regmap;
289
290 regmap_update_bits(regmap, ISC_DPC_CFG, ISC_DPC_CFG_BLOFF_MASK,
291 (64 << ISC_DPC_CFG_BLOFF_SHIFT));
292 regmap_update_bits(regmap, ISC_DPC_CFG, ISC_DPC_CFG_BAYCFG_MASK,
293 (bay_cfg << ISC_DPC_CFG_BAYCFG_SHIFT));
294}
295
296static void isc_sama7g5_config_gam(struct isc_device *isc)
297{
298 struct regmap *regmap = isc->regmap;
299
300 regmap_update_bits(regmap, ISC_GAM_CTRL, ISC_GAM_CTRL_BIPART,
301 ISC_GAM_CTRL_BIPART);
302}
303
304static void isc_sama7g5_config_rlp(struct isc_device *isc)
305{
306 struct regmap *regmap = isc->regmap;
307 u32 rlp_mode = isc->config.rlp_cfg_mode;
308
309 regmap_update_bits(regmap, ISC_RLP_CFG + isc->offsets.rlp,
310 ISC_RLP_CFG_MODE_MASK | ISC_RLP_CFG_LSH |
311 ISC_RLP_CFG_YMODE_MASK, rlp_mode);
312}
313
314static void isc_sama7g5_adapt_pipeline(struct isc_device *isc)
315{
316 isc->try_config.bits_pipeline &= ISC_SAMA7G5_PIPELINE;
317}
318
319/* Gamma table with gamma 1/2.2 */
320static const u32 isc_sama7g5_gamma_table[][GAMMA_ENTRIES] = {
321 /* index 0 --> gamma bipartite */
322 {
323 0x980, 0x4c0320, 0x650260, 0x7801e0, 0x8701a0, 0x940180,
324 0xa00160, 0xab0120, 0xb40120, 0xbd0120, 0xc60100, 0xce0100,
325 0xd600e0, 0xdd00e0, 0xe400e0, 0xeb00c0, 0xf100c0, 0xf700c0,
326 0xfd00c0, 0x10300a0, 0x10800c0, 0x10e00a0, 0x11300a0, 0x11800a0,
327 0x11d00a0, 0x12200a0, 0x12700a0, 0x12c0080, 0x13000a0, 0x1350080,
328 0x13900a0, 0x13e0080, 0x1420076, 0x17d0062, 0x1ae0054, 0x1d8004a,
329 0x1fd0044, 0x21f003e, 0x23e003a, 0x25b0036, 0x2760032, 0x28f0030,
330 0x2a7002e, 0x2be002c, 0x2d4002c, 0x2ea0028, 0x2fe0028, 0x3120026,
331 0x3250024, 0x3370024, 0x3490022, 0x35a0022, 0x36b0020, 0x37b0020,
332 0x38b0020, 0x39b001e, 0x3aa001e, 0x3b9001c, 0x3c7001c, 0x3d5001c,
333 0x3e3001c, 0x3f1001c, 0x3ff001a, 0x40c001a },
334};
335
336static int xisc_parse_dt(struct device *dev, struct isc_device *isc)
337{
338 struct device_node *np = dev->of_node;
339 struct device_node *epn = NULL;
340 struct isc_subdev_entity *subdev_entity;
341 unsigned int flags;
342 int ret;
343 bool mipi_mode;
344
345 INIT_LIST_HEAD(&isc->subdev_entities);
346
347 mipi_mode = of_property_read_bool(np, "microchip,mipi-mode");
348
349 while (1) {
350 struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 };
351
352 epn = of_graph_get_next_endpoint(np, epn);
353 if (!epn)
354 return 0;
355
356 ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn),
357 &v4l2_epn);
358 if (ret) {
359 ret = -EINVAL;
360 dev_err(dev, "Could not parse the endpoint\n");
361 break;
362 }
363
364 subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity),
365 GFP_KERNEL);
366 if (!subdev_entity) {
367 ret = -ENOMEM;
368 break;
369 }
370 subdev_entity->epn = epn;
371
372 flags = v4l2_epn.bus.parallel.flags;
373
374 if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
375 subdev_entity->pfe_cfg0 = ISC_PFE_CFG0_HPOL_LOW;
376
377 if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
378 subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_VPOL_LOW;
379
380 if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
381 subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_PPOL_LOW;
382
383 if (v4l2_epn.bus_type == V4L2_MBUS_BT656)
384 subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_CCIR_CRC |
385 ISC_PFE_CFG0_CCIR656;
386
387 if (mipi_mode)
388 subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_MIPI;
389
390 list_add_tail(&subdev_entity->list, &isc->subdev_entities);
391 }
392 of_node_put(epn);
393
394 return ret;
395}
396
397static int microchip_xisc_probe(struct platform_device *pdev)
398{
399 struct device *dev = &pdev->dev;
400 struct isc_device *isc;
401 struct resource *res;
402 void __iomem *io_base;
403 struct isc_subdev_entity *subdev_entity;
404 int irq;
405 int ret;
406 u32 ver;
407
408 isc = devm_kzalloc(dev, sizeof(*isc), GFP_KERNEL);
409 if (!isc)
410 return -ENOMEM;
411
412 platform_set_drvdata(pdev, isc);
413 isc->dev = dev;
414
415 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
416 io_base = devm_ioremap_resource(dev, res);
417 if (IS_ERR(io_base))
418 return PTR_ERR(io_base);
419
420 isc->regmap = devm_regmap_init_mmio(dev, io_base, µchip_isc_regmap_config);
421 if (IS_ERR(isc->regmap)) {
422 ret = PTR_ERR(isc->regmap);
423 dev_err(dev, "failed to init register map: %d\n", ret);
424 return ret;
425 }
426
427 irq = platform_get_irq(pdev, 0);
428 if (irq < 0)
429 return irq;
430
431 ret = devm_request_irq(dev, irq, microchip_isc_interrupt, 0,
432 "microchip-sama7g5-xisc", isc);
433 if (ret < 0) {
434 dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n",
435 irq, ret);
436 return ret;
437 }
438
439 isc->gamma_table = isc_sama7g5_gamma_table;
440 isc->gamma_max = 0;
441
442 isc->max_width = ISC_SAMA7G5_MAX_SUPPORT_WIDTH;
443 isc->max_height = ISC_SAMA7G5_MAX_SUPPORT_HEIGHT;
444
445 isc->config_dpc = isc_sama7g5_config_dpc;
446 isc->config_csc = isc_sama7g5_config_csc;
447 isc->config_cbc = isc_sama7g5_config_cbc;
448 isc->config_cc = isc_sama7g5_config_cc;
449 isc->config_gam = isc_sama7g5_config_gam;
450 isc->config_rlp = isc_sama7g5_config_rlp;
451 isc->config_ctrls = isc_sama7g5_config_ctrls;
452
453 isc->adapt_pipeline = isc_sama7g5_adapt_pipeline;
454
455 isc->offsets.csc = ISC_SAMA7G5_CSC_OFFSET;
456 isc->offsets.cbc = ISC_SAMA7G5_CBC_OFFSET;
457 isc->offsets.sub422 = ISC_SAMA7G5_SUB422_OFFSET;
458 isc->offsets.sub420 = ISC_SAMA7G5_SUB420_OFFSET;
459 isc->offsets.rlp = ISC_SAMA7G5_RLP_OFFSET;
460 isc->offsets.his = ISC_SAMA7G5_HIS_OFFSET;
461 isc->offsets.dma = ISC_SAMA7G5_DMA_OFFSET;
462 isc->offsets.version = ISC_SAMA7G5_VERSION_OFFSET;
463 isc->offsets.his_entry = ISC_SAMA7G5_HIS_ENTRY_OFFSET;
464
465 isc->controller_formats = sama7g5_controller_formats;
466 isc->controller_formats_size = ARRAY_SIZE(sama7g5_controller_formats);
467 isc->formats_list = sama7g5_formats_list;
468 isc->formats_list_size = ARRAY_SIZE(sama7g5_formats_list);
469
470 /* sama7g5-isc RAM access port is full AXI4 - 32 bits per beat */
471 isc->dcfg = ISC_DCFG_YMBSIZE_BEATS32 | ISC_DCFG_CMBSIZE_BEATS32;
472
473 /* sama7g5-isc : ISPCK does not exist, ISC is clocked by MCK */
474 isc->ispck_required = false;
475
476 ret = microchip_isc_pipeline_init(isc);
477 if (ret)
478 return ret;
479
480 isc->hclock = devm_clk_get(dev, "hclock");
481 if (IS_ERR(isc->hclock)) {
482 ret = PTR_ERR(isc->hclock);
483 dev_err(dev, "failed to get hclock: %d\n", ret);
484 return ret;
485 }
486
487 ret = clk_prepare_enable(isc->hclock);
488 if (ret) {
489 dev_err(dev, "failed to enable hclock: %d\n", ret);
490 return ret;
491 }
492
493 ret = microchip_isc_clk_init(isc);
494 if (ret) {
495 dev_err(dev, "failed to init isc clock: %d\n", ret);
496 goto unprepare_hclk;
497 }
498
499 ret = v4l2_device_register(dev, &isc->v4l2_dev);
500 if (ret) {
501 dev_err(dev, "unable to register v4l2 device.\n");
502 goto unprepare_hclk;
503 }
504
505 ret = xisc_parse_dt(dev, isc);
506 if (ret) {
507 dev_err(dev, "fail to parse device tree\n");
508 goto unregister_v4l2_device;
509 }
510
511 if (list_empty(&isc->subdev_entities)) {
512 dev_err(dev, "no subdev found\n");
513 ret = -ENODEV;
514 goto unregister_v4l2_device;
515 }
516
517 list_for_each_entry(subdev_entity, &isc->subdev_entities, list) {
518 struct v4l2_async_subdev *asd;
519 struct fwnode_handle *fwnode =
520 of_fwnode_handle(subdev_entity->epn);
521
522 v4l2_async_nf_init(&subdev_entity->notifier);
523
524 asd = v4l2_async_nf_add_fwnode_remote(&subdev_entity->notifier,
525 fwnode,
526 struct v4l2_async_subdev);
527
528 of_node_put(subdev_entity->epn);
529 subdev_entity->epn = NULL;
530
531 if (IS_ERR(asd)) {
532 ret = PTR_ERR(asd);
533 goto cleanup_subdev;
534 }
535
536 subdev_entity->notifier.ops = µchip_isc_async_ops;
537
538 ret = v4l2_async_nf_register(&isc->v4l2_dev,
539 &subdev_entity->notifier);
540 if (ret) {
541 dev_err(dev, "fail to register async notifier\n");
542 goto cleanup_subdev;
543 }
544
545 if (video_is_registered(&isc->video_dev))
546 break;
547 }
548
549 regmap_read(isc->regmap, ISC_VERSION + isc->offsets.version, &ver);
550
551 ret = isc_mc_init(isc, ver);
552 if (ret < 0)
553 goto isc_probe_mc_init_err;
554
555 pm_runtime_set_active(dev);
556 pm_runtime_enable(dev);
557 pm_request_idle(dev);
558
559 dev_info(dev, "Microchip XISC version %x\n", ver);
560
561 return 0;
562
563isc_probe_mc_init_err:
564 isc_mc_cleanup(isc);
565
566cleanup_subdev:
567 microchip_isc_subdev_cleanup(isc);
568
569unregister_v4l2_device:
570 v4l2_device_unregister(&isc->v4l2_dev);
571
572unprepare_hclk:
573 clk_disable_unprepare(isc->hclock);
574
575 microchip_isc_clk_cleanup(isc);
576
577 return ret;
578}
579
580static int microchip_xisc_remove(struct platform_device *pdev)
581{
582 struct isc_device *isc = platform_get_drvdata(pdev);
583
584 pm_runtime_disable(&pdev->dev);
585
586 isc_mc_cleanup(isc);
587
588 microchip_isc_subdev_cleanup(isc);
589
590 v4l2_device_unregister(&isc->v4l2_dev);
591
592 clk_disable_unprepare(isc->hclock);
593
594 microchip_isc_clk_cleanup(isc);
595
596 return 0;
597}
598
599static int __maybe_unused xisc_runtime_suspend(struct device *dev)
600{
601 struct isc_device *isc = dev_get_drvdata(dev);
602
603 clk_disable_unprepare(isc->hclock);
604
605 return 0;
606}
607
608static int __maybe_unused xisc_runtime_resume(struct device *dev)
609{
610 struct isc_device *isc = dev_get_drvdata(dev);
611 int ret;
612
613 ret = clk_prepare_enable(isc->hclock);
614 if (ret)
615 return ret;
616
617 return ret;
618}
619
620static const struct dev_pm_ops microchip_xisc_dev_pm_ops = {
621 SET_RUNTIME_PM_OPS(xisc_runtime_suspend, xisc_runtime_resume, NULL)
622};
623
624#if IS_ENABLED(CONFIG_OF)
625static const struct of_device_id microchip_xisc_of_match[] = {
626 { .compatible = "microchip,sama7g5-isc" },
627 { }
628};
629MODULE_DEVICE_TABLE(of, microchip_xisc_of_match);
630#endif
631
632static struct platform_driver microchip_xisc_driver = {
633 .probe = microchip_xisc_probe,
634 .remove = microchip_xisc_remove,
635 .driver = {
636 .name = "microchip-sama7g5-xisc",
637 .pm = µchip_xisc_dev_pm_ops,
638 .of_match_table = of_match_ptr(microchip_xisc_of_match),
639 },
640};
641
642module_platform_driver(microchip_xisc_driver);
643
644MODULE_AUTHOR("Eugen Hristev <eugen.hristev@microchip.com>");
645MODULE_DESCRIPTION("The V4L2 driver for Microchip-XISC");
646MODULE_LICENSE("GPL v2");
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Microchip eXtended Image Sensor Controller (XISC) driver
4 *
5 * Copyright (C) 2019-2021 Microchip Technology, Inc. and its subsidiaries
6 *
7 * Author: Eugen Hristev <eugen.hristev@microchip.com>
8 *
9 * Sensor-->PFE-->DPC-->WB-->CFA-->CC-->GAM-->VHXS-->CSC-->CBHS-->SUB-->RLP-->DMA-->HIS
10 *
11 * ISC video pipeline integrates the following submodules:
12 * PFE: Parallel Front End to sample the camera sensor input stream
13 * DPC: Defective Pixel Correction with black offset correction, green disparity
14 * correction and defective pixel correction (3 modules total)
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 *VHXS: Vertical and Horizontal Scaler
20 * CSC: Programmable color space conversion
21 *CBHS: Contrast Brightness Hue and Saturation control
22 * SUB: This module performs YCbCr444 to YCbCr420 chrominance subsampling
23 * RLP: This module performs rounding, range limiting
24 * and packing of the incoming data
25 * DMA: This module performs DMA master accesses to write frames to external RAM
26 * HIS: Histogram module performs statistic counters on the frames
27 */
28
29#include <linux/clk.h>
30#include <linux/clkdev.h>
31#include <linux/clk-provider.h>
32#include <linux/delay.h>
33#include <linux/interrupt.h>
34#include <linux/math64.h>
35#include <linux/module.h>
36#include <linux/of.h>
37#include <linux/of_graph.h>
38#include <linux/platform_device.h>
39#include <linux/pm_runtime.h>
40#include <linux/regmap.h>
41#include <linux/videodev2.h>
42
43#include <media/v4l2-ctrls.h>
44#include <media/v4l2-device.h>
45#include <media/v4l2-event.h>
46#include <media/v4l2-image-sizes.h>
47#include <media/v4l2-ioctl.h>
48#include <media/v4l2-fwnode.h>
49#include <media/v4l2-subdev.h>
50#include <media/videobuf2-dma-contig.h>
51
52#include "microchip-isc-regs.h"
53#include "microchip-isc.h"
54
55#define ISC_SAMA7G5_MAX_SUPPORT_WIDTH 3264
56#define ISC_SAMA7G5_MAX_SUPPORT_HEIGHT 2464
57
58#define ISC_SAMA7G5_PIPELINE \
59 (WB_ENABLE | CFA_ENABLE | CC_ENABLE | GAM_ENABLES | CSC_ENABLE | \
60 CBC_ENABLE | SUB422_ENABLE | SUB420_ENABLE)
61
62/* This is a list of the formats that the ISC can *output* */
63static const struct isc_format sama7g5_controller_formats[] = {
64 {
65 .fourcc = V4L2_PIX_FMT_ARGB444,
66 }, {
67 .fourcc = V4L2_PIX_FMT_ARGB555,
68 }, {
69 .fourcc = V4L2_PIX_FMT_RGB565,
70 }, {
71 .fourcc = V4L2_PIX_FMT_ABGR32,
72 }, {
73 .fourcc = V4L2_PIX_FMT_XBGR32,
74 }, {
75 .fourcc = V4L2_PIX_FMT_YUV420,
76 }, {
77 .fourcc = V4L2_PIX_FMT_UYVY,
78 }, {
79 .fourcc = V4L2_PIX_FMT_VYUY,
80 }, {
81 .fourcc = V4L2_PIX_FMT_YUYV,
82 }, {
83 .fourcc = V4L2_PIX_FMT_YUV422P,
84 }, {
85 .fourcc = V4L2_PIX_FMT_GREY,
86 }, {
87 .fourcc = V4L2_PIX_FMT_Y10,
88 }, {
89 .fourcc = V4L2_PIX_FMT_Y16,
90 }, {
91 .fourcc = V4L2_PIX_FMT_SBGGR8,
92 .raw = true,
93 }, {
94 .fourcc = V4L2_PIX_FMT_SGBRG8,
95 .raw = true,
96 }, {
97 .fourcc = V4L2_PIX_FMT_SGRBG8,
98 .raw = true,
99 }, {
100 .fourcc = V4L2_PIX_FMT_SRGGB8,
101 .raw = true,
102 }, {
103 .fourcc = V4L2_PIX_FMT_SBGGR10,
104 .raw = true,
105 }, {
106 .fourcc = V4L2_PIX_FMT_SGBRG10,
107 .raw = true,
108 }, {
109 .fourcc = V4L2_PIX_FMT_SGRBG10,
110 .raw = true,
111 }, {
112 .fourcc = V4L2_PIX_FMT_SRGGB10,
113 .raw = true,
114 }, {
115 .fourcc = V4L2_PIX_FMT_SBGGR12,
116 .raw = true,
117 }, {
118 .fourcc = V4L2_PIX_FMT_SGBRG12,
119 .raw = true,
120 }, {
121 .fourcc = V4L2_PIX_FMT_SGRBG12,
122 .raw = true,
123 }, {
124 .fourcc = V4L2_PIX_FMT_SRGGB12,
125 .raw = true,
126 },
127};
128
129/* This is a list of formats that the ISC can receive as *input* */
130static struct isc_format sama7g5_formats_list[] = {
131 {
132 .fourcc = V4L2_PIX_FMT_SBGGR8,
133 .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
134 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
135 .cfa_baycfg = ISC_BAY_CFG_BGBG,
136 },
137 {
138 .fourcc = V4L2_PIX_FMT_SGBRG8,
139 .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8,
140 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
141 .cfa_baycfg = ISC_BAY_CFG_GBGB,
142 },
143 {
144 .fourcc = V4L2_PIX_FMT_SGRBG8,
145 .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,
146 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
147 .cfa_baycfg = ISC_BAY_CFG_GRGR,
148 },
149 {
150 .fourcc = V4L2_PIX_FMT_SRGGB8,
151 .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8,
152 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
153 .cfa_baycfg = ISC_BAY_CFG_RGRG,
154 },
155 {
156 .fourcc = V4L2_PIX_FMT_SBGGR10,
157 .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
158 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
159 .cfa_baycfg = ISC_BAY_CFG_RGRG,
160 },
161 {
162 .fourcc = V4L2_PIX_FMT_SGBRG10,
163 .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
164 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
165 .cfa_baycfg = ISC_BAY_CFG_GBGB,
166 },
167 {
168 .fourcc = V4L2_PIX_FMT_SGRBG10,
169 .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
170 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
171 .cfa_baycfg = ISC_BAY_CFG_GRGR,
172 },
173 {
174 .fourcc = V4L2_PIX_FMT_SRGGB10,
175 .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
176 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
177 .cfa_baycfg = ISC_BAY_CFG_RGRG,
178 },
179 {
180 .fourcc = V4L2_PIX_FMT_SBGGR12,
181 .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12,
182 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE,
183 .cfa_baycfg = ISC_BAY_CFG_BGBG,
184 },
185 {
186 .fourcc = V4L2_PIX_FMT_SGBRG12,
187 .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12,
188 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE,
189 .cfa_baycfg = ISC_BAY_CFG_GBGB,
190 },
191 {
192 .fourcc = V4L2_PIX_FMT_SGRBG12,
193 .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
194 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE,
195 .cfa_baycfg = ISC_BAY_CFG_GRGR,
196 },
197 {
198 .fourcc = V4L2_PIX_FMT_SRGGB12,
199 .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12,
200 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE,
201 .cfa_baycfg = ISC_BAY_CFG_RGRG,
202 },
203 {
204 .fourcc = V4L2_PIX_FMT_GREY,
205 .mbus_code = MEDIA_BUS_FMT_Y8_1X8,
206 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
207 },
208 {
209 .fourcc = V4L2_PIX_FMT_YUYV,
210 .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
211 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
212 },
213 {
214 .fourcc = V4L2_PIX_FMT_UYVY,
215 .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
216 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
217 },
218 {
219 .fourcc = V4L2_PIX_FMT_RGB565,
220 .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE,
221 .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
222 },
223 {
224 .fourcc = V4L2_PIX_FMT_Y10,
225 .mbus_code = MEDIA_BUS_FMT_Y10_1X10,
226 .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
227 },
228};
229
230static void isc_sama7g5_config_csc(struct isc_device *isc)
231{
232 struct regmap *regmap = isc->regmap;
233
234 /* Convert RGB to YUV */
235 regmap_write(regmap, ISC_CSC_YR_YG + isc->offsets.csc,
236 0x42 | (0x81 << 16));
237 regmap_write(regmap, ISC_CSC_YB_OY + isc->offsets.csc,
238 0x19 | (0x10 << 16));
239 regmap_write(regmap, ISC_CSC_CBR_CBG + isc->offsets.csc,
240 0xFDA | (0xFB6 << 16));
241 regmap_write(regmap, ISC_CSC_CBB_OCB + isc->offsets.csc,
242 0x70 | (0x80 << 16));
243 regmap_write(regmap, ISC_CSC_CRR_CRG + isc->offsets.csc,
244 0x70 | (0xFA2 << 16));
245 regmap_write(regmap, ISC_CSC_CRB_OCR + isc->offsets.csc,
246 0xFEE | (0x80 << 16));
247}
248
249static void isc_sama7g5_config_cbc(struct isc_device *isc)
250{
251 struct regmap *regmap = isc->regmap;
252
253 /* Configure what is set via v4l2 ctrls */
254 regmap_write(regmap, ISC_CBC_BRIGHT + isc->offsets.cbc, isc->ctrls.brightness);
255 regmap_write(regmap, ISC_CBC_CONTRAST + isc->offsets.cbc, isc->ctrls.contrast);
256 /* Configure Hue and Saturation as neutral midpoint */
257 regmap_write(regmap, ISC_CBCHS_HUE, 0);
258 regmap_write(regmap, ISC_CBCHS_SAT, (1 << 4));
259}
260
261static void isc_sama7g5_config_cc(struct isc_device *isc)
262{
263 struct regmap *regmap = isc->regmap;
264
265 /* Configure each register at the neutral fixed point 1.0 or 0.0 */
266 regmap_write(regmap, ISC_CC_RR_RG, (1 << 8));
267 regmap_write(regmap, ISC_CC_RB_OR, 0);
268 regmap_write(regmap, ISC_CC_GR_GG, (1 << 8) << 16);
269 regmap_write(regmap, ISC_CC_GB_OG, 0);
270 regmap_write(regmap, ISC_CC_BR_BG, 0);
271 regmap_write(regmap, ISC_CC_BB_OB, (1 << 8));
272}
273
274static void isc_sama7g5_config_ctrls(struct isc_device *isc,
275 const struct v4l2_ctrl_ops *ops)
276{
277 struct isc_ctrls *ctrls = &isc->ctrls;
278 struct v4l2_ctrl_handler *hdl = &ctrls->handler;
279
280 ctrls->contrast = 16;
281
282 v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -2048, 2047, 1, 16);
283}
284
285static void isc_sama7g5_config_dpc(struct isc_device *isc)
286{
287 u32 bay_cfg = isc->config.sd_format->cfa_baycfg;
288 struct regmap *regmap = isc->regmap;
289
290 regmap_update_bits(regmap, ISC_DPC_CFG, ISC_DPC_CFG_BLOFF_MASK,
291 (64 << ISC_DPC_CFG_BLOFF_SHIFT));
292 regmap_update_bits(regmap, ISC_DPC_CFG, ISC_DPC_CFG_BAYCFG_MASK,
293 (bay_cfg << ISC_DPC_CFG_BAYCFG_SHIFT));
294}
295
296static void isc_sama7g5_config_gam(struct isc_device *isc)
297{
298 struct regmap *regmap = isc->regmap;
299
300 regmap_update_bits(regmap, ISC_GAM_CTRL, ISC_GAM_CTRL_BIPART,
301 ISC_GAM_CTRL_BIPART);
302}
303
304static void isc_sama7g5_config_rlp(struct isc_device *isc)
305{
306 struct regmap *regmap = isc->regmap;
307 u32 rlp_mode = isc->config.rlp_cfg_mode;
308
309 regmap_update_bits(regmap, ISC_RLP_CFG + isc->offsets.rlp,
310 ISC_RLP_CFG_MODE_MASK | ISC_RLP_CFG_LSH |
311 ISC_RLP_CFG_YMODE_MASK, rlp_mode);
312}
313
314static void isc_sama7g5_adapt_pipeline(struct isc_device *isc)
315{
316 isc->try_config.bits_pipeline &= ISC_SAMA7G5_PIPELINE;
317}
318
319/* Gamma table with gamma 1/2.2 */
320static const u32 isc_sama7g5_gamma_table[][GAMMA_ENTRIES] = {
321 /* index 0 --> gamma bipartite */
322 {
323 0x980, 0x4c0320, 0x650260, 0x7801e0, 0x8701a0, 0x940180,
324 0xa00160, 0xab0120, 0xb40120, 0xbd0120, 0xc60100, 0xce0100,
325 0xd600e0, 0xdd00e0, 0xe400e0, 0xeb00c0, 0xf100c0, 0xf700c0,
326 0xfd00c0, 0x10300a0, 0x10800c0, 0x10e00a0, 0x11300a0, 0x11800a0,
327 0x11d00a0, 0x12200a0, 0x12700a0, 0x12c0080, 0x13000a0, 0x1350080,
328 0x13900a0, 0x13e0080, 0x1420076, 0x17d0062, 0x1ae0054, 0x1d8004a,
329 0x1fd0044, 0x21f003e, 0x23e003a, 0x25b0036, 0x2760032, 0x28f0030,
330 0x2a7002e, 0x2be002c, 0x2d4002c, 0x2ea0028, 0x2fe0028, 0x3120026,
331 0x3250024, 0x3370024, 0x3490022, 0x35a0022, 0x36b0020, 0x37b0020,
332 0x38b0020, 0x39b001e, 0x3aa001e, 0x3b9001c, 0x3c7001c, 0x3d5001c,
333 0x3e3001c, 0x3f1001c, 0x3ff001a, 0x40c001a },
334};
335
336static int xisc_parse_dt(struct device *dev, struct isc_device *isc)
337{
338 struct device_node *np = dev->of_node;
339 struct device_node *epn = NULL;
340 struct isc_subdev_entity *subdev_entity;
341 unsigned int flags;
342 int ret;
343 bool mipi_mode;
344
345 INIT_LIST_HEAD(&isc->subdev_entities);
346
347 mipi_mode = of_property_read_bool(np, "microchip,mipi-mode");
348
349 while (1) {
350 struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 };
351
352 epn = of_graph_get_next_endpoint(np, epn);
353 if (!epn)
354 return 0;
355
356 ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn),
357 &v4l2_epn);
358 if (ret) {
359 ret = -EINVAL;
360 dev_err(dev, "Could not parse the endpoint\n");
361 break;
362 }
363
364 subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity),
365 GFP_KERNEL);
366 if (!subdev_entity) {
367 ret = -ENOMEM;
368 break;
369 }
370 subdev_entity->epn = epn;
371
372 flags = v4l2_epn.bus.parallel.flags;
373
374 if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
375 subdev_entity->pfe_cfg0 = ISC_PFE_CFG0_HPOL_LOW;
376
377 if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
378 subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_VPOL_LOW;
379
380 if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
381 subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_PPOL_LOW;
382
383 if (v4l2_epn.bus_type == V4L2_MBUS_BT656)
384 subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_CCIR_CRC |
385 ISC_PFE_CFG0_CCIR656;
386
387 if (mipi_mode)
388 subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_MIPI;
389
390 list_add_tail(&subdev_entity->list, &isc->subdev_entities);
391 }
392 of_node_put(epn);
393
394 return ret;
395}
396
397static int microchip_xisc_probe(struct platform_device *pdev)
398{
399 struct device *dev = &pdev->dev;
400 struct isc_device *isc;
401 void __iomem *io_base;
402 struct isc_subdev_entity *subdev_entity;
403 int irq;
404 int ret;
405 u32 ver;
406
407 isc = devm_kzalloc(dev, sizeof(*isc), GFP_KERNEL);
408 if (!isc)
409 return -ENOMEM;
410
411 platform_set_drvdata(pdev, isc);
412 isc->dev = dev;
413
414 io_base = devm_platform_ioremap_resource(pdev, 0);
415 if (IS_ERR(io_base))
416 return PTR_ERR(io_base);
417
418 isc->regmap = devm_regmap_init_mmio(dev, io_base, µchip_isc_regmap_config);
419 if (IS_ERR(isc->regmap)) {
420 ret = PTR_ERR(isc->regmap);
421 dev_err(dev, "failed to init register map: %d\n", ret);
422 return ret;
423 }
424
425 irq = platform_get_irq(pdev, 0);
426 if (irq < 0)
427 return irq;
428
429 ret = devm_request_irq(dev, irq, microchip_isc_interrupt, 0,
430 "microchip-sama7g5-xisc", isc);
431 if (ret < 0) {
432 dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n",
433 irq, ret);
434 return ret;
435 }
436
437 isc->gamma_table = isc_sama7g5_gamma_table;
438 isc->gamma_max = 0;
439
440 isc->max_width = ISC_SAMA7G5_MAX_SUPPORT_WIDTH;
441 isc->max_height = ISC_SAMA7G5_MAX_SUPPORT_HEIGHT;
442
443 isc->config_dpc = isc_sama7g5_config_dpc;
444 isc->config_csc = isc_sama7g5_config_csc;
445 isc->config_cbc = isc_sama7g5_config_cbc;
446 isc->config_cc = isc_sama7g5_config_cc;
447 isc->config_gam = isc_sama7g5_config_gam;
448 isc->config_rlp = isc_sama7g5_config_rlp;
449 isc->config_ctrls = isc_sama7g5_config_ctrls;
450
451 isc->adapt_pipeline = isc_sama7g5_adapt_pipeline;
452
453 isc->offsets.csc = ISC_SAMA7G5_CSC_OFFSET;
454 isc->offsets.cbc = ISC_SAMA7G5_CBC_OFFSET;
455 isc->offsets.sub422 = ISC_SAMA7G5_SUB422_OFFSET;
456 isc->offsets.sub420 = ISC_SAMA7G5_SUB420_OFFSET;
457 isc->offsets.rlp = ISC_SAMA7G5_RLP_OFFSET;
458 isc->offsets.his = ISC_SAMA7G5_HIS_OFFSET;
459 isc->offsets.dma = ISC_SAMA7G5_DMA_OFFSET;
460 isc->offsets.version = ISC_SAMA7G5_VERSION_OFFSET;
461 isc->offsets.his_entry = ISC_SAMA7G5_HIS_ENTRY_OFFSET;
462
463 isc->controller_formats = sama7g5_controller_formats;
464 isc->controller_formats_size = ARRAY_SIZE(sama7g5_controller_formats);
465 isc->formats_list = sama7g5_formats_list;
466 isc->formats_list_size = ARRAY_SIZE(sama7g5_formats_list);
467
468 /* sama7g5-isc RAM access port is full AXI4 - 32 bits per beat */
469 isc->dcfg = ISC_DCFG_YMBSIZE_BEATS32 | ISC_DCFG_CMBSIZE_BEATS32;
470
471 /* sama7g5-isc : ISPCK does not exist, ISC is clocked by MCK */
472 isc->ispck_required = false;
473
474 ret = microchip_isc_pipeline_init(isc);
475 if (ret)
476 return ret;
477
478 isc->hclock = devm_clk_get(dev, "hclock");
479 if (IS_ERR(isc->hclock)) {
480 ret = PTR_ERR(isc->hclock);
481 dev_err(dev, "failed to get hclock: %d\n", ret);
482 return ret;
483 }
484
485 ret = clk_prepare_enable(isc->hclock);
486 if (ret) {
487 dev_err(dev, "failed to enable hclock: %d\n", ret);
488 return ret;
489 }
490
491 ret = microchip_isc_clk_init(isc);
492 if (ret) {
493 dev_err(dev, "failed to init isc clock: %d\n", ret);
494 goto unprepare_hclk;
495 }
496
497 ret = v4l2_device_register(dev, &isc->v4l2_dev);
498 if (ret) {
499 dev_err(dev, "unable to register v4l2 device.\n");
500 goto unprepare_hclk;
501 }
502
503 ret = xisc_parse_dt(dev, isc);
504 if (ret) {
505 dev_err(dev, "fail to parse device tree\n");
506 goto unregister_v4l2_device;
507 }
508
509 if (list_empty(&isc->subdev_entities)) {
510 dev_err(dev, "no subdev found\n");
511 ret = -ENODEV;
512 goto unregister_v4l2_device;
513 }
514
515 list_for_each_entry(subdev_entity, &isc->subdev_entities, list) {
516 struct v4l2_async_connection *asd;
517 struct fwnode_handle *fwnode =
518 of_fwnode_handle(subdev_entity->epn);
519
520 v4l2_async_nf_init(&subdev_entity->notifier, &isc->v4l2_dev);
521
522 asd = v4l2_async_nf_add_fwnode_remote(&subdev_entity->notifier,
523 fwnode,
524 struct v4l2_async_connection);
525
526 of_node_put(subdev_entity->epn);
527 subdev_entity->epn = NULL;
528
529 if (IS_ERR(asd)) {
530 ret = PTR_ERR(asd);
531 goto cleanup_subdev;
532 }
533
534 subdev_entity->notifier.ops = µchip_isc_async_ops;
535
536 ret = v4l2_async_nf_register(&subdev_entity->notifier);
537 if (ret) {
538 dev_err(dev, "fail to register async notifier\n");
539 goto cleanup_subdev;
540 }
541
542 if (video_is_registered(&isc->video_dev))
543 break;
544 }
545
546 regmap_read(isc->regmap, ISC_VERSION + isc->offsets.version, &ver);
547
548 ret = isc_mc_init(isc, ver);
549 if (ret < 0)
550 goto isc_probe_mc_init_err;
551
552 pm_runtime_set_active(dev);
553 pm_runtime_enable(dev);
554 pm_request_idle(dev);
555
556 dev_info(dev, "Microchip XISC version %x\n", ver);
557
558 return 0;
559
560isc_probe_mc_init_err:
561 isc_mc_cleanup(isc);
562
563cleanup_subdev:
564 microchip_isc_subdev_cleanup(isc);
565
566unregister_v4l2_device:
567 v4l2_device_unregister(&isc->v4l2_dev);
568
569unprepare_hclk:
570 clk_disable_unprepare(isc->hclock);
571
572 microchip_isc_clk_cleanup(isc);
573
574 return ret;
575}
576
577static void microchip_xisc_remove(struct platform_device *pdev)
578{
579 struct isc_device *isc = platform_get_drvdata(pdev);
580
581 pm_runtime_disable(&pdev->dev);
582
583 isc_mc_cleanup(isc);
584
585 microchip_isc_subdev_cleanup(isc);
586
587 v4l2_device_unregister(&isc->v4l2_dev);
588
589 clk_disable_unprepare(isc->hclock);
590
591 microchip_isc_clk_cleanup(isc);
592}
593
594static int __maybe_unused xisc_runtime_suspend(struct device *dev)
595{
596 struct isc_device *isc = dev_get_drvdata(dev);
597
598 clk_disable_unprepare(isc->hclock);
599
600 return 0;
601}
602
603static int __maybe_unused xisc_runtime_resume(struct device *dev)
604{
605 struct isc_device *isc = dev_get_drvdata(dev);
606 int ret;
607
608 ret = clk_prepare_enable(isc->hclock);
609 if (ret)
610 return ret;
611
612 return ret;
613}
614
615static const struct dev_pm_ops microchip_xisc_dev_pm_ops = {
616 SET_RUNTIME_PM_OPS(xisc_runtime_suspend, xisc_runtime_resume, NULL)
617};
618
619#if IS_ENABLED(CONFIG_OF)
620static const struct of_device_id microchip_xisc_of_match[] = {
621 { .compatible = "microchip,sama7g5-isc" },
622 { }
623};
624MODULE_DEVICE_TABLE(of, microchip_xisc_of_match);
625#endif
626
627static struct platform_driver microchip_xisc_driver = {
628 .probe = microchip_xisc_probe,
629 .remove_new = microchip_xisc_remove,
630 .driver = {
631 .name = "microchip-sama7g5-xisc",
632 .pm = µchip_xisc_dev_pm_ops,
633 .of_match_table = of_match_ptr(microchip_xisc_of_match),
634 },
635};
636
637module_platform_driver(microchip_xisc_driver);
638
639MODULE_AUTHOR("Eugen Hristev <eugen.hristev@microchip.com>");
640MODULE_DESCRIPTION("The V4L2 driver for Microchip-XISC");
641MODULE_LICENSE("GPL v2");