1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * stf_camss.c
4 *
5 * Starfive Camera Subsystem driver
6 *
7 * Copyright (C) 2021-2023 StarFive Technology Co., Ltd.
8 *
9 * Author: Jack Zhu <jack.zhu@starfivetech.com>
10 * Author: Changhuang Liang <changhuang.liang@starfivetech.com>
11 *
12 */
13 #include <linux/module.h>
14 #include <linux/of.h>
15 #include <linux/of_graph.h>
16 #include <linux/platform_device.h>
17 #include <linux/pm_runtime.h>
18 #include <linux/videodev2.h>
19 #include <media/v4l2-fwnode.h>
20 #include <media/v4l2-mc.h>
21
22 #include "stf-camss.h"
23
24 static const char * const stfcamss_clocks[] = {
25 "wrapper_clk_c",
26 "ispcore_2x",
27 "isp_axi",
28 };
29
30 static const char * const stfcamss_resets[] = {
31 "wrapper_p",
32 "wrapper_c",
33 "axiwr",
34 "isp_top_n",
35 "isp_top_axi",
36 };
37
38 static const struct stf_isr_data stf_isrs[] = {
39 {"wr_irq", stf_wr_irq_handler},
40 {"isp_irq", stf_isp_irq_handler},
41 {"line_irq", stf_line_irq_handler},
42 };
43
stfcamss_get_mem_res(struct stfcamss * stfcamss)44 static int stfcamss_get_mem_res(struct stfcamss *stfcamss)
45 {
46 struct platform_device *pdev = to_platform_device(stfcamss->dev);
47
48 stfcamss->syscon_base =
49 devm_platform_ioremap_resource_byname(pdev, "syscon");
50 if (IS_ERR(stfcamss->syscon_base))
51 return PTR_ERR(stfcamss->syscon_base);
52
53 stfcamss->isp_base = devm_platform_ioremap_resource_byname(pdev, "isp");
54 if (IS_ERR(stfcamss->isp_base))
55 return PTR_ERR(stfcamss->isp_base);
56
57 return 0;
58 }
59
60 /*
61 * stfcamss_of_parse_endpoint_node - Parse port endpoint node
62 * @dev: Device
63 * @node: Device node to be parsed
64 * @csd: Parsed data from port endpoint node
65 *
66 * Return 0 on success or a negative error code on failure
67 */
stfcamss_of_parse_endpoint_node(struct stfcamss * stfcamss,struct device_node * node,struct stfcamss_async_subdev * csd)68 static int stfcamss_of_parse_endpoint_node(struct stfcamss *stfcamss,
69 struct device_node *node,
70 struct stfcamss_async_subdev *csd)
71 {
72 struct v4l2_fwnode_endpoint vep = { { 0 } };
73 int ret;
74
75 ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(node), &vep);
76 if (ret) {
77 dev_err(stfcamss->dev, "endpoint not defined at %pOF\n", node);
78 return ret;
79 }
80
81 csd->port = vep.base.port;
82
83 return 0;
84 }
85
86 /*
87 * stfcamss_of_parse_ports - Parse ports node
88 * @stfcamss: STFCAMSS device
89 *
90 * Return number of "port" nodes found in "ports" node
91 */
stfcamss_of_parse_ports(struct stfcamss * stfcamss)92 static int stfcamss_of_parse_ports(struct stfcamss *stfcamss)
93 {
94 struct device_node *node = NULL;
95 int ret, num_subdevs = 0;
96
97 for_each_endpoint_of_node(stfcamss->dev->of_node, node) {
98 struct stfcamss_async_subdev *csd;
99
100 if (!of_device_is_available(node))
101 continue;
102
103 csd = v4l2_async_nf_add_fwnode_remote(&stfcamss->notifier,
104 of_fwnode_handle(node),
105 struct stfcamss_async_subdev);
106 if (IS_ERR(csd)) {
107 ret = PTR_ERR(csd);
108 dev_err(stfcamss->dev, "failed to add async notifier\n");
109 goto err_cleanup;
110 }
111
112 ret = stfcamss_of_parse_endpoint_node(stfcamss, node, csd);
113 if (ret)
114 goto err_cleanup;
115
116 num_subdevs++;
117 }
118
119 return num_subdevs;
120
121 err_cleanup:
122 of_node_put(node);
123 return ret;
124 }
125
stfcamss_register_devs(struct stfcamss * stfcamss)126 static int stfcamss_register_devs(struct stfcamss *stfcamss)
127 {
128 struct stf_capture *cap_yuv = &stfcamss->captures[STF_CAPTURE_YUV];
129 struct stf_isp_dev *isp_dev = &stfcamss->isp_dev;
130 int ret;
131
132 ret = stf_isp_register(isp_dev, &stfcamss->v4l2_dev);
133 if (ret < 0) {
134 dev_err(stfcamss->dev,
135 "failed to register stf isp%d entity: %d\n", 0, ret);
136 return ret;
137 }
138
139 ret = stf_capture_register(stfcamss, &stfcamss->v4l2_dev);
140 if (ret < 0) {
141 dev_err(stfcamss->dev,
142 "failed to register capture: %d\n", ret);
143 goto err_isp_unregister;
144 }
145
146 ret = media_create_pad_link(&isp_dev->subdev.entity, STF_ISP_PAD_SRC,
147 &cap_yuv->video.vdev.entity, 0, 0);
148 if (ret)
149 goto err_cap_unregister;
150
151 cap_yuv->video.source_subdev = &isp_dev->subdev;
152
153 return ret;
154
155 err_cap_unregister:
156 stf_capture_unregister(stfcamss);
157 err_isp_unregister:
158 stf_isp_unregister(&stfcamss->isp_dev);
159
160 return ret;
161 }
162
stfcamss_unregister_devs(struct stfcamss * stfcamss)163 static void stfcamss_unregister_devs(struct stfcamss *stfcamss)
164 {
165 stf_isp_unregister(&stfcamss->isp_dev);
166 stf_capture_unregister(stfcamss);
167 }
168
stfcamss_subdev_notifier_bound(struct v4l2_async_notifier * async,struct v4l2_subdev * subdev,struct v4l2_async_connection * asc)169 static int stfcamss_subdev_notifier_bound(struct v4l2_async_notifier *async,
170 struct v4l2_subdev *subdev,
171 struct v4l2_async_connection *asc)
172 {
173 struct stfcamss *stfcamss =
174 container_of(async, struct stfcamss, notifier);
175 struct stfcamss_async_subdev *csd =
176 container_of(asc, struct stfcamss_async_subdev, asd);
177 enum stf_port_num port = csd->port;
178 struct stf_isp_dev *isp_dev = &stfcamss->isp_dev;
179 struct stf_capture *cap_raw = &stfcamss->captures[STF_CAPTURE_RAW];
180 struct media_pad *pad;
181 int ret;
182
183 if (port == STF_PORT_CSI2RX) {
184 pad = &isp_dev->pads[STF_ISP_PAD_SINK];
185 } else {
186 dev_err(stfcamss->dev, "not support port %d\n", port);
187 return -EPERM;
188 }
189
190 ret = v4l2_create_fwnode_links_to_pad(subdev, pad, 0);
191 if (ret)
192 return ret;
193
194 ret = media_create_pad_link(&subdev->entity, 1,
195 &cap_raw->video.vdev.entity, 0, 0);
196 if (ret)
197 return ret;
198
199 isp_dev->source_subdev = subdev;
200 cap_raw->video.source_subdev = subdev;
201
202 return 0;
203 }
204
stfcamss_subdev_notifier_complete(struct v4l2_async_notifier * ntf)205 static int stfcamss_subdev_notifier_complete(struct v4l2_async_notifier *ntf)
206 {
207 struct stfcamss *stfcamss =
208 container_of(ntf, struct stfcamss, notifier);
209
210 return v4l2_device_register_subdev_nodes(&stfcamss->v4l2_dev);
211 }
212
213 static const struct v4l2_async_notifier_operations
214 stfcamss_subdev_notifier_ops = {
215 .bound = stfcamss_subdev_notifier_bound,
216 .complete = stfcamss_subdev_notifier_complete,
217 };
218
stfcamss_mc_init(struct platform_device * pdev,struct stfcamss * stfcamss)219 static void stfcamss_mc_init(struct platform_device *pdev,
220 struct stfcamss *stfcamss)
221 {
222 stfcamss->media_dev.dev = stfcamss->dev;
223 strscpy(stfcamss->media_dev.model, "Starfive Camera Subsystem",
224 sizeof(stfcamss->media_dev.model));
225 media_device_init(&stfcamss->media_dev);
226
227 stfcamss->v4l2_dev.mdev = &stfcamss->media_dev;
228 }
229
230 /*
231 * stfcamss_probe - Probe STFCAMSS platform device
232 * @pdev: Pointer to STFCAMSS platform device
233 *
234 * Return 0 on success or a negative error code on failure
235 */
stfcamss_probe(struct platform_device * pdev)236 static int stfcamss_probe(struct platform_device *pdev)
237 {
238 struct stfcamss *stfcamss;
239 struct device *dev = &pdev->dev;
240 int ret, num_subdevs;
241 unsigned int i;
242
243 stfcamss = devm_kzalloc(dev, sizeof(*stfcamss), GFP_KERNEL);
244 if (!stfcamss)
245 return -ENOMEM;
246
247 stfcamss->dev = dev;
248
249 for (i = 0; i < ARRAY_SIZE(stf_isrs); ++i) {
250 int irq;
251
252 irq = platform_get_irq(pdev, i);
253 if (irq < 0)
254 return irq;
255
256 ret = devm_request_irq(stfcamss->dev, irq, stf_isrs[i].isr, 0,
257 stf_isrs[i].name, stfcamss);
258 if (ret) {
259 dev_err(dev, "request irq failed: %d\n", ret);
260 return ret;
261 }
262 }
263
264 stfcamss->nclks = ARRAY_SIZE(stfcamss->sys_clk);
265 for (i = 0; i < stfcamss->nclks; ++i)
266 stfcamss->sys_clk[i].id = stfcamss_clocks[i];
267 ret = devm_clk_bulk_get(dev, stfcamss->nclks, stfcamss->sys_clk);
268 if (ret) {
269 dev_err(dev, "Failed to get clk controls\n");
270 return ret;
271 }
272
273 stfcamss->nrsts = ARRAY_SIZE(stfcamss->sys_rst);
274 for (i = 0; i < stfcamss->nrsts; ++i)
275 stfcamss->sys_rst[i].id = stfcamss_resets[i];
276 ret = devm_reset_control_bulk_get_shared(dev, stfcamss->nrsts,
277 stfcamss->sys_rst);
278 if (ret) {
279 dev_err(dev, "Failed to get reset controls\n");
280 return ret;
281 }
282
283 ret = stfcamss_get_mem_res(stfcamss);
284 if (ret) {
285 dev_err(dev, "Could not map registers\n");
286 return ret;
287 }
288
289 platform_set_drvdata(pdev, stfcamss);
290
291 v4l2_async_nf_init(&stfcamss->notifier, &stfcamss->v4l2_dev);
292
293 num_subdevs = stfcamss_of_parse_ports(stfcamss);
294 if (num_subdevs < 0) {
295 ret = -ENODEV;
296 dev_err(dev, "Failed to get sub devices: %d\n", ret);
297 goto err_cleanup_notifier;
298 }
299
300 ret = stf_isp_init(stfcamss);
301 if (ret < 0) {
302 dev_err(dev, "Failed to init isp: %d\n", ret);
303 goto err_cleanup_notifier;
304 }
305
306 stfcamss_mc_init(pdev, stfcamss);
307
308 ret = v4l2_device_register(stfcamss->dev, &stfcamss->v4l2_dev);
309 if (ret < 0) {
310 dev_err(dev, "Failed to register V4L2 device: %d\n", ret);
311 goto err_cleanup_media_device;
312 }
313
314 ret = media_device_register(&stfcamss->media_dev);
315 if (ret) {
316 dev_err(dev, "Failed to register media device: %d\n", ret);
317 goto err_unregister_device;
318 }
319
320 ret = stfcamss_register_devs(stfcamss);
321 if (ret < 0) {
322 dev_err(dev, "Failed to register subdevice: %d\n", ret);
323 goto err_unregister_media_dev;
324 }
325
326 pm_runtime_enable(dev);
327
328 stfcamss->notifier.ops = &stfcamss_subdev_notifier_ops;
329 ret = v4l2_async_nf_register(&stfcamss->notifier);
330 if (ret) {
331 dev_err(dev, "Failed to register async subdev nodes: %d\n",
332 ret);
333 pm_runtime_disable(dev);
334 goto err_unregister_subdevs;
335 }
336
337 return 0;
338
339 err_unregister_subdevs:
340 stfcamss_unregister_devs(stfcamss);
341 err_unregister_media_dev:
342 media_device_unregister(&stfcamss->media_dev);
343 err_unregister_device:
344 v4l2_device_unregister(&stfcamss->v4l2_dev);
345 err_cleanup_media_device:
346 media_device_cleanup(&stfcamss->media_dev);
347 err_cleanup_notifier:
348 v4l2_async_nf_cleanup(&stfcamss->notifier);
349 return ret;
350 }
351
352 /*
353 * stfcamss_remove - Remove STFCAMSS platform device
354 * @pdev: Pointer to STFCAMSS platform device
355 *
356 * Always returns 0.
357 */
stfcamss_remove(struct platform_device * pdev)358 static int stfcamss_remove(struct platform_device *pdev)
359 {
360 struct stfcamss *stfcamss = platform_get_drvdata(pdev);
361
362 stfcamss_unregister_devs(stfcamss);
363 v4l2_device_unregister(&stfcamss->v4l2_dev);
364 media_device_cleanup(&stfcamss->media_dev);
365 v4l2_async_nf_cleanup(&stfcamss->notifier);
366 pm_runtime_disable(&pdev->dev);
367
368 return 0;
369 }
370
371 static const struct of_device_id stfcamss_of_match[] = {
372 { .compatible = "starfive,jh7110-camss" },
373 { /* sentinel */ },
374 };
375
376 MODULE_DEVICE_TABLE(of, stfcamss_of_match);
377
stfcamss_runtime_suspend(struct device * dev)378 static int __maybe_unused stfcamss_runtime_suspend(struct device *dev)
379 {
380 struct stfcamss *stfcamss = dev_get_drvdata(dev);
381 int ret;
382
383 ret = reset_control_bulk_assert(stfcamss->nrsts, stfcamss->sys_rst);
384 if (ret) {
385 dev_err(dev, "reset assert failed\n");
386 return ret;
387 }
388
389 clk_bulk_disable_unprepare(stfcamss->nclks, stfcamss->sys_clk);
390
391 return 0;
392 }
393
stfcamss_runtime_resume(struct device * dev)394 static int __maybe_unused stfcamss_runtime_resume(struct device *dev)
395 {
396 struct stfcamss *stfcamss = dev_get_drvdata(dev);
397 int ret;
398
399 ret = clk_bulk_prepare_enable(stfcamss->nclks, stfcamss->sys_clk);
400 if (ret) {
401 dev_err(dev, "clock prepare enable failed\n");
402 return ret;
403 }
404
405 ret = reset_control_bulk_deassert(stfcamss->nrsts, stfcamss->sys_rst);
406 if (ret < 0) {
407 dev_err(dev, "cannot deassert resets\n");
408 clk_bulk_disable_unprepare(stfcamss->nclks, stfcamss->sys_clk);
409 return ret;
410 }
411
412 return 0;
413 }
414
415 static const struct dev_pm_ops stfcamss_pm_ops = {
416 SET_RUNTIME_PM_OPS(stfcamss_runtime_suspend,
417 stfcamss_runtime_resume,
418 NULL)
419 };
420
421 static struct platform_driver stfcamss_driver = {
422 .probe = stfcamss_probe,
423 .remove = stfcamss_remove,
424 .driver = {
425 .name = "starfive-camss",
426 .pm = &stfcamss_pm_ops,
427 .of_match_table = stfcamss_of_match,
428 },
429 };
430
431 module_platform_driver(stfcamss_driver);
432
433 MODULE_AUTHOR("Jack Zhu <jack.zhu@starfivetech.com>");
434 MODULE_AUTHOR("Changhuang Liang <changhuang.liang@starfivetech.com>");
435 MODULE_DESCRIPTION("StarFive Camera Subsystem driver");
436 MODULE_LICENSE("GPL");
437