1 // SPDX-License-Identifier: (GPL-2.0-only OR MIT)
2 /*
3 * Copyright (C) 2024 Amlogic, Inc. All rights reserved
4 */
5
6 #include <linux/clk.h>
7 #include <linux/device.h>
8 #include <linux/module.h>
9 #include <linux/mutex.h>
10 #include <linux/platform_device.h>
11 #include <linux/pm_runtime.h>
12
13 #include <media/v4l2-common.h>
14 #include <media/v4l2-device.h>
15 #include <media/v4l2-fwnode.h>
16 #include <media/v4l2-mc.h>
17
18 #include "c3-isp-common.h"
19 #include "c3-isp-regs.h"
20
c3_isp_read(struct c3_isp_device * isp,u32 reg)21 u32 c3_isp_read(struct c3_isp_device *isp, u32 reg)
22 {
23 return readl(isp->base + reg);
24 }
25
c3_isp_write(struct c3_isp_device * isp,u32 reg,u32 val)26 void c3_isp_write(struct c3_isp_device *isp, u32 reg, u32 val)
27 {
28 writel(val, isp->base + reg);
29 }
30
c3_isp_update_bits(struct c3_isp_device * isp,u32 reg,u32 mask,u32 val)31 void c3_isp_update_bits(struct c3_isp_device *isp, u32 reg, u32 mask, u32 val)
32 {
33 u32 orig, tmp;
34
35 orig = c3_isp_read(isp, reg);
36
37 tmp = orig & ~mask;
38 tmp |= val & mask;
39
40 if (tmp != orig)
41 c3_isp_write(isp, reg, tmp);
42 }
43
44 /* PM runtime suspend */
c3_isp_runtime_suspend(struct device * dev)45 static int c3_isp_runtime_suspend(struct device *dev)
46 {
47 struct c3_isp_device *isp = dev_get_drvdata(dev);
48
49 clk_bulk_disable_unprepare(isp->info->clock_num, isp->clks);
50
51 return 0;
52 }
53
54 /* PM runtime resume */
c3_isp_runtime_resume(struct device * dev)55 static int c3_isp_runtime_resume(struct device *dev)
56 {
57 struct c3_isp_device *isp = dev_get_drvdata(dev);
58
59 return clk_bulk_prepare_enable(isp->info->clock_num, isp->clks);
60 }
61
62 static const struct dev_pm_ops c3_isp_pm_ops = {
63 SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
64 pm_runtime_force_resume)
65 RUNTIME_PM_OPS(c3_isp_runtime_suspend,
66 c3_isp_runtime_resume, NULL)
67 };
68
69 /* IRQ handling */
c3_isp_irq_handler(int irq,void * dev)70 static irqreturn_t c3_isp_irq_handler(int irq, void *dev)
71 {
72 struct c3_isp_device *isp = dev;
73 u32 status;
74
75 /* Get irq status and clear irq status */
76 status = c3_isp_read(isp, ISP_TOP_RO_IRQ_STAT);
77 c3_isp_write(isp, ISP_TOP_IRQ_CLR, status);
78
79 if (status & ISP_TOP_RO_IRQ_STAT_FRM_END_MASK) {
80 c3_isp_stats_isr(isp);
81 c3_isp_params_isr(isp);
82 c3_isp_captures_isr(isp);
83 isp->frm_sequence++;
84 }
85
86 if (status & ISP_TOP_RO_IRQ_STAT_FRM_RST_MASK)
87 c3_isp_core_queue_sof(isp);
88
89 return IRQ_HANDLED;
90 }
91
92 /* Subdev notifier register */
c3_isp_notify_bound(struct v4l2_async_notifier * notifier,struct v4l2_subdev * sd,struct v4l2_async_connection * asc)93 static int c3_isp_notify_bound(struct v4l2_async_notifier *notifier,
94 struct v4l2_subdev *sd,
95 struct v4l2_async_connection *asc)
96 {
97 struct c3_isp_device *isp =
98 container_of(notifier, struct c3_isp_device, notifier);
99 struct media_pad *sink =
100 &isp->core.sd.entity.pads[C3_ISP_CORE_PAD_SINK_VIDEO];
101
102 return v4l2_create_fwnode_links_to_pad(sd, sink, MEDIA_LNK_FL_ENABLED |
103 MEDIA_LNK_FL_IMMUTABLE);
104 }
105
c3_isp_notify_complete(struct v4l2_async_notifier * notifier)106 static int c3_isp_notify_complete(struct v4l2_async_notifier *notifier)
107 {
108 struct c3_isp_device *isp =
109 container_of(notifier, struct c3_isp_device, notifier);
110
111 return v4l2_device_register_subdev_nodes(&isp->v4l2_dev);
112 }
113
114 static const struct v4l2_async_notifier_operations c3_isp_notify_ops = {
115 .bound = c3_isp_notify_bound,
116 .complete = c3_isp_notify_complete,
117 };
118
c3_isp_async_nf_register(struct c3_isp_device * isp)119 static int c3_isp_async_nf_register(struct c3_isp_device *isp)
120 {
121 struct v4l2_async_connection *asc;
122 struct fwnode_handle *ep;
123 int ret;
124
125 v4l2_async_nf_init(&isp->notifier, &isp->v4l2_dev);
126
127 ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(isp->dev), 0, 0,
128 FWNODE_GRAPH_ENDPOINT_NEXT);
129 if (!ep)
130 return -ENOTCONN;
131
132 asc = v4l2_async_nf_add_fwnode_remote(&isp->notifier, ep,
133 struct v4l2_async_connection);
134 fwnode_handle_put(ep);
135
136 if (IS_ERR(asc))
137 return PTR_ERR(asc);
138
139 isp->notifier.ops = &c3_isp_notify_ops;
140 ret = v4l2_async_nf_register(&isp->notifier);
141 if (ret)
142 v4l2_async_nf_cleanup(&isp->notifier);
143
144 return ret;
145 }
146
c3_isp_async_nf_unregister(struct c3_isp_device * isp)147 static void c3_isp_async_nf_unregister(struct c3_isp_device *isp)
148 {
149 v4l2_async_nf_unregister(&isp->notifier);
150 v4l2_async_nf_cleanup(&isp->notifier);
151 }
152
c3_isp_media_register(struct c3_isp_device * isp)153 static int c3_isp_media_register(struct c3_isp_device *isp)
154 {
155 struct media_device *media_dev = &isp->media_dev;
156 struct v4l2_device *v4l2_dev = &isp->v4l2_dev;
157 int ret;
158
159 /* Initialize media device */
160 strscpy(media_dev->model, C3_ISP_DRIVER_NAME, sizeof(media_dev->model));
161 media_dev->dev = isp->dev;
162
163 media_device_init(media_dev);
164
165 /* Initialize v4l2 device */
166 v4l2_dev->mdev = media_dev;
167 strscpy(v4l2_dev->name, C3_ISP_DRIVER_NAME, sizeof(v4l2_dev->name));
168
169 ret = v4l2_device_register(isp->dev, v4l2_dev);
170 if (ret)
171 goto err_media_dev_cleanup;
172
173 ret = media_device_register(&isp->media_dev);
174 if (ret) {
175 dev_err(isp->dev, "Failed to register media device: %d\n", ret);
176 goto err_unreg_v4l2_dev;
177 }
178
179 return 0;
180
181 err_unreg_v4l2_dev:
182 v4l2_device_unregister(&isp->v4l2_dev);
183 err_media_dev_cleanup:
184 media_device_cleanup(media_dev);
185 return ret;
186 }
187
c3_isp_media_unregister(struct c3_isp_device * isp)188 static void c3_isp_media_unregister(struct c3_isp_device *isp)
189 {
190 media_device_unregister(&isp->media_dev);
191 v4l2_device_unregister(&isp->v4l2_dev);
192 media_device_cleanup(&isp->media_dev);
193 }
194
c3_isp_remove_links(struct c3_isp_device * isp)195 static void c3_isp_remove_links(struct c3_isp_device *isp)
196 {
197 unsigned int i;
198
199 media_entity_remove_links(&isp->core.sd.entity);
200
201 for (i = 0; i < C3_ISP_NUM_RSZ; i++)
202 media_entity_remove_links(&isp->resizers[i].sd.entity);
203
204 for (i = 0; i < C3_ISP_NUM_CAP_DEVS; i++)
205 media_entity_remove_links(&isp->caps[i].vdev.entity);
206 }
207
c3_isp_create_links(struct c3_isp_device * isp)208 static int c3_isp_create_links(struct c3_isp_device *isp)
209 {
210 unsigned int i;
211 int ret;
212
213 for (i = 0; i < C3_ISP_NUM_RSZ; i++) {
214 ret = media_create_pad_link(&isp->resizers[i].sd.entity,
215 C3_ISP_RSZ_PAD_SOURCE,
216 &isp->caps[i].vdev.entity, 0,
217 MEDIA_LNK_FL_ENABLED |
218 MEDIA_LNK_FL_IMMUTABLE);
219 if (ret) {
220 dev_err(isp->dev,
221 "Failed to link rsz %u and cap %u\n", i, i);
222 goto err_remove_links;
223 }
224
225 ret = media_create_pad_link(&isp->core.sd.entity,
226 C3_ISP_CORE_PAD_SOURCE_VIDEO_0 + i,
227 &isp->resizers[i].sd.entity,
228 C3_ISP_RSZ_PAD_SINK,
229 MEDIA_LNK_FL_ENABLED);
230 if (ret) {
231 dev_err(isp->dev,
232 "Failed to link core and rsz %u\n", i);
233 goto err_remove_links;
234 }
235 }
236
237 ret = media_create_pad_link(&isp->core.sd.entity,
238 C3_ISP_CORE_PAD_SOURCE_STATS,
239 &isp->stats.vdev.entity,
240 0, MEDIA_LNK_FL_ENABLED);
241 if (ret) {
242 dev_err(isp->dev, "Failed to link core and stats\n");
243 goto err_remove_links;
244 }
245
246 ret = media_create_pad_link(&isp->params.vdev.entity, 0,
247 &isp->core.sd.entity,
248 C3_ISP_CORE_PAD_SINK_PARAMS,
249 MEDIA_LNK_FL_ENABLED);
250 if (ret) {
251 dev_err(isp->dev, "Failed to link params and core\n");
252 goto err_remove_links;
253 }
254
255 return 0;
256
257 err_remove_links:
258 c3_isp_remove_links(isp);
259 return ret;
260 }
261
c3_isp_videos_register(struct c3_isp_device * isp)262 static int c3_isp_videos_register(struct c3_isp_device *isp)
263 {
264 int ret;
265
266 ret = c3_isp_captures_register(isp);
267 if (ret)
268 return ret;
269
270 ret = c3_isp_stats_register(isp);
271 if (ret)
272 goto err_captures_unregister;
273
274 ret = c3_isp_params_register(isp);
275 if (ret)
276 goto err_stats_unregister;
277
278 ret = c3_isp_create_links(isp);
279 if (ret)
280 goto err_params_unregister;
281
282 return 0;
283
284 err_params_unregister:
285 c3_isp_params_unregister(isp);
286 err_stats_unregister:
287 c3_isp_stats_unregister(isp);
288 err_captures_unregister:
289 c3_isp_captures_unregister(isp);
290 return ret;
291 }
292
c3_isp_videos_unregister(struct c3_isp_device * isp)293 static void c3_isp_videos_unregister(struct c3_isp_device *isp)
294 {
295 c3_isp_remove_links(isp);
296 c3_isp_params_unregister(isp);
297 c3_isp_stats_unregister(isp);
298 c3_isp_captures_unregister(isp);
299 }
300
c3_isp_get_clocks(struct c3_isp_device * isp)301 static int c3_isp_get_clocks(struct c3_isp_device *isp)
302 {
303 const struct c3_isp_info *info = isp->info;
304
305 for (unsigned int i = 0; i < info->clock_num; i++)
306 isp->clks[i].id = info->clocks[i];
307
308 return devm_clk_bulk_get(isp->dev, info->clock_num, isp->clks);
309 }
310
c3_isp_probe(struct platform_device * pdev)311 static int c3_isp_probe(struct platform_device *pdev)
312 {
313 struct device *dev = &pdev->dev;
314 struct c3_isp_device *isp;
315 int irq;
316 int ret;
317
318 isp = devm_kzalloc(dev, sizeof(*isp), GFP_KERNEL);
319 if (!isp)
320 return -ENOMEM;
321
322 isp->info = of_device_get_match_data(dev);
323 isp->dev = dev;
324
325 isp->base = devm_platform_ioremap_resource_byname(pdev, "isp");
326 if (IS_ERR(isp->base))
327 return dev_err_probe(dev, PTR_ERR(isp->base),
328 "Failed to ioremap resource\n");
329
330 irq = platform_get_irq(pdev, 0);
331 if (irq < 0)
332 return irq;
333
334 ret = c3_isp_get_clocks(isp);
335 if (ret)
336 return dev_err_probe(dev, ret, "Failed to get clocks\n");
337
338 platform_set_drvdata(pdev, isp);
339
340 pm_runtime_enable(dev);
341
342 ret = c3_isp_media_register(isp);
343 if (ret)
344 goto err_runtime_disable;
345
346 ret = c3_isp_core_register(isp);
347 if (ret)
348 goto err_v4l2_unregister;
349
350 ret = c3_isp_resizers_register(isp);
351 if (ret)
352 goto err_core_unregister;
353
354 ret = c3_isp_async_nf_register(isp);
355 if (ret)
356 goto err_resizers_unregister;
357
358 ret = devm_request_irq(dev, irq,
359 c3_isp_irq_handler, IRQF_SHARED,
360 dev_driver_string(dev), isp);
361 if (ret)
362 goto err_nf_unregister;
363
364 ret = c3_isp_videos_register(isp);
365 if (ret)
366 goto err_nf_unregister;
367
368 return 0;
369
370 err_nf_unregister:
371 c3_isp_async_nf_unregister(isp);
372 err_resizers_unregister:
373 c3_isp_resizers_unregister(isp);
374 err_core_unregister:
375 c3_isp_core_unregister(isp);
376 err_v4l2_unregister:
377 c3_isp_media_unregister(isp);
378 err_runtime_disable:
379 pm_runtime_disable(dev);
380 return ret;
381 };
382
c3_isp_remove(struct platform_device * pdev)383 static void c3_isp_remove(struct platform_device *pdev)
384 {
385 struct c3_isp_device *isp = platform_get_drvdata(pdev);
386
387 c3_isp_videos_unregister(isp);
388 c3_isp_async_nf_unregister(isp);
389 c3_isp_core_unregister(isp);
390 c3_isp_resizers_unregister(isp);
391 c3_isp_media_unregister(isp);
392 pm_runtime_disable(isp->dev);
393 };
394
395 static const struct c3_isp_info isp_info = {
396 .clocks = {"vapb", "isp0"},
397 .clock_num = 2
398 };
399
400 static const struct of_device_id c3_isp_of_match[] = {
401 { .compatible = "amlogic,c3-isp",
402 .data = &isp_info },
403 { },
404 };
405 MODULE_DEVICE_TABLE(of, c3_isp_of_match);
406
407 static struct platform_driver c3_isp_driver = {
408 .probe = c3_isp_probe,
409 .remove = c3_isp_remove,
410 .driver = {
411 .name = "c3-isp",
412 .of_match_table = c3_isp_of_match,
413 .pm = pm_ptr(&c3_isp_pm_ops),
414 },
415 };
416
417 module_platform_driver(c3_isp_driver);
418
419 MODULE_AUTHOR("Keke Li <keke.li@amlogic.com>");
420 MODULE_DESCRIPTION("Amlogic C3 ISP pipeline");
421 MODULE_LICENSE("GPL");
422