]> rtime.felk.cvut.cz Git - zynq/linux.git/blob - drivers/media/platform/xilinx/xilinx-scenechange.c
v4l: xilinx: scd: Cleanup the DMA engine at remove() time
[zynq/linux.git] / drivers / media / platform / xilinx / xilinx-scenechange.c
1 //SPDX-License-Identifier: GPL-2.0
2 /*
3  * Xilinx Scene Change Detection driver
4  *
5  * Copyright (C) 2018 Xilinx, Inc.
6  *
7  * Authors: Anand Ashok Dumbre <anand.ashok.dumbre@xilinx.com>
8  *          Satish Kumar Nagireddy <satish.nagireddy.nagireddy@xilinx.com>
9  */
10
11 #include "xilinx-scenechange.h"
12
13 #define XSCD_RESET_DEASSERT     (0)
14 #define XSCD_RESET_ASSERT       (1)
15
16 static irqreturn_t xscd_irq_handler(int irq, void *data)
17 {
18         struct xscd_device *xscd = (struct xscd_device *)data;
19         unsigned int i;
20         u32 status;
21
22         status = xscd_read(xscd->iomem, XSCD_ISR_OFFSET);
23         if (!(status & XSCD_IE_AP_DONE))
24                 return IRQ_NONE;
25
26         xscd_write(xscd->iomem, XSCD_ISR_OFFSET, XSCD_IE_AP_DONE);
27
28         for (i = 0; i < xscd->numstreams; ++i)
29                 xscd_chan_irq_handler(xscd->chans[i]);
30
31         return IRQ_HANDLED;
32 }
33
34 static int xscd_init_resources(struct xscd_device *xscd)
35 {
36         struct platform_device *pdev = to_platform_device(xscd->dev);
37         struct resource *res;
38
39         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
40         xscd->iomem = devm_ioremap_resource(xscd->dev, res);
41         if (IS_ERR(xscd->iomem))
42                 return PTR_ERR(xscd->iomem);
43
44         xscd->clk = devm_clk_get(xscd->dev, NULL);
45         if (IS_ERR(xscd->clk))
46                 return PTR_ERR(xscd->clk);
47
48         clk_prepare_enable(xscd->clk);
49         return 0;
50 }
51
52 static int xscd_parse_of(struct xscd_device *xscd)
53 {
54         struct device *dev = xscd->dev;
55         struct device_node *node = xscd->dev->of_node;
56         int ret;
57
58         xscd->shared_data.memory_based =
59                         of_property_read_bool(node, "xlnx,memorybased");
60         xscd->rst_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
61         if (IS_ERR(xscd->rst_gpio)) {
62                 if (PTR_ERR(xscd->rst_gpio) != -EPROBE_DEFER)
63                         dev_err(dev, "Reset GPIO not setup in DT\n");
64
65                 return PTR_ERR(xscd->rst_gpio);
66         }
67
68         ret = of_property_read_u32(node, "xlnx,numstreams",
69                                    &xscd->numstreams);
70         if (ret < 0)
71                 return ret;
72
73         if (!xscd->shared_data.memory_based && xscd->numstreams != 1) {
74                 dev_err(dev, "Stream-based mode only supports one stream\n");
75                 return -EINVAL;
76         }
77
78         xscd->irq = irq_of_parse_and_map(node, 0);
79         if (!xscd->irq) {
80                 dev_err(xscd->dev, "No valid irq found\n");
81                 return -EINVAL;
82         }
83
84         ret = devm_request_irq(xscd->dev, xscd->irq, xscd_irq_handler,
85                                IRQF_SHARED, dev_name(xscd->dev), xscd);
86
87         return 0;
88 }
89
90 static int xscd_probe(struct platform_device *pdev)
91 {
92         struct xscd_device *xscd;
93         struct device_node *subdev_node;
94         unsigned int id;
95         int ret;
96
97         xscd = devm_kzalloc(&pdev->dev, sizeof(*xscd), GFP_KERNEL);
98         if (!xscd)
99                 return -ENOMEM;
100
101         xscd->dev = &pdev->dev;
102
103         ret = xscd_parse_of(xscd);
104         if (ret < 0)
105                 return ret;
106
107         ret = xscd_init_resources(xscd);
108         if (ret < 0)
109                 return ret;
110
111         /* Reset Scene Change Detection IP */
112         gpiod_set_value_cansleep(xscd->rst_gpio, XSCD_RESET_ASSERT);
113         gpiod_set_value_cansleep(xscd->rst_gpio, XSCD_RESET_DEASSERT);
114
115         xscd->shared_data.iomem = xscd->iomem;
116         xscd->shared_data.dma_chan_list = xscd->channels;
117         platform_set_drvdata(pdev, (void *)&xscd->shared_data);
118
119         id = 0;
120         for_each_child_of_node(xscd->dev->of_node, subdev_node) {
121                 if (id >= xscd->numstreams) {
122                         dev_warn(&pdev->dev,
123                                  "Too many channels, limiting to %u\n",
124                                  xscd->numstreams);
125                         of_node_put(subdev_node);
126                         break;
127                 }
128
129                 ret = xscd_chan_init(xscd, id, subdev_node);
130                 if (ret < 0) {
131                         dev_err(&pdev->dev, "Failed to initialize channel %u\n",
132                                 id);
133                         return ret;
134                 }
135
136                 id++;
137         }
138
139         ret = xscd_dma_init(xscd);
140         if (ret < 0)
141                 dev_err(&pdev->dev, "Failed to initialize the DMA\n");
142
143         dev_info(xscd->dev, "scene change detect device found!\n");
144         return 0;
145 }
146
147 static int xscd_remove(struct platform_device *pdev)
148 {
149         struct xscd_device *xscd = platform_get_drvdata(pdev);
150
151         xscd_dma_cleanup(xscd);
152         clk_disable_unprepare(xscd->clk);
153
154         return 0;
155 }
156
157 static const struct of_device_id xscd_of_id_table[] = {
158         { .compatible = "xlnx,v-scd" },
159         { }
160 };
161 MODULE_DEVICE_TABLE(of, xscd_of_id_table);
162
163 static struct platform_driver xscd_driver = {
164         .driver = {
165                 .name           = "xilinx-scd",
166                 .of_match_table = xscd_of_id_table,
167         },
168         .probe                  = xscd_probe,
169         .remove                 = xscd_remove,
170 };
171
172 module_platform_driver(xscd_driver);
173
174 MODULE_AUTHOR("Xilinx Inc.");
175 MODULE_DESCRIPTION("Xilinx Scene Change Detection");
176 MODULE_LICENSE("GPL v2");