File Index
Files and directories
Sign in
FILE

csisp.c

File preview with syntax highlighting.

linux-6.19.14/drivers/media/platform/renesas/rcar-isp/csisp.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Copyright (C) 2021 Renesas Electronics Corp.
  4 *
  5 * Driver for Renesas R-Car ISP Channel Selector
  6 *
  7 * The ISP hardware is capable of more than just channel selection, features
  8 * such as demosaicing, white balance control and color space conversion are
  9 * also possible. These more advanced features are not supported by the driver
 10 * due to lack of documentation.
 11 */
 12
 13#include <linux/module.h>
 14#include <linux/mutex.h>
 15#include <linux/of.h>
 16#include <linux/platform_device.h>
 17#include <linux/pm_runtime.h>
 18#include <linux/reset.h>
 19
 20#include <media/mipi-csi2.h>
 21#include <media/v4l2-subdev.h>
 22
 23#define ISPINPUTSEL0_REG				0x0008
 24#define ISPINPUTSEL0_SEL_CSI0				BIT(31)
 25
 26#define ISPSTART_REG					0x0014
 27#define ISPSTART_START					0xffff
 28#define ISPSTART_STOP					0x0000
 29
 30#define ISPPROCMODE_DT_REG(n)				(0x1100 + (0x4 * (n)))
 31#define ISPPROCMODE_DT_PROC_MODE_VC3(pm)		(((pm) & 0x3f) << 24)
 32#define ISPPROCMODE_DT_PROC_MODE_VC2(pm)		(((pm) & 0x3f) << 16)
 33#define ISPPROCMODE_DT_PROC_MODE_VC1(pm)		(((pm) & 0x3f) << 8)
 34#define ISPPROCMODE_DT_PROC_MODE_VC0(pm)		((pm) & 0x3f)
 35
 36#define ISPCS_FILTER_ID_CH_REG(n)			(0x3000 + (0x0100 * (n)))
 37
 38#define ISPCS_DT_CODE03_CH_REG(n)			(0x3008 + (0x100 * (n)))
 39#define ISPCS_DT_CODE03_EN3				BIT(31)
 40#define ISPCS_DT_CODE03_DT3(dt)				(((dt) & 0x3f) << 24)
 41#define ISPCS_DT_CODE03_EN2				BIT(23)
 42#define ISPCS_DT_CODE03_DT2(dt)				(((dt) & 0x3f) << 16)
 43#define ISPCS_DT_CODE03_EN1				BIT(15)
 44#define ISPCS_DT_CODE03_DT1(dt)				(((dt) & 0x3f) << 8)
 45#define ISPCS_DT_CODE03_EN0				BIT(7)
 46#define ISPCS_DT_CODE03_DT0(dt)				((dt) & 0x3f)
 47
 48struct rcar_isp_format {
 49	u32 code;
 50	unsigned int datatype;
 51	unsigned int procmode;
 52};
 53
 54static const struct rcar_isp_format rcar_isp_formats[] = {
 55	{
 56		.code = MEDIA_BUS_FMT_RGB888_1X24,
 57		.datatype = MIPI_CSI2_DT_RGB888,
 58		.procmode = 0x15
 59	}, {
 60		.code = MEDIA_BUS_FMT_Y10_1X10,
 61		.datatype = MIPI_CSI2_DT_RAW10,
 62		.procmode = 0x10,
 63	}, {
 64		.code = MEDIA_BUS_FMT_UYVY8_1X16,
 65		.datatype = MIPI_CSI2_DT_YUV422_8B,
 66		.procmode = 0x0c,
 67	}, {
 68		.code = MEDIA_BUS_FMT_YUYV8_1X16,
 69		.datatype = MIPI_CSI2_DT_YUV422_8B,
 70		.procmode = 0x0c,
 71	}, {
 72		.code = MEDIA_BUS_FMT_UYVY8_2X8,
 73		.datatype = MIPI_CSI2_DT_YUV422_8B,
 74		.procmode = 0x0c,
 75	}, {
 76		.code = MEDIA_BUS_FMT_YUYV10_2X10,
 77		.datatype = MIPI_CSI2_DT_YUV422_8B,
 78		.procmode = 0x0c,
 79	}, {
 80		.code = MEDIA_BUS_FMT_SBGGR8_1X8,
 81		.datatype = MIPI_CSI2_DT_RAW8,
 82		.procmode = 0x00,
 83	}, {
 84		.code = MEDIA_BUS_FMT_SGBRG8_1X8,
 85		.datatype = MIPI_CSI2_DT_RAW8,
 86		.procmode = 0x00,
 87	}, {
 88		.code = MEDIA_BUS_FMT_SGRBG8_1X8,
 89		.datatype = MIPI_CSI2_DT_RAW8,
 90		.procmode = 0x00,
 91	}, {
 92		.code = MEDIA_BUS_FMT_SRGGB8_1X8,
 93		.datatype = MIPI_CSI2_DT_RAW8,
 94		.procmode = 0x00,
 95	}, {
 96		.code = MEDIA_BUS_FMT_SBGGR10_1X10,
 97		.datatype = MIPI_CSI2_DT_RAW10,
 98		.procmode = 0x01,
 99	}, {
100		.code = MEDIA_BUS_FMT_SGBRG10_1X10,
101		.datatype = MIPI_CSI2_DT_RAW10,
102		.procmode = 0x01,
103	}, {
104		.code = MEDIA_BUS_FMT_SGRBG10_1X10,
105		.datatype = MIPI_CSI2_DT_RAW10,
106		.procmode = 0x01,
107	}, {
108		.code = MEDIA_BUS_FMT_SRGGB10_1X10,
109		.datatype = MIPI_CSI2_DT_RAW10,
110		.procmode = 0x01,
111	}, {
112		.code = MEDIA_BUS_FMT_SBGGR12_1X12,
113		.datatype = MIPI_CSI2_DT_RAW12,
114		.procmode = 0x02,
115	}, {
116		.code = MEDIA_BUS_FMT_SGBRG12_1X12,
117		.datatype = MIPI_CSI2_DT_RAW12,
118		.procmode = 0x02,
119	}, {
120		.code = MEDIA_BUS_FMT_SGRBG12_1X12,
121		.datatype = MIPI_CSI2_DT_RAW12,
122		.procmode = 0x02,
123	}, {
124		.code = MEDIA_BUS_FMT_SRGGB12_1X12,
125		.datatype = MIPI_CSI2_DT_RAW12,
126		.procmode = 0x02,
127	},
128};
129
130static const struct rcar_isp_format *risp_code_to_fmt(unsigned int code)
131{
132	unsigned int i;
133
134	for (i = 0; i < ARRAY_SIZE(rcar_isp_formats); i++) {
135		if (rcar_isp_formats[i].code == code)
136			return &rcar_isp_formats[i];
137	}
138
139	return NULL;
140}
141
142enum rcar_isp_input {
143	RISP_CSI_INPUT0,
144	RISP_CSI_INPUT1,
145};
146
147enum rcar_isp_pads {
148	RCAR_ISP_SINK,
149	RCAR_ISP_PORT0,
150	RCAR_ISP_PORT1,
151	RCAR_ISP_PORT2,
152	RCAR_ISP_PORT3,
153	RCAR_ISP_PORT4,
154	RCAR_ISP_PORT5,
155	RCAR_ISP_PORT6,
156	RCAR_ISP_PORT7,
157	RCAR_ISP_NUM_PADS,
158};
159
160struct rcar_isp {
161	struct device *dev;
162	void __iomem *csbase;
163	struct reset_control *rstc;
164
165	enum rcar_isp_input csi_input;
166
167	struct v4l2_subdev subdev;
168	struct media_pad pads[RCAR_ISP_NUM_PADS];
169
170	struct v4l2_async_notifier notifier;
171	struct v4l2_subdev *remote;
172	unsigned int remote_pad;
173
174	int stream_count;
175};
176
177static inline struct rcar_isp *sd_to_isp(struct v4l2_subdev *sd)
178{
179	return container_of(sd, struct rcar_isp, subdev);
180}
181
182static inline struct rcar_isp *notifier_to_isp(struct v4l2_async_notifier *n)
183{
184	return container_of(n, struct rcar_isp, notifier);
185}
186
187static void risp_write_cs(struct rcar_isp *isp, u32 offset, u32 value)
188{
189	iowrite32(value, isp->csbase + offset);
190}
191
192static u32 risp_read_cs(struct rcar_isp *isp, u32 offset)
193{
194	return ioread32(isp->csbase + offset);
195}
196
197static int risp_power_on(struct rcar_isp *isp)
198{
199	int ret;
200
201	ret = pm_runtime_resume_and_get(isp->dev);
202	if (ret < 0)
203		return ret;
204
205	ret = reset_control_deassert(isp->rstc);
206	if (ret < 0) {
207		pm_runtime_put(isp->dev);
208		return ret;
209	}
210
211	return 0;
212}
213
214static void risp_power_off(struct rcar_isp *isp)
215{
216	reset_control_assert(isp->rstc);
217	pm_runtime_put(isp->dev);
218}
219
220static int risp_start(struct rcar_isp *isp, struct v4l2_subdev_state *state)
221{
222	const struct v4l2_mbus_framefmt *fmt;
223	const struct rcar_isp_format *format;
224	unsigned int vc;
225	u32 sel_csi = 0;
226	int ret;
227
228	fmt = v4l2_subdev_state_get_format(state, RCAR_ISP_SINK);
229	if (!fmt)
230		return -EINVAL;
231
232	format = risp_code_to_fmt(fmt->code);
233	if (!format) {
234		dev_err(isp->dev, "Unsupported bus format\n");
235		return -EINVAL;
236	}
237
238	ret = risp_power_on(isp);
239	if (ret) {
240		dev_err(isp->dev, "Failed to power on ISP\n");
241		return ret;
242	}
243
244	/* Select CSI-2 input source. */
245	if (isp->csi_input == RISP_CSI_INPUT1)
246		sel_csi = ISPINPUTSEL0_SEL_CSI0;
247
248	risp_write_cs(isp, ISPINPUTSEL0_REG,
249		      risp_read_cs(isp, ISPINPUTSEL0_REG) | sel_csi);
250
251	/* Configure Channel Selector. */
252	for (vc = 0; vc < 4; vc++) {
253		u8 ch = vc + 4;
254		u8 dt = format->datatype;
255
256		risp_write_cs(isp, ISPCS_FILTER_ID_CH_REG(ch), BIT(vc));
257		risp_write_cs(isp, ISPCS_DT_CODE03_CH_REG(ch),
258			      ISPCS_DT_CODE03_EN3 | ISPCS_DT_CODE03_DT3(dt) |
259			      ISPCS_DT_CODE03_EN2 | ISPCS_DT_CODE03_DT2(dt) |
260			      ISPCS_DT_CODE03_EN1 | ISPCS_DT_CODE03_DT1(dt) |
261			      ISPCS_DT_CODE03_EN0 | ISPCS_DT_CODE03_DT0(dt));
262	}
263
264	/* Setup processing method. */
265	risp_write_cs(isp, ISPPROCMODE_DT_REG(format->datatype),
266		      ISPPROCMODE_DT_PROC_MODE_VC3(format->procmode) |
267		      ISPPROCMODE_DT_PROC_MODE_VC2(format->procmode) |
268		      ISPPROCMODE_DT_PROC_MODE_VC1(format->procmode) |
269		      ISPPROCMODE_DT_PROC_MODE_VC0(format->procmode));
270
271	/* Start ISP. */
272	risp_write_cs(isp, ISPSTART_REG, ISPSTART_START);
273
274	ret = v4l2_subdev_enable_streams(isp->remote, isp->remote_pad,
275					 BIT_ULL(0));
276	if (ret)
277		risp_power_off(isp);
278
279	return ret;
280}
281
282static void risp_stop(struct rcar_isp *isp)
283{
284	v4l2_subdev_disable_streams(isp->remote, isp->remote_pad, BIT_ULL(0));
285
286	/* Stop ISP. */
287	risp_write_cs(isp, ISPSTART_REG, ISPSTART_STOP);
288
289	risp_power_off(isp);
290}
291
292static int risp_enable_streams(struct v4l2_subdev *sd,
293			       struct v4l2_subdev_state *state, u32 source_pad,
294			       u64 source_streams_mask)
295{
296	struct rcar_isp *isp = sd_to_isp(sd);
297	int ret = 0;
298
299	if (source_streams_mask != 1)
300		return -EINVAL;
301
302	if (!isp->remote)
303		return -ENODEV;
304
305	if (isp->stream_count == 0) {
306		ret = risp_start(isp, state);
307		if (ret)
308			return ret;
309	}
310
311	isp->stream_count += 1;
312
313	return ret;
314}
315
316static int risp_disable_streams(struct v4l2_subdev *sd,
317				struct v4l2_subdev_state *state, u32 source_pad,
318				u64 source_streams_mask)
319{
320	struct rcar_isp *isp = sd_to_isp(sd);
321
322	if (source_streams_mask != 1)
323		return -EINVAL;
324
325	if (!isp->remote)
326		return -ENODEV;
327
328	if (isp->stream_count == 1)
329		risp_stop(isp);
330
331	isp->stream_count -= 1;
332
333	return 0;
334}
335
336static int risp_set_pad_format(struct v4l2_subdev *sd,
337			       struct v4l2_subdev_state *state,
338			       struct v4l2_subdev_format *format)
339{
340	struct v4l2_mbus_framefmt *framefmt;
341
342	if (format->pad > RCAR_ISP_SINK)
343		return v4l2_subdev_get_fmt(sd, state, format);
344
345	if (!risp_code_to_fmt(format->format.code))
346		format->format.code = rcar_isp_formats[0].code;
347
348	for (unsigned int i = 0; i < RCAR_ISP_NUM_PADS; i++) {
349		framefmt = v4l2_subdev_state_get_format(state, i);
350		*framefmt = format->format;
351	}
352
353	return 0;
354}
355
356static const struct v4l2_subdev_pad_ops risp_pad_ops = {
357	.enable_streams = risp_enable_streams,
358	.disable_streams = risp_disable_streams,
359	.set_fmt = risp_set_pad_format,
360	.get_fmt = v4l2_subdev_get_fmt,
361	.link_validate = v4l2_subdev_link_validate_default,
362};
363
364static const struct v4l2_subdev_ops rcar_isp_subdev_ops = {
365	.pad	= &risp_pad_ops,
366};
367
368/* -----------------------------------------------------------------------------
369 * Async handling and registration of subdevices and links
370 */
371
372static int risp_notify_bound(struct v4l2_async_notifier *notifier,
373			     struct v4l2_subdev *subdev,
374			     struct v4l2_async_connection *asd)
375{
376	struct rcar_isp *isp = notifier_to_isp(notifier);
377	int pad;
378
379	pad = media_entity_get_fwnode_pad(&subdev->entity, asd->match.fwnode,
380					  MEDIA_PAD_FL_SOURCE);
381	if (pad < 0) {
382		dev_err(isp->dev, "Failed to find pad for %s\n", subdev->name);
383		return pad;
384	}
385
386	isp->remote = subdev;
387	isp->remote_pad = pad;
388
389	dev_dbg(isp->dev, "Bound %s pad: %d\n", subdev->name, pad);
390
391	return media_create_pad_link(&subdev->entity, pad,
392				     &isp->subdev.entity, 0,
393				     MEDIA_LNK_FL_ENABLED |
394				     MEDIA_LNK_FL_IMMUTABLE);
395}
396
397static void risp_notify_unbind(struct v4l2_async_notifier *notifier,
398			       struct v4l2_subdev *subdev,
399			       struct v4l2_async_connection *asd)
400{
401	struct rcar_isp *isp = notifier_to_isp(notifier);
402
403	isp->remote = NULL;
404
405	dev_dbg(isp->dev, "Unbind %s\n", subdev->name);
406}
407
408static const struct v4l2_async_notifier_operations risp_notify_ops = {
409	.bound = risp_notify_bound,
410	.unbind = risp_notify_unbind,
411};
412
413static int risp_parse_dt(struct rcar_isp *isp)
414{
415	struct v4l2_async_connection *asd;
416	struct fwnode_handle *fwnode;
417	struct fwnode_handle *ep;
418	unsigned int id;
419	int ret;
420
421	for (id = 0; id < 2; id++) {
422		ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(isp->dev),
423						     0, id, 0);
424		if (ep)
425			break;
426	}
427
428	if (!ep) {
429		dev_err(isp->dev, "Not connected to subdevice\n");
430		return -EINVAL;
431	}
432
433	if (id == 1)
434		isp->csi_input = RISP_CSI_INPUT1;
435
436	fwnode = fwnode_graph_get_remote_endpoint(ep);
437	fwnode_handle_put(ep);
438
439	dev_dbg(isp->dev, "Found '%pOF'\n", to_of_node(fwnode));
440
441	v4l2_async_subdev_nf_init(&isp->notifier, &isp->subdev);
442	isp->notifier.ops = &risp_notify_ops;
443
444	asd = v4l2_async_nf_add_fwnode(&isp->notifier, fwnode,
445				       struct v4l2_async_connection);
446	fwnode_handle_put(fwnode);
447	if (IS_ERR(asd))
448		return PTR_ERR(asd);
449
450	ret = v4l2_async_nf_register(&isp->notifier);
451	if (ret)
452		v4l2_async_nf_cleanup(&isp->notifier);
453
454	return ret;
455}
456
457/* -----------------------------------------------------------------------------
458 * Platform Device Driver
459 */
460
461static const struct media_entity_operations risp_entity_ops = {
462	.link_validate = v4l2_subdev_link_validate,
463};
464
465static int risp_probe_resources(struct rcar_isp *isp,
466				struct platform_device *pdev)
467{
468	struct resource *res;
469
470	/*
471	 * For backward compatibility allow cs base to be the only reg if no
472	 * reg-names are set in DT.
473	 */
474	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cs");
475	if (!res)
476		isp->csbase = devm_platform_ioremap_resource(pdev, 0);
477	else
478		isp->csbase = devm_ioremap_resource(&pdev->dev, res);
479
480	if (IS_ERR(isp->csbase))
481		return PTR_ERR(isp->csbase);
482
483	isp->rstc = devm_reset_control_get(&pdev->dev, NULL);
484
485	return PTR_ERR_OR_ZERO(isp->rstc);
486}
487
488static const struct of_device_id risp_of_id_table[] = {
489	{ .compatible = "renesas,r8a779a0-isp" },
490	{ .compatible = "renesas,r8a779g0-isp" },
491	/* Keep above for compatibility with old DTB files. */
492	{ .compatible = "renesas,rcar-gen4-isp" },
493	{ /* sentinel */ }
494};
495MODULE_DEVICE_TABLE(of, risp_of_id_table);
496
497static int risp_probe(struct platform_device *pdev)
498{
499	struct rcar_isp *isp;
500	unsigned int i;
501	int ret;
502
503	isp = devm_kzalloc(&pdev->dev, sizeof(*isp), GFP_KERNEL);
504	if (!isp)
505		return -ENOMEM;
506
507	isp->dev = &pdev->dev;
508
509	ret = risp_probe_resources(isp, pdev);
510	if (ret) {
511		dev_err(isp->dev, "Failed to get resources\n");
512		return ret;
513	}
514
515	platform_set_drvdata(pdev, isp);
516
517	pm_runtime_enable(&pdev->dev);
518
519	ret = risp_parse_dt(isp);
520	if (ret)
521		goto error_pm;
522
523	isp->subdev.owner = THIS_MODULE;
524	isp->subdev.dev = &pdev->dev;
525	v4l2_subdev_init(&isp->subdev, &rcar_isp_subdev_ops);
526	v4l2_set_subdevdata(&isp->subdev, &pdev->dev);
527	snprintf(isp->subdev.name, sizeof(isp->subdev.name), "%s %s",
528		 KBUILD_MODNAME, dev_name(&pdev->dev));
529	isp->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
530
531	isp->subdev.entity.function = MEDIA_ENT_F_VID_MUX;
532	isp->subdev.entity.ops = &risp_entity_ops;
533
534	isp->pads[RCAR_ISP_SINK].flags = MEDIA_PAD_FL_SINK;
535	for (i = RCAR_ISP_PORT0; i < RCAR_ISP_NUM_PADS; i++)
536		isp->pads[i].flags = MEDIA_PAD_FL_SOURCE;
537
538	ret = media_entity_pads_init(&isp->subdev.entity, RCAR_ISP_NUM_PADS,
539				     isp->pads);
540	if (ret)
541		goto error_notifier;
542
543	ret = v4l2_subdev_init_finalize(&isp->subdev);
544	if (ret)
545		goto error_notifier;
546
547	ret = v4l2_async_register_subdev(&isp->subdev);
548	if (ret < 0)
549		goto error_subdev;
550
551	dev_info(isp->dev, "Using CSI-2 input: %u\n", isp->csi_input);
552
553	return 0;
554
555error_subdev:
556	v4l2_subdev_cleanup(&isp->subdev);
557error_notifier:
558	v4l2_async_nf_unregister(&isp->notifier);
559	v4l2_async_nf_cleanup(&isp->notifier);
560error_pm:
561	pm_runtime_disable(&pdev->dev);
562
563	return ret;
564}
565
566static void risp_remove(struct platform_device *pdev)
567{
568	struct rcar_isp *isp = platform_get_drvdata(pdev);
569
570	v4l2_async_nf_unregister(&isp->notifier);
571	v4l2_async_nf_cleanup(&isp->notifier);
572
573	v4l2_async_unregister_subdev(&isp->subdev);
574	v4l2_subdev_cleanup(&isp->subdev);
575
576	pm_runtime_disable(&pdev->dev);
577}
578
579static struct platform_driver rcar_isp_driver = {
580	.driver = {
581		.name = "rcar-isp",
582		.suppress_bind_attrs = true,
583		.of_match_table = risp_of_id_table,
584	},
585	.probe = risp_probe,
586	.remove = risp_remove,
587};
588
589module_platform_driver(rcar_isp_driver);
590
591MODULE_AUTHOR("Niklas Söderlund <niklas.soderlund@ragnatech.se>");
592MODULE_DESCRIPTION("Renesas R-Car ISP Channel Selector driver");
593MODULE_LICENSE("GPL");
~/ Read-only file preview.