1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * TI OMAP4 ISS V4L2 Driver - ISP IPIPE module
4 *
5 * Copyright (C) 2012 Texas Instruments, Inc.
6 *
7 * Author: Sergio Aguirre <sergio.a.aguirre@gmail.com>
8 */
9
10#include <linux/module.h>
11#include <linux/uaccess.h>
12#include <linux/delay.h>
13#include <linux/device.h>
14#include <linux/dma-mapping.h>
15#include <linux/mm.h>
16#include <linux/sched.h>
17
18#include "iss.h"
19#include "iss_regs.h"
20#include "iss_ipipe.h"
21
22static struct v4l2_mbus_framefmt *
23__ipipe_get_format(struct iss_ipipe_device *ipipe,
24		   struct v4l2_subdev_state *sd_state,
25		   unsigned int pad,
26		   enum v4l2_subdev_format_whence which);
27
28static const unsigned int ipipe_fmts[] = {
29	MEDIA_BUS_FMT_SGRBG10_1X10,
30	MEDIA_BUS_FMT_SRGGB10_1X10,
31	MEDIA_BUS_FMT_SBGGR10_1X10,
32	MEDIA_BUS_FMT_SGBRG10_1X10,
33};
34
35/*
36 * ipipe_print_status - Print current IPIPE Module register values.
37 * @ipipe: Pointer to ISS ISP IPIPE device.
38 *
39 * Also prints other debug information stored in the IPIPE module.
40 */
41#define IPIPE_PRINT_REGISTER(iss, name)\
42	dev_dbg(iss->dev, "###IPIPE " #name "=0x%08x\n", \
43		iss_reg_read(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_##name))
44
45static void ipipe_print_status(struct iss_ipipe_device *ipipe)
46{
47	struct iss_device *iss = to_iss_device(ipipe);
48
49	dev_dbg(iss->dev, "-------------IPIPE Register dump-------------\n");
50
51	IPIPE_PRINT_REGISTER(iss, SRC_EN);
52	IPIPE_PRINT_REGISTER(iss, SRC_MODE);
53	IPIPE_PRINT_REGISTER(iss, SRC_FMT);
54	IPIPE_PRINT_REGISTER(iss, SRC_COL);
55	IPIPE_PRINT_REGISTER(iss, SRC_VPS);
56	IPIPE_PRINT_REGISTER(iss, SRC_VSZ);
57	IPIPE_PRINT_REGISTER(iss, SRC_HPS);
58	IPIPE_PRINT_REGISTER(iss, SRC_HSZ);
59	IPIPE_PRINT_REGISTER(iss, GCK_MMR);
60	IPIPE_PRINT_REGISTER(iss, YUV_PHS);
61
62	dev_dbg(iss->dev, "-----------------------------------------------\n");
63}
64
65/*
66 * ipipe_enable - Enable/Disable IPIPE.
67 * @enable: enable flag
68 *
69 */
70static void ipipe_enable(struct iss_ipipe_device *ipipe, u8 enable)
71{
72	struct iss_device *iss = to_iss_device(ipipe);
73
74	iss_reg_update(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_SRC_EN,
75		       IPIPE_SRC_EN_EN, enable ? IPIPE_SRC_EN_EN : 0);
76}
77
78/* -----------------------------------------------------------------------------
79 * Format- and pipeline-related configuration helpers
80 */
81
82static void ipipe_configure(struct iss_ipipe_device *ipipe)
83{
84	struct iss_device *iss = to_iss_device(ipipe);
85	struct v4l2_mbus_framefmt *format;
86
87	/* IPIPE_PAD_SINK */
88	format = &ipipe->formats[IPIPE_PAD_SINK];
89
90	/* NOTE: Currently just supporting pipeline IN: RGB, OUT: YUV422 */
91	iss_reg_write(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_SRC_FMT,
92		      IPIPE_SRC_FMT_RAW2YUV);
93
94	/* Enable YUV444 -> YUV422 conversion */
95	iss_reg_write(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_YUV_PHS,
96		      IPIPE_YUV_PHS_LPF);
97
98	iss_reg_write(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_SRC_VPS, 0);
99	iss_reg_write(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_SRC_HPS, 0);
100	iss_reg_write(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_SRC_VSZ,
101		      (format->height - 2) & IPIPE_SRC_VSZ_MASK);
102	iss_reg_write(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_SRC_HSZ,
103		      (format->width - 1) & IPIPE_SRC_HSZ_MASK);
104
105	/* Ignore ipipeif_wrt signal, and operate on-the-fly.  */
106	iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_SRC_MODE,
107		    IPIPE_SRC_MODE_WRT | IPIPE_SRC_MODE_OST);
108
109	/* HACK: Values tuned for Ducati SW (OV) */
110	iss_reg_write(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_SRC_COL,
111		      IPIPE_SRC_COL_EE_B | IPIPE_SRC_COL_EO_GB |
112		      IPIPE_SRC_COL_OE_GR | IPIPE_SRC_COL_OO_R);
113
114	/* IPIPE_PAD_SOURCE_VP */
115	format = &ipipe->formats[IPIPE_PAD_SOURCE_VP];
116	/* Do nothing? */
117}
118
119/* -----------------------------------------------------------------------------
120 * V4L2 subdev operations
121 */
122
123/*
124 * ipipe_set_stream - Enable/Disable streaming on the IPIPE module
125 * @sd: ISP IPIPE V4L2 subdevice
126 * @enable: Enable/disable stream
127 */
128static int ipipe_set_stream(struct v4l2_subdev *sd, int enable)
129{
130	struct iss_ipipe_device *ipipe = v4l2_get_subdevdata(sd);
131	struct iss_device *iss = to_iss_device(ipipe);
132	int ret = 0;
133
134	if (ipipe->state == ISS_PIPELINE_STREAM_STOPPED) {
135		if (enable == ISS_PIPELINE_STREAM_STOPPED)
136			return 0;
137
138		omap4iss_isp_subclk_enable(iss, OMAP4_ISS_ISP_SUBCLK_IPIPE);
139
140		/* Enable clk_arm_g0 */
141		iss_reg_write(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_GCK_MMR,
142			      IPIPE_GCK_MMR_REG);
143
144		/* Enable clk_pix_g[3:0] */
145		iss_reg_write(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_GCK_PIX,
146			      IPIPE_GCK_PIX_G3 | IPIPE_GCK_PIX_G2 |
147			      IPIPE_GCK_PIX_G1 | IPIPE_GCK_PIX_G0);
148	}
149
150	switch (enable) {
151	case ISS_PIPELINE_STREAM_CONTINUOUS:
152
153		ipipe_configure(ipipe);
154		ipipe_print_status(ipipe);
155
156		atomic_set(&ipipe->stopping, 0);
157		ipipe_enable(ipipe, 1);
158		break;
159
160	case ISS_PIPELINE_STREAM_STOPPED:
161		if (ipipe->state == ISS_PIPELINE_STREAM_STOPPED)
162			return 0;
163		if (omap4iss_module_sync_idle(&sd->entity, &ipipe->wait,
164					      &ipipe->stopping))
165			ret = -ETIMEDOUT;
166
167		ipipe_enable(ipipe, 0);
168		omap4iss_isp_subclk_disable(iss, OMAP4_ISS_ISP_SUBCLK_IPIPE);
169		break;
170	}
171
172	ipipe->state = enable;
173	return ret;
174}
175
176static struct v4l2_mbus_framefmt *
177__ipipe_get_format(struct iss_ipipe_device *ipipe,
178		   struct v4l2_subdev_state *sd_state,
179		   unsigned int pad,
180		   enum v4l2_subdev_format_whence which)
181{
182	if (which == V4L2_SUBDEV_FORMAT_TRY)
183		return v4l2_subdev_state_get_format(sd_state, pad);
184
185	return &ipipe->formats[pad];
186}
187
188/*
189 * ipipe_try_format - Try video format on a pad
190 * @ipipe: ISS IPIPE device
191 * @sd_state: V4L2 subdev state
192 * @pad: Pad number
193 * @fmt: Format
194 */
195static void
196ipipe_try_format(struct iss_ipipe_device *ipipe,
197		 struct v4l2_subdev_state *sd_state,
198		 unsigned int pad,
199		 struct v4l2_mbus_framefmt *fmt,
200		 enum v4l2_subdev_format_whence which)
201{
202	struct v4l2_mbus_framefmt *format;
203	unsigned int width = fmt->width;
204	unsigned int height = fmt->height;
205	unsigned int i;
206
207	switch (pad) {
208	case IPIPE_PAD_SINK:
209		for (i = 0; i < ARRAY_SIZE(ipipe_fmts); i++) {
210			if (fmt->code == ipipe_fmts[i])
211				break;
212		}
213
214		/* If not found, use SGRBG10 as default */
215		if (i >= ARRAY_SIZE(ipipe_fmts))
216			fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10;
217
218		/* Clamp the input size. */
219		fmt->width = clamp_t(u32, width, 1, 8192);
220		fmt->height = clamp_t(u32, height, 1, 8192);
221		fmt->colorspace = V4L2_COLORSPACE_SRGB;
222		break;
223
224	case IPIPE_PAD_SOURCE_VP:
225		format = __ipipe_get_format(ipipe, sd_state, IPIPE_PAD_SINK,
226					    which);
227		memcpy(fmt, format, sizeof(*fmt));
228
229		fmt->code = MEDIA_BUS_FMT_UYVY8_1X16;
230		fmt->width = clamp_t(u32, width, 32, fmt->width);
231		fmt->height = clamp_t(u32, height, 32, fmt->height);
232		fmt->colorspace = V4L2_COLORSPACE_JPEG;
233		break;
234	}
235
236	fmt->field = V4L2_FIELD_NONE;
237}
238
239/*
240 * ipipe_enum_mbus_code - Handle pixel format enumeration
241 * @sd     : pointer to v4l2 subdev structure
242 * @sd_state: V4L2 subdev state
243 * @code   : pointer to v4l2_subdev_mbus_code_enum structure
244 * return -EINVAL or zero on success
245 */
246static int ipipe_enum_mbus_code(struct v4l2_subdev *sd,
247				struct v4l2_subdev_state *sd_state,
248				struct v4l2_subdev_mbus_code_enum *code)
249{
250	switch (code->pad) {
251	case IPIPE_PAD_SINK:
252		if (code->index >= ARRAY_SIZE(ipipe_fmts))
253			return -EINVAL;
254
255		code->code = ipipe_fmts[code->index];
256		break;
257
258	case IPIPE_PAD_SOURCE_VP:
259		/* FIXME: Forced format conversion inside IPIPE ? */
260		if (code->index != 0)
261			return -EINVAL;
262
263		code->code = MEDIA_BUS_FMT_UYVY8_1X16;
264		break;
265
266	default:
267		return -EINVAL;
268	}
269
270	return 0;
271}
272
273static int ipipe_enum_frame_size(struct v4l2_subdev *sd,
274				 struct v4l2_subdev_state *sd_state,
275				 struct v4l2_subdev_frame_size_enum *fse)
276{
277	struct iss_ipipe_device *ipipe = v4l2_get_subdevdata(sd);
278	struct v4l2_mbus_framefmt format;
279
280	if (fse->index != 0)
281		return -EINVAL;
282
283	format.code = fse->code;
284	format.width = 1;
285	format.height = 1;
286	ipipe_try_format(ipipe, sd_state, fse->pad, &format, fse->which);
287	fse->min_width = format.width;
288	fse->min_height = format.height;
289
290	if (format.code != fse->code)
291		return -EINVAL;
292
293	format.code = fse->code;
294	format.width = -1;
295	format.height = -1;
296	ipipe_try_format(ipipe, sd_state, fse->pad, &format, fse->which);
297	fse->max_width = format.width;
298	fse->max_height = format.height;
299
300	return 0;
301}
302
303/*
304 * ipipe_get_format - Retrieve the video format on a pad
305 * @sd : ISP IPIPE V4L2 subdevice
306 * @sd_state: V4L2 subdev state
307 * @fmt: Format
308 *
309 * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
310 * to the format type.
311 */
312static int ipipe_get_format(struct v4l2_subdev *sd,
313			    struct v4l2_subdev_state *sd_state,
314			    struct v4l2_subdev_format *fmt)
315{
316	struct iss_ipipe_device *ipipe = v4l2_get_subdevdata(sd);
317	struct v4l2_mbus_framefmt *format;
318
319	format = __ipipe_get_format(ipipe, sd_state, fmt->pad, fmt->which);
320	if (!format)
321		return -EINVAL;
322
323	fmt->format = *format;
324	return 0;
325}
326
327/*
328 * ipipe_set_format - Set the video format on a pad
329 * @sd : ISP IPIPE V4L2 subdevice
330 * @sd_state: V4L2 subdev state
331 * @fmt: Format
332 *
333 * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
334 * to the format type.
335 */
336static int ipipe_set_format(struct v4l2_subdev *sd,
337			    struct v4l2_subdev_state *sd_state,
338			    struct v4l2_subdev_format *fmt)
339{
340	struct iss_ipipe_device *ipipe = v4l2_get_subdevdata(sd);
341	struct v4l2_mbus_framefmt *format;
342
343	format = __ipipe_get_format(ipipe, sd_state, fmt->pad, fmt->which);
344	if (!format)
345		return -EINVAL;
346
347	ipipe_try_format(ipipe, sd_state, fmt->pad, &fmt->format, fmt->which);
348	*format = fmt->format;
349
350	/* Propagate the format from sink to source */
351	if (fmt->pad == IPIPE_PAD_SINK) {
352		format = __ipipe_get_format(ipipe, sd_state,
353					    IPIPE_PAD_SOURCE_VP,
354					    fmt->which);
355		*format = fmt->format;
356		ipipe_try_format(ipipe, sd_state, IPIPE_PAD_SOURCE_VP, format,
357				 fmt->which);
358	}
359
360	return 0;
361}
362
363static int ipipe_link_validate(struct v4l2_subdev *sd, struct media_link *link,
364			       struct v4l2_subdev_format *source_fmt,
365			       struct v4l2_subdev_format *sink_fmt)
366{
367	/* Check if the two ends match */
368	if (source_fmt->format.width != sink_fmt->format.width ||
369	    source_fmt->format.height != sink_fmt->format.height)
370		return -EPIPE;
371
372	if (source_fmt->format.code != sink_fmt->format.code)
373		return -EPIPE;
374
375	return 0;
376}
377
378/*
379 * ipipe_init_formats - Initialize formats on all pads
380 * @sd: ISP IPIPE V4L2 subdevice
381 * @fh: V4L2 subdev file handle
382 *
383 * Initialize all pad formats with default values. If fh is not NULL, try
384 * formats are initialized on the file handle. Otherwise active formats are
385 * initialized on the device.
386 */
387static int ipipe_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
388{
389	struct v4l2_subdev_format format;
390
391	memset(&format, 0, sizeof(format));
392	format.pad = IPIPE_PAD_SINK;
393	format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
394	format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10;
395	format.format.width = 4096;
396	format.format.height = 4096;
397	ipipe_set_format(sd, fh ? fh->state : NULL, &format);
398
399	return 0;
400}
401
402/* V4L2 subdev video operations */
403static const struct v4l2_subdev_video_ops ipipe_v4l2_video_ops = {
404	.s_stream = ipipe_set_stream,
405};
406
407/* V4L2 subdev pad operations */
408static const struct v4l2_subdev_pad_ops ipipe_v4l2_pad_ops = {
409	.enum_mbus_code = ipipe_enum_mbus_code,
410	.enum_frame_size = ipipe_enum_frame_size,
411	.get_fmt = ipipe_get_format,
412	.set_fmt = ipipe_set_format,
413	.link_validate = ipipe_link_validate,
414};
415
416/* V4L2 subdev operations */
417static const struct v4l2_subdev_ops ipipe_v4l2_ops = {
418	.video = &ipipe_v4l2_video_ops,
419	.pad = &ipipe_v4l2_pad_ops,
420};
421
422/* V4L2 subdev internal operations */
423static const struct v4l2_subdev_internal_ops ipipe_v4l2_internal_ops = {
424	.open = ipipe_init_formats,
425};
426
427/* -----------------------------------------------------------------------------
428 * Media entity operations
429 */
430
431/*
432 * ipipe_link_setup - Setup IPIPE connections
433 * @entity: IPIPE media entity
434 * @local: Pad at the local end of the link
435 * @remote: Pad at the remote end of the link
436 * @flags: Link flags
437 *
438 * return -EINVAL or zero on success
439 */
440static int ipipe_link_setup(struct media_entity *entity,
441			    const struct media_pad *local,
442			    const struct media_pad *remote, u32 flags)
443{
444	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
445	struct iss_ipipe_device *ipipe = v4l2_get_subdevdata(sd);
446	struct iss_device *iss = to_iss_device(ipipe);
447
448	if (!is_media_entity_v4l2_subdev(remote->entity))
449		return -EINVAL;
450
451	switch (local->index) {
452	case IPIPE_PAD_SINK:
453		/* Read from IPIPEIF. */
454		if (!(flags & MEDIA_LNK_FL_ENABLED)) {
455			ipipe->input = IPIPE_INPUT_NONE;
456			break;
457		}
458
459		if (ipipe->input != IPIPE_INPUT_NONE)
460			return -EBUSY;
461
462		if (remote->entity == &iss->ipipeif.subdev.entity)
463			ipipe->input = IPIPE_INPUT_IPIPEIF;
464
465		break;
466
467	case IPIPE_PAD_SOURCE_VP:
468		/* Send to RESIZER */
469		if (flags & MEDIA_LNK_FL_ENABLED) {
470			if (ipipe->output & ~IPIPE_OUTPUT_VP)
471				return -EBUSY;
472			ipipe->output |= IPIPE_OUTPUT_VP;
473		} else {
474			ipipe->output &= ~IPIPE_OUTPUT_VP;
475		}
476		break;
477
478	default:
479		return -EINVAL;
480	}
481
482	return 0;
483}
484
485/* media operations */
486static const struct media_entity_operations ipipe_media_ops = {
487	.link_setup = ipipe_link_setup,
488	.link_validate = v4l2_subdev_link_validate,
489};
490
491/*
492 * ipipe_init_entities - Initialize V4L2 subdev and media entity
493 * @ipipe: ISS ISP IPIPE module
494 *
495 * Return 0 on success and a negative error code on failure.
496 */
497static int ipipe_init_entities(struct iss_ipipe_device *ipipe)
498{
499	struct v4l2_subdev *sd = &ipipe->subdev;
500	struct media_pad *pads = ipipe->pads;
501	struct media_entity *me = &sd->entity;
502	int ret;
503
504	ipipe->input = IPIPE_INPUT_NONE;
505
506	v4l2_subdev_init(sd, &ipipe_v4l2_ops);
507	sd->internal_ops = &ipipe_v4l2_internal_ops;
508	strscpy(sd->name, "OMAP4 ISS ISP IPIPE", sizeof(sd->name));
509	sd->grp_id = BIT(16);	/* group ID for iss subdevs */
510	v4l2_set_subdevdata(sd, ipipe);
511	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
512
513	pads[IPIPE_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
514	pads[IPIPE_PAD_SOURCE_VP].flags = MEDIA_PAD_FL_SOURCE;
515
516	me->ops = &ipipe_media_ops;
517	ret = media_entity_pads_init(me, IPIPE_PADS_NUM, pads);
518	if (ret < 0)
519		return ret;
520
521	ipipe_init_formats(sd, NULL);
522
523	return 0;
524}
525
526void omap4iss_ipipe_unregister_entities(struct iss_ipipe_device *ipipe)
527{
528	v4l2_device_unregister_subdev(&ipipe->subdev);
529}
530
531int omap4iss_ipipe_register_entities(struct iss_ipipe_device *ipipe,
532				     struct v4l2_device *vdev)
533{
534	int ret;
535
536	/* Register the subdev and video node. */
537	ret = v4l2_device_register_subdev(vdev, &ipipe->subdev);
538	if (ret < 0)
539		goto error;
540
541	return 0;
542
543error:
544	omap4iss_ipipe_unregister_entities(ipipe);
545	return ret;
546}
547
548/* -----------------------------------------------------------------------------
549 * ISP IPIPE initialisation and cleanup
550 */
551
552/*
553 * omap4iss_ipipe_init - IPIPE module initialization.
554 * @iss: Device pointer specific to the OMAP4 ISS.
555 *
556 * TODO: Get the initialisation values from platform data.
557 *
558 * Return 0 on success or a negative error code otherwise.
559 */
560int omap4iss_ipipe_init(struct iss_device *iss)
561{
562	struct iss_ipipe_device *ipipe = &iss->ipipe;
563
564	ipipe->state = ISS_PIPELINE_STREAM_STOPPED;
565	init_waitqueue_head(&ipipe->wait);
566
567	return ipipe_init_entities(ipipe);
568}
569
570/*
571 * omap4iss_ipipe_cleanup - IPIPE module cleanup.
572 * @iss: Device pointer specific to the OMAP4 ISS.
573 */
574void omap4iss_ipipe_cleanup(struct iss_device *iss)
575{
576	struct iss_ipipe_device *ipipe = &iss->ipipe;
577
578	media_entity_cleanup(&ipipe->subdev.entity);
579}
580