File Index
Files and directories
Sign in
FILE

c3-isp-dev.c

File preview with syntax highlighting.

linux-6.19.14/drivers/media/platform/amlogic/c3/isp/c3-isp-dev.c C
  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
 21u32 c3_isp_read(struct c3_isp_device *isp, u32 reg)
 22{
 23	return readl(isp->base + reg);
 24}
 25
 26void c3_isp_write(struct c3_isp_device *isp, u32 reg, u32 val)
 27{
 28	writel(val, isp->base + reg);
 29}
 30
 31void 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 */
 45static 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 */
 55static 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
 62static 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 */
 70static 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 */
 93static 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
106static 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
114static const struct v4l2_async_notifier_operations c3_isp_notify_ops = {
115	.bound = c3_isp_notify_bound,
116	.complete = c3_isp_notify_complete,
117};
118
119static 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
147static 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
153static 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
181err_unreg_v4l2_dev:
182	v4l2_device_unregister(&isp->v4l2_dev);
183err_media_dev_cleanup:
184	media_device_cleanup(media_dev);
185	return ret;
186}
187
188static 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
195static 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
208static 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
257err_remove_links:
258	c3_isp_remove_links(isp);
259	return ret;
260}
261
262static 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
284err_params_unregister:
285	c3_isp_params_unregister(isp);
286err_stats_unregister:
287	c3_isp_stats_unregister(isp);
288err_captures_unregister:
289	c3_isp_captures_unregister(isp);
290	return ret;
291}
292
293static 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
301static 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
311static 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
370err_nf_unregister:
371	c3_isp_async_nf_unregister(isp);
372err_resizers_unregister:
373	c3_isp_resizers_unregister(isp);
374err_core_unregister:
375	c3_isp_core_unregister(isp);
376err_v4l2_unregister:
377	c3_isp_media_unregister(isp);
378err_runtime_disable:
379	pm_runtime_disable(dev);
380	return ret;
381};
382
383static 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
395static const struct c3_isp_info isp_info = {
396	.clocks = {"vapb", "isp0"},
397	.clock_num = 2
398};
399
400static const struct of_device_id c3_isp_of_match[] = {
401	{ .compatible = "amlogic,c3-isp",
402	  .data = &isp_info },
403	{ },
404};
405MODULE_DEVICE_TABLE(of, c3_isp_of_match);
406
407static 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
417module_platform_driver(c3_isp_driver);
418
419MODULE_AUTHOR("Keke Li <keke.li@amlogic.com>");
420MODULE_DESCRIPTION("Amlogic C3 ISP pipeline");
421MODULE_LICENSE("GPL");
~/ Read-only file preview.