18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Microchip Image Sensor Controller (ISC) driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2016-2019 Microchip Technology, Inc. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Songjun Wu 88c2ecf20Sopenharmony_ci * Author: Eugen Hristev <eugen.hristev@microchip.com> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Sensor-->PFE-->WB-->CFA-->CC-->GAM-->CSC-->CBC-->SUB-->RLP-->DMA 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * ISC video pipeline integrates the following submodules: 148c2ecf20Sopenharmony_ci * PFE: Parallel Front End to sample the camera sensor input stream 158c2ecf20Sopenharmony_ci * WB: Programmable white balance in the Bayer domain 168c2ecf20Sopenharmony_ci * CFA: Color filter array interpolation module 178c2ecf20Sopenharmony_ci * CC: Programmable color correction 188c2ecf20Sopenharmony_ci * GAM: Gamma correction 198c2ecf20Sopenharmony_ci * CSC: Programmable color space conversion 208c2ecf20Sopenharmony_ci * CBC: Contrast and Brightness control 218c2ecf20Sopenharmony_ci * SUB: This module performs YCbCr444 to YCbCr420 chrominance subsampling 228c2ecf20Sopenharmony_ci * RLP: This module performs rounding, range limiting 238c2ecf20Sopenharmony_ci * and packing of the incoming data 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <linux/clk.h> 278c2ecf20Sopenharmony_ci#include <linux/clkdev.h> 288c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 298c2ecf20Sopenharmony_ci#include <linux/delay.h> 308c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 318c2ecf20Sopenharmony_ci#include <linux/math64.h> 328c2ecf20Sopenharmony_ci#include <linux/module.h> 338c2ecf20Sopenharmony_ci#include <linux/of.h> 348c2ecf20Sopenharmony_ci#include <linux/of_graph.h> 358c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 368c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 378c2ecf20Sopenharmony_ci#include <linux/regmap.h> 388c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h> 418c2ecf20Sopenharmony_ci#include <media/v4l2-device.h> 428c2ecf20Sopenharmony_ci#include <media/v4l2-event.h> 438c2ecf20Sopenharmony_ci#include <media/v4l2-image-sizes.h> 448c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h> 458c2ecf20Sopenharmony_ci#include <media/v4l2-fwnode.h> 468c2ecf20Sopenharmony_ci#include <media/v4l2-subdev.h> 478c2ecf20Sopenharmony_ci#include <media/videobuf2-dma-contig.h> 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#include "atmel-isc-regs.h" 508c2ecf20Sopenharmony_ci#include "atmel-isc.h" 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define ISC_MAX_SUPPORT_WIDTH 2592 538c2ecf20Sopenharmony_ci#define ISC_MAX_SUPPORT_HEIGHT 1944 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define ISC_CLK_MAX_DIV 255 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic int isc_parse_dt(struct device *dev, struct isc_device *isc) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 608c2ecf20Sopenharmony_ci struct device_node *epn = NULL, *rem; 618c2ecf20Sopenharmony_ci struct isc_subdev_entity *subdev_entity; 628c2ecf20Sopenharmony_ci unsigned int flags; 638c2ecf20Sopenharmony_ci int ret; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&isc->subdev_entities); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci while (1) { 688c2ecf20Sopenharmony_ci struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 }; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci epn = of_graph_get_next_endpoint(np, epn); 718c2ecf20Sopenharmony_ci if (!epn) 728c2ecf20Sopenharmony_ci return 0; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci rem = of_graph_get_remote_port_parent(epn); 758c2ecf20Sopenharmony_ci if (!rem) { 768c2ecf20Sopenharmony_ci dev_notice(dev, "Remote device at %pOF not found\n", 778c2ecf20Sopenharmony_ci epn); 788c2ecf20Sopenharmony_ci continue; 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn), 828c2ecf20Sopenharmony_ci &v4l2_epn); 838c2ecf20Sopenharmony_ci if (ret) { 848c2ecf20Sopenharmony_ci of_node_put(rem); 858c2ecf20Sopenharmony_ci ret = -EINVAL; 868c2ecf20Sopenharmony_ci dev_err(dev, "Could not parse the endpoint\n"); 878c2ecf20Sopenharmony_ci break; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity), 918c2ecf20Sopenharmony_ci GFP_KERNEL); 928c2ecf20Sopenharmony_ci if (!subdev_entity) { 938c2ecf20Sopenharmony_ci of_node_put(rem); 948c2ecf20Sopenharmony_ci ret = -ENOMEM; 958c2ecf20Sopenharmony_ci break; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci /* asd will be freed by the subsystem once it's added to the 998c2ecf20Sopenharmony_ci * notifier list 1008c2ecf20Sopenharmony_ci */ 1018c2ecf20Sopenharmony_ci subdev_entity->asd = kzalloc(sizeof(*subdev_entity->asd), 1028c2ecf20Sopenharmony_ci GFP_KERNEL); 1038c2ecf20Sopenharmony_ci if (!subdev_entity->asd) { 1048c2ecf20Sopenharmony_ci of_node_put(rem); 1058c2ecf20Sopenharmony_ci ret = -ENOMEM; 1068c2ecf20Sopenharmony_ci break; 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci flags = v4l2_epn.bus.parallel.flags; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) 1128c2ecf20Sopenharmony_ci subdev_entity->pfe_cfg0 = ISC_PFE_CFG0_HPOL_LOW; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) 1158c2ecf20Sopenharmony_ci subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_VPOL_LOW; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) 1188c2ecf20Sopenharmony_ci subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_PPOL_LOW; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (v4l2_epn.bus_type == V4L2_MBUS_BT656) 1218c2ecf20Sopenharmony_ci subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_CCIR_CRC | 1228c2ecf20Sopenharmony_ci ISC_PFE_CFG0_CCIR656; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci subdev_entity->asd->match_type = V4L2_ASYNC_MATCH_FWNODE; 1258c2ecf20Sopenharmony_ci subdev_entity->asd->match.fwnode = of_fwnode_handle(rem); 1268c2ecf20Sopenharmony_ci list_add_tail(&subdev_entity->list, &isc->subdev_entities); 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci of_node_put(epn); 1308c2ecf20Sopenharmony_ci return ret; 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic int atmel_isc_probe(struct platform_device *pdev) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1368c2ecf20Sopenharmony_ci struct isc_device *isc; 1378c2ecf20Sopenharmony_ci struct resource *res; 1388c2ecf20Sopenharmony_ci void __iomem *io_base; 1398c2ecf20Sopenharmony_ci struct isc_subdev_entity *subdev_entity; 1408c2ecf20Sopenharmony_ci int irq; 1418c2ecf20Sopenharmony_ci int ret; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci isc = devm_kzalloc(dev, sizeof(*isc), GFP_KERNEL); 1448c2ecf20Sopenharmony_ci if (!isc) 1458c2ecf20Sopenharmony_ci return -ENOMEM; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, isc); 1488c2ecf20Sopenharmony_ci isc->dev = dev; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1518c2ecf20Sopenharmony_ci io_base = devm_ioremap_resource(dev, res); 1528c2ecf20Sopenharmony_ci if (IS_ERR(io_base)) 1538c2ecf20Sopenharmony_ci return PTR_ERR(io_base); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci isc->regmap = devm_regmap_init_mmio(dev, io_base, &isc_regmap_config); 1568c2ecf20Sopenharmony_ci if (IS_ERR(isc->regmap)) { 1578c2ecf20Sopenharmony_ci ret = PTR_ERR(isc->regmap); 1588c2ecf20Sopenharmony_ci dev_err(dev, "failed to init register map: %d\n", ret); 1598c2ecf20Sopenharmony_ci return ret; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 1638c2ecf20Sopenharmony_ci if (irq < 0) 1648c2ecf20Sopenharmony_ci return irq; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci ret = devm_request_irq(dev, irq, isc_interrupt, 0, 1678c2ecf20Sopenharmony_ci ATMEL_ISC_NAME, isc); 1688c2ecf20Sopenharmony_ci if (ret < 0) { 1698c2ecf20Sopenharmony_ci dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n", 1708c2ecf20Sopenharmony_ci irq, ret); 1718c2ecf20Sopenharmony_ci return ret; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci ret = isc_pipeline_init(isc); 1758c2ecf20Sopenharmony_ci if (ret) 1768c2ecf20Sopenharmony_ci return ret; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci isc->hclock = devm_clk_get(dev, "hclock"); 1798c2ecf20Sopenharmony_ci if (IS_ERR(isc->hclock)) { 1808c2ecf20Sopenharmony_ci ret = PTR_ERR(isc->hclock); 1818c2ecf20Sopenharmony_ci dev_err(dev, "failed to get hclock: %d\n", ret); 1828c2ecf20Sopenharmony_ci return ret; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci ret = clk_prepare_enable(isc->hclock); 1868c2ecf20Sopenharmony_ci if (ret) { 1878c2ecf20Sopenharmony_ci dev_err(dev, "failed to enable hclock: %d\n", ret); 1888c2ecf20Sopenharmony_ci return ret; 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci ret = isc_clk_init(isc); 1928c2ecf20Sopenharmony_ci if (ret) { 1938c2ecf20Sopenharmony_ci dev_err(dev, "failed to init isc clock: %d\n", ret); 1948c2ecf20Sopenharmony_ci goto unprepare_hclk; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci isc->ispck = isc->isc_clks[ISC_ISPCK].clk; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci ret = clk_prepare_enable(isc->ispck); 2008c2ecf20Sopenharmony_ci if (ret) { 2018c2ecf20Sopenharmony_ci dev_err(dev, "failed to enable ispck: %d\n", ret); 2028c2ecf20Sopenharmony_ci goto unprepare_hclk; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci /* ispck should be greater or equal to hclock */ 2068c2ecf20Sopenharmony_ci ret = clk_set_rate(isc->ispck, clk_get_rate(isc->hclock)); 2078c2ecf20Sopenharmony_ci if (ret) { 2088c2ecf20Sopenharmony_ci dev_err(dev, "failed to set ispck rate: %d\n", ret); 2098c2ecf20Sopenharmony_ci goto unprepare_clk; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci ret = v4l2_device_register(dev, &isc->v4l2_dev); 2138c2ecf20Sopenharmony_ci if (ret) { 2148c2ecf20Sopenharmony_ci dev_err(dev, "unable to register v4l2 device.\n"); 2158c2ecf20Sopenharmony_ci goto unprepare_clk; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci ret = isc_parse_dt(dev, isc); 2198c2ecf20Sopenharmony_ci if (ret) { 2208c2ecf20Sopenharmony_ci dev_err(dev, "fail to parse device tree\n"); 2218c2ecf20Sopenharmony_ci goto unregister_v4l2_device; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (list_empty(&isc->subdev_entities)) { 2258c2ecf20Sopenharmony_ci dev_err(dev, "no subdev found\n"); 2268c2ecf20Sopenharmony_ci ret = -ENODEV; 2278c2ecf20Sopenharmony_ci goto unregister_v4l2_device; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci list_for_each_entry(subdev_entity, &isc->subdev_entities, list) { 2318c2ecf20Sopenharmony_ci v4l2_async_notifier_init(&subdev_entity->notifier); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci ret = v4l2_async_notifier_add_subdev(&subdev_entity->notifier, 2348c2ecf20Sopenharmony_ci subdev_entity->asd); 2358c2ecf20Sopenharmony_ci if (ret) { 2368c2ecf20Sopenharmony_ci fwnode_handle_put(subdev_entity->asd->match.fwnode); 2378c2ecf20Sopenharmony_ci kfree(subdev_entity->asd); 2388c2ecf20Sopenharmony_ci goto cleanup_subdev; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci subdev_entity->notifier.ops = &isc_async_ops; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci ret = v4l2_async_notifier_register(&isc->v4l2_dev, 2448c2ecf20Sopenharmony_ci &subdev_entity->notifier); 2458c2ecf20Sopenharmony_ci if (ret) { 2468c2ecf20Sopenharmony_ci dev_err(dev, "fail to register async notifier\n"); 2478c2ecf20Sopenharmony_ci goto cleanup_subdev; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci if (video_is_registered(&isc->video_dev)) 2518c2ecf20Sopenharmony_ci break; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci pm_runtime_set_active(dev); 2558c2ecf20Sopenharmony_ci pm_runtime_enable(dev); 2568c2ecf20Sopenharmony_ci pm_request_idle(dev); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci return 0; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cicleanup_subdev: 2618c2ecf20Sopenharmony_ci isc_subdev_cleanup(isc); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ciunregister_v4l2_device: 2648c2ecf20Sopenharmony_ci v4l2_device_unregister(&isc->v4l2_dev); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ciunprepare_clk: 2678c2ecf20Sopenharmony_ci clk_disable_unprepare(isc->ispck); 2688c2ecf20Sopenharmony_ciunprepare_hclk: 2698c2ecf20Sopenharmony_ci clk_disable_unprepare(isc->hclock); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci isc_clk_cleanup(isc); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci return ret; 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic int atmel_isc_remove(struct platform_device *pdev) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci struct isc_device *isc = platform_get_drvdata(pdev); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci isc_subdev_cleanup(isc); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci v4l2_device_unregister(&isc->v4l2_dev); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci clk_disable_unprepare(isc->ispck); 2878c2ecf20Sopenharmony_ci clk_disable_unprepare(isc->hclock); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci isc_clk_cleanup(isc); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci return 0; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic int __maybe_unused isc_runtime_suspend(struct device *dev) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci struct isc_device *isc = dev_get_drvdata(dev); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci clk_disable_unprepare(isc->ispck); 2998c2ecf20Sopenharmony_ci clk_disable_unprepare(isc->hclock); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci return 0; 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic int __maybe_unused isc_runtime_resume(struct device *dev) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci struct isc_device *isc = dev_get_drvdata(dev); 3078c2ecf20Sopenharmony_ci int ret; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci ret = clk_prepare_enable(isc->hclock); 3108c2ecf20Sopenharmony_ci if (ret) 3118c2ecf20Sopenharmony_ci return ret; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci ret = clk_prepare_enable(isc->ispck); 3148c2ecf20Sopenharmony_ci if (ret) 3158c2ecf20Sopenharmony_ci clk_disable_unprepare(isc->hclock); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci return ret; 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic const struct dev_pm_ops atmel_isc_dev_pm_ops = { 3218c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(isc_runtime_suspend, isc_runtime_resume, NULL) 3228c2ecf20Sopenharmony_ci}; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_OF) 3258c2ecf20Sopenharmony_cistatic const struct of_device_id atmel_isc_of_match[] = { 3268c2ecf20Sopenharmony_ci { .compatible = "atmel,sama5d2-isc" }, 3278c2ecf20Sopenharmony_ci { } 3288c2ecf20Sopenharmony_ci}; 3298c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, atmel_isc_of_match); 3308c2ecf20Sopenharmony_ci#endif 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic struct platform_driver atmel_isc_driver = { 3338c2ecf20Sopenharmony_ci .probe = atmel_isc_probe, 3348c2ecf20Sopenharmony_ci .remove = atmel_isc_remove, 3358c2ecf20Sopenharmony_ci .driver = { 3368c2ecf20Sopenharmony_ci .name = ATMEL_ISC_NAME, 3378c2ecf20Sopenharmony_ci .pm = &atmel_isc_dev_pm_ops, 3388c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(atmel_isc_of_match), 3398c2ecf20Sopenharmony_ci }, 3408c2ecf20Sopenharmony_ci}; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cimodule_platform_driver(atmel_isc_driver); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ciMODULE_AUTHOR("Songjun Wu"); 3458c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("The V4L2 driver for Atmel-ISC"); 3468c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 3478c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("video"); 348