1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * TI OMAP4 ISS V4L2 Driver - ISP RESIZER 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_resizer.h"
21 
22 static const unsigned int resizer_fmts[] = {
23 	MEDIA_BUS_FMT_UYVY8_1X16,
24 	MEDIA_BUS_FMT_YUYV8_1X16,
25 };
26 
27 /*
28  * resizer_print_status - Print current RESIZER Module register values.
29  * @resizer: Pointer to ISS ISP RESIZER device.
30  *
31  * Also prints other debug information stored in the RESIZER module.
32  */
33 #define RSZ_PRINT_REGISTER(iss, name)\
34 	dev_dbg(iss->dev, "###RSZ " #name "=0x%08x\n", \
35 		iss_reg_read(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_##name))
36 
37 #define RZA_PRINT_REGISTER(iss, name)\
38 	dev_dbg(iss->dev, "###RZA " #name "=0x%08x\n", \
39 		iss_reg_read(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_##name))
40 
41 static void resizer_print_status(struct iss_resizer_device *resizer)
42 {
43 	struct iss_device *iss = to_iss_device(resizer);
44 
45 	dev_dbg(iss->dev, "-------------RESIZER Register dump-------------\n");
46 
47 	RSZ_PRINT_REGISTER(iss, SYSCONFIG);
48 	RSZ_PRINT_REGISTER(iss, IN_FIFO_CTRL);
49 	RSZ_PRINT_REGISTER(iss, FRACDIV);
50 	RSZ_PRINT_REGISTER(iss, SRC_EN);
51 	RSZ_PRINT_REGISTER(iss, SRC_MODE);
52 	RSZ_PRINT_REGISTER(iss, SRC_FMT0);
53 	RSZ_PRINT_REGISTER(iss, SRC_FMT1);
54 	RSZ_PRINT_REGISTER(iss, SRC_VPS);
55 	RSZ_PRINT_REGISTER(iss, SRC_VSZ);
56 	RSZ_PRINT_REGISTER(iss, SRC_HPS);
57 	RSZ_PRINT_REGISTER(iss, SRC_HSZ);
58 	RSZ_PRINT_REGISTER(iss, DMA_RZA);
59 	RSZ_PRINT_REGISTER(iss, DMA_RZB);
60 	RSZ_PRINT_REGISTER(iss, DMA_STA);
61 	RSZ_PRINT_REGISTER(iss, GCK_MMR);
62 	RSZ_PRINT_REGISTER(iss, GCK_SDR);
63 	RSZ_PRINT_REGISTER(iss, IRQ_RZA);
64 	RSZ_PRINT_REGISTER(iss, IRQ_RZB);
65 	RSZ_PRINT_REGISTER(iss, YUV_Y_MIN);
66 	RSZ_PRINT_REGISTER(iss, YUV_Y_MAX);
67 	RSZ_PRINT_REGISTER(iss, YUV_C_MIN);
68 	RSZ_PRINT_REGISTER(iss, YUV_C_MAX);
69 	RSZ_PRINT_REGISTER(iss, SEQ);
70 
71 	RZA_PRINT_REGISTER(iss, EN);
72 	RZA_PRINT_REGISTER(iss, MODE);
73 	RZA_PRINT_REGISTER(iss, 420);
74 	RZA_PRINT_REGISTER(iss, I_VPS);
75 	RZA_PRINT_REGISTER(iss, I_HPS);
76 	RZA_PRINT_REGISTER(iss, O_VSZ);
77 	RZA_PRINT_REGISTER(iss, O_HSZ);
78 	RZA_PRINT_REGISTER(iss, V_PHS_Y);
79 	RZA_PRINT_REGISTER(iss, V_PHS_C);
80 	RZA_PRINT_REGISTER(iss, V_DIF);
81 	RZA_PRINT_REGISTER(iss, V_TYP);
82 	RZA_PRINT_REGISTER(iss, V_LPF);
83 	RZA_PRINT_REGISTER(iss, H_PHS);
84 	RZA_PRINT_REGISTER(iss, H_DIF);
85 	RZA_PRINT_REGISTER(iss, H_TYP);
86 	RZA_PRINT_REGISTER(iss, H_LPF);
87 	RZA_PRINT_REGISTER(iss, DWN_EN);
88 	RZA_PRINT_REGISTER(iss, SDR_Y_BAD_H);
89 	RZA_PRINT_REGISTER(iss, SDR_Y_BAD_L);
90 	RZA_PRINT_REGISTER(iss, SDR_Y_SAD_H);
91 	RZA_PRINT_REGISTER(iss, SDR_Y_SAD_L);
92 	RZA_PRINT_REGISTER(iss, SDR_Y_OFT);
93 	RZA_PRINT_REGISTER(iss, SDR_Y_PTR_S);
94 	RZA_PRINT_REGISTER(iss, SDR_Y_PTR_E);
95 	RZA_PRINT_REGISTER(iss, SDR_C_BAD_H);
96 	RZA_PRINT_REGISTER(iss, SDR_C_BAD_L);
97 	RZA_PRINT_REGISTER(iss, SDR_C_SAD_H);
98 	RZA_PRINT_REGISTER(iss, SDR_C_SAD_L);
99 	RZA_PRINT_REGISTER(iss, SDR_C_OFT);
100 	RZA_PRINT_REGISTER(iss, SDR_C_PTR_S);
101 	RZA_PRINT_REGISTER(iss, SDR_C_PTR_E);
102 
103 	dev_dbg(iss->dev, "-----------------------------------------------\n");
104 }
105 
106 /*
107  * resizer_enable - Enable/Disable RESIZER.
108  * @enable: enable flag
109  *
110  */
111 static void resizer_enable(struct iss_resizer_device *resizer, u8 enable)
112 {
113 	struct iss_device *iss = to_iss_device(resizer);
114 
115 	iss_reg_update(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_EN,
116 		       RSZ_SRC_EN_SRC_EN, enable ? RSZ_SRC_EN_SRC_EN : 0);
117 
118 	/* TODO: Enable RSZB */
119 	iss_reg_update(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_EN, RSZ_EN_EN,
120 		       enable ? RSZ_EN_EN : 0);
121 }
122 
123 /* -----------------------------------------------------------------------------
124  * Format- and pipeline-related configuration helpers
125  */
126 
127 /*
128  * resizer_set_outaddr - Set memory address to save output image
129  * @resizer: Pointer to ISP RESIZER device.
130  * @addr: 32-bit memory address aligned on 32 byte boundary.
131  *
132  * Sets the memory address where the output will be saved.
133  */
134 static void resizer_set_outaddr(struct iss_resizer_device *resizer, u32 addr)
135 {
136 	struct iss_device *iss = to_iss_device(resizer);
137 	struct v4l2_mbus_framefmt *informat, *outformat;
138 
139 	informat = &resizer->formats[RESIZER_PAD_SINK];
140 	outformat = &resizer->formats[RESIZER_PAD_SOURCE_MEM];
141 
142 	/* Save address split in Base Address H & L */
143 	iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_Y_BAD_H,
144 		      (addr >> 16) & 0xffff);
145 	iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_Y_BAD_L,
146 		      addr & 0xffff);
147 
148 	/* SAD = BAD */
149 	iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_Y_SAD_H,
150 		      (addr >> 16) & 0xffff);
151 	iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_Y_SAD_L,
152 		      addr & 0xffff);
153 
154 	/* Program UV buffer address... Hardcoded to be contiguous! */
155 	if ((informat->code == MEDIA_BUS_FMT_UYVY8_1X16) &&
156 	    (outformat->code == MEDIA_BUS_FMT_YUYV8_1_5X8)) {
157 		u32 c_addr = addr + resizer->video_out.bpl_value
158 			   * outformat->height;
159 
160 		/* Ensure Y_BAD_L[6:0] = C_BAD_L[6:0]*/
161 		if ((c_addr ^ addr) & 0x7f) {
162 			c_addr &= ~0x7f;
163 			c_addr += 0x80;
164 			c_addr |= addr & 0x7f;
165 		}
166 
167 		/* Save address split in Base Address H & L */
168 		iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_C_BAD_H,
169 			      (c_addr >> 16) & 0xffff);
170 		iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_C_BAD_L,
171 			      c_addr & 0xffff);
172 
173 		/* SAD = BAD */
174 		iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_C_SAD_H,
175 			      (c_addr >> 16) & 0xffff);
176 		iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_C_SAD_L,
177 			      c_addr & 0xffff);
178 	}
179 }
180 
181 static void resizer_configure(struct iss_resizer_device *resizer)
182 {
183 	struct iss_device *iss = to_iss_device(resizer);
184 	struct v4l2_mbus_framefmt *informat, *outformat;
185 
186 	informat = &resizer->formats[RESIZER_PAD_SINK];
187 	outformat = &resizer->formats[RESIZER_PAD_SOURCE_MEM];
188 
189 	/* Disable pass-through more. Despite its name, the BYPASS bit controls
190 	 * pass-through mode, not bypass mode.
191 	 */
192 	iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_FMT0,
193 		    RSZ_SRC_FMT0_BYPASS);
194 
195 	/* Select RSZ input */
196 	iss_reg_update(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_FMT0,
197 		       RSZ_SRC_FMT0_SEL,
198 		       resizer->input == RESIZER_INPUT_IPIPEIF ?
199 		       RSZ_SRC_FMT0_SEL : 0);
200 
201 	/* RSZ ignores WEN signal from IPIPE/IPIPEIF */
202 	iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_MODE,
203 		    RSZ_SRC_MODE_WRT);
204 
205 	/* Set Resizer in free-running mode */
206 	iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_MODE,
207 		    RSZ_SRC_MODE_OST);
208 
209 	/* Init Resizer A */
210 	iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_MODE,
211 		    RZA_MODE_ONE_SHOT);
212 
213 	/* Set size related things now */
214 	iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_VPS, 0);
215 	iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_HPS, 0);
216 	iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_VSZ,
217 		      informat->height - 2);
218 	iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_HSZ,
219 		      informat->width - 1);
220 
221 	iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_I_VPS, 0);
222 	iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_I_HPS, 0);
223 
224 	iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_O_VSZ,
225 		      outformat->height - 2);
226 	iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_O_HSZ,
227 		      outformat->width - 1);
228 
229 	iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_V_DIF, 0x100);
230 	iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_H_DIF, 0x100);
231 
232 	/* Buffer output settings */
233 	iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_Y_PTR_S, 0);
234 	iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_Y_PTR_E,
235 		      outformat->height - 1);
236 
237 	iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_Y_OFT,
238 		      resizer->video_out.bpl_value);
239 
240 	/* UYVY -> NV12 conversion */
241 	if ((informat->code == MEDIA_BUS_FMT_UYVY8_1X16) &&
242 	    (outformat->code == MEDIA_BUS_FMT_YUYV8_1_5X8)) {
243 		iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_420,
244 			      RSZ_420_CEN | RSZ_420_YEN);
245 
246 		/* UV Buffer output settings */
247 		iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_C_PTR_S,
248 			      0);
249 		iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_C_PTR_E,
250 			      outformat->height - 1);
251 
252 		iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_C_OFT,
253 			      resizer->video_out.bpl_value);
254 	} else {
255 		iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_420, 0);
256 	}
257 }
258 
259 /* -----------------------------------------------------------------------------
260  * Interrupt handling
261  */
262 
263 static void resizer_isr_buffer(struct iss_resizer_device *resizer)
264 {
265 	struct iss_buffer *buffer;
266 
267 	/* The whole resizer needs to be stopped. Disabling RZA only produces
268 	 * input FIFO overflows, most probably when the next frame is received.
269 	 */
270 	resizer_enable(resizer, 0);
271 
272 	buffer = omap4iss_video_buffer_next(&resizer->video_out);
273 	if (!buffer)
274 		return;
275 
276 	resizer_set_outaddr(resizer, buffer->iss_addr);
277 
278 	resizer_enable(resizer, 1);
279 }
280 
281 /*
282  * omap4iss_resizer_isr - Configure resizer during interframe time.
283  * @resizer: Pointer to ISP RESIZER device.
284  * @events: RESIZER events
285  */
286 void omap4iss_resizer_isr(struct iss_resizer_device *resizer, u32 events)
287 {
288 	struct iss_device *iss = to_iss_device(resizer);
289 	struct iss_pipeline *pipe =
290 			     to_iss_pipeline(&resizer->subdev.entity);
291 
292 	if (events & (ISP5_IRQ_RSZ_FIFO_IN_BLK_ERR |
293 		      ISP5_IRQ_RSZ_FIFO_OVF)) {
294 		dev_dbg(iss->dev, "RSZ Err: FIFO_IN_BLK:%d, FIFO_OVF:%d\n",
295 			events & ISP5_IRQ_RSZ_FIFO_IN_BLK_ERR ? 1 : 0,
296 			events & ISP5_IRQ_RSZ_FIFO_OVF ? 1 : 0);
297 		omap4iss_pipeline_cancel_stream(pipe);
298 	}
299 
300 	if (omap4iss_module_sync_is_stopping(&resizer->wait,
301 					     &resizer->stopping))
302 		return;
303 
304 	if (events & ISP5_IRQ_RSZ_INT_DMA)
305 		resizer_isr_buffer(resizer);
306 }
307 
308 /* -----------------------------------------------------------------------------
309  * ISS video operations
310  */
311 
312 static int resizer_video_queue(struct iss_video *video,
313 			       struct iss_buffer *buffer)
314 {
315 	struct iss_resizer_device *resizer = container_of(video,
316 				struct iss_resizer_device, video_out);
317 
318 	if (!(resizer->output & RESIZER_OUTPUT_MEMORY))
319 		return -ENODEV;
320 
321 	resizer_set_outaddr(resizer, buffer->iss_addr);
322 
323 	/*
324 	 * If streaming was enabled before there was a buffer queued
325 	 * or underrun happened in the ISR, the hardware was not enabled
326 	 * and DMA queue flag ISS_VIDEO_DMAQUEUE_UNDERRUN is still set.
327 	 * Enable it now.
328 	 */
329 	if (video->dmaqueue_flags & ISS_VIDEO_DMAQUEUE_UNDERRUN) {
330 		resizer_enable(resizer, 1);
331 		iss_video_dmaqueue_flags_clr(video);
332 	}
333 
334 	return 0;
335 }
336 
337 static const struct iss_video_operations resizer_video_ops = {
338 	.queue = resizer_video_queue,
339 };
340 
341 /* -----------------------------------------------------------------------------
342  * V4L2 subdev operations
343  */
344 
345 /*
346  * resizer_set_stream - Enable/Disable streaming on the RESIZER module
347  * @sd: ISP RESIZER V4L2 subdevice
348  * @enable: Enable/disable stream
349  */
350 static int resizer_set_stream(struct v4l2_subdev *sd, int enable)
351 {
352 	struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd);
353 	struct iss_device *iss = to_iss_device(resizer);
354 	struct iss_video *video_out = &resizer->video_out;
355 	int ret = 0;
356 
357 	if (resizer->state == ISS_PIPELINE_STREAM_STOPPED) {
358 		if (enable == ISS_PIPELINE_STREAM_STOPPED)
359 			return 0;
360 
361 		omap4iss_isp_subclk_enable(iss, OMAP4_ISS_ISP_SUBCLK_RSZ);
362 
363 		iss_reg_set(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_GCK_MMR,
364 			    RSZ_GCK_MMR_MMR);
365 		iss_reg_set(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_GCK_SDR,
366 			    RSZ_GCK_SDR_CORE);
367 
368 		/* FIXME: Enable RSZB also */
369 		iss_reg_set(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SYSCONFIG,
370 			    RSZ_SYSCONFIG_RSZA_CLK_EN);
371 	}
372 
373 	switch (enable) {
374 	case ISS_PIPELINE_STREAM_CONTINUOUS:
375 
376 		resizer_configure(resizer);
377 		resizer_print_status(resizer);
378 
379 		/*
380 		 * When outputting to memory with no buffer available, let the
381 		 * buffer queue handler start the hardware. A DMA queue flag
382 		 * ISS_VIDEO_DMAQUEUE_QUEUED will be set as soon as there is
383 		 * a buffer available.
384 		 */
385 		if (resizer->output & RESIZER_OUTPUT_MEMORY &&
386 		    !(video_out->dmaqueue_flags & ISS_VIDEO_DMAQUEUE_QUEUED))
387 			break;
388 
389 		atomic_set(&resizer->stopping, 0);
390 		resizer_enable(resizer, 1);
391 		iss_video_dmaqueue_flags_clr(video_out);
392 		break;
393 
394 	case ISS_PIPELINE_STREAM_STOPPED:
395 		if (resizer->state == ISS_PIPELINE_STREAM_STOPPED)
396 			return 0;
397 		if (omap4iss_module_sync_idle(&sd->entity, &resizer->wait,
398 					      &resizer->stopping))
399 			ret = -ETIMEDOUT;
400 
401 		resizer_enable(resizer, 0);
402 		iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SYSCONFIG,
403 			    RSZ_SYSCONFIG_RSZA_CLK_EN);
404 		iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_GCK_SDR,
405 			    RSZ_GCK_SDR_CORE);
406 		iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_GCK_MMR,
407 			    RSZ_GCK_MMR_MMR);
408 		omap4iss_isp_subclk_disable(iss, OMAP4_ISS_ISP_SUBCLK_RSZ);
409 		iss_video_dmaqueue_flags_clr(video_out);
410 		break;
411 	}
412 
413 	resizer->state = enable;
414 	return ret;
415 }
416 
417 static struct v4l2_mbus_framefmt *
418 __resizer_get_format(struct iss_resizer_device *resizer,
419 		     struct v4l2_subdev_state *sd_state, unsigned int pad,
420 		     enum v4l2_subdev_format_whence which)
421 {
422 	if (which == V4L2_SUBDEV_FORMAT_TRY)
423 		return v4l2_subdev_state_get_format(sd_state, pad);
424 	return &resizer->formats[pad];
425 }
426 
427 /*
428  * resizer_try_format - Try video format on a pad
429  * @resizer: ISS RESIZER device
430  * @sd_state: V4L2 subdev state
431  * @pad: Pad number
432  * @fmt: Format
433  */
434 static void
435 resizer_try_format(struct iss_resizer_device *resizer,
436 		   struct v4l2_subdev_state *sd_state, unsigned int pad,
437 		   struct v4l2_mbus_framefmt *fmt,
438 		   enum v4l2_subdev_format_whence which)
439 {
440 	u32 pixelcode;
441 	struct v4l2_mbus_framefmt *format;
442 	unsigned int width = fmt->width;
443 	unsigned int height = fmt->height;
444 	unsigned int i;
445 
446 	switch (pad) {
447 	case RESIZER_PAD_SINK:
448 		for (i = 0; i < ARRAY_SIZE(resizer_fmts); i++) {
449 			if (fmt->code == resizer_fmts[i])
450 				break;
451 		}
452 
453 		/* If not found, use UYVY as default */
454 		if (i >= ARRAY_SIZE(resizer_fmts))
455 			fmt->code = MEDIA_BUS_FMT_UYVY8_1X16;
456 
457 		/* Clamp the input size. */
458 		fmt->width = clamp_t(u32, width, 1, 8192);
459 		fmt->height = clamp_t(u32, height, 1, 8192);
460 		break;
461 
462 	case RESIZER_PAD_SOURCE_MEM:
463 		pixelcode = fmt->code;
464 		format = __resizer_get_format(resizer, sd_state,
465 					      RESIZER_PAD_SINK,
466 					      which);
467 		memcpy(fmt, format, sizeof(*fmt));
468 
469 		if ((pixelcode == MEDIA_BUS_FMT_YUYV8_1_5X8) &&
470 		    (fmt->code == MEDIA_BUS_FMT_UYVY8_1X16))
471 			fmt->code = pixelcode;
472 
473 		/* The data formatter truncates the number of horizontal output
474 		 * pixels to a multiple of 16. To avoid clipping data, allow
475 		 * callers to request an output size bigger than the input size
476 		 * up to the nearest multiple of 16.
477 		 */
478 		fmt->width = clamp_t(u32, width, 32, (fmt->width + 15) & ~15);
479 		fmt->width &= ~15;
480 		fmt->height = clamp_t(u32, height, 32, fmt->height);
481 		break;
482 	}
483 
484 	fmt->colorspace = V4L2_COLORSPACE_JPEG;
485 	fmt->field = V4L2_FIELD_NONE;
486 }
487 
488 /*
489  * resizer_enum_mbus_code - Handle pixel format enumeration
490  * @sd     : pointer to v4l2 subdev structure
491  * @sd_state: V4L2 subdev state
492  * @code   : pointer to v4l2_subdev_mbus_code_enum structure
493  * return -EINVAL or zero on success
494  */
495 static int resizer_enum_mbus_code(struct v4l2_subdev *sd,
496 				  struct v4l2_subdev_state *sd_state,
497 				  struct v4l2_subdev_mbus_code_enum *code)
498 {
499 	struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd);
500 	struct v4l2_mbus_framefmt *format;
501 
502 	switch (code->pad) {
503 	case RESIZER_PAD_SINK:
504 		if (code->index >= ARRAY_SIZE(resizer_fmts))
505 			return -EINVAL;
506 
507 		code->code = resizer_fmts[code->index];
508 		break;
509 
510 	case RESIZER_PAD_SOURCE_MEM:
511 		format = __resizer_get_format(resizer, sd_state,
512 					      RESIZER_PAD_SINK,
513 					      code->which);
514 
515 		if (code->index == 0) {
516 			code->code = format->code;
517 			break;
518 		}
519 
520 		switch (format->code) {
521 		case MEDIA_BUS_FMT_UYVY8_1X16:
522 			if (code->index == 1)
523 				code->code = MEDIA_BUS_FMT_YUYV8_1_5X8;
524 			else
525 				return -EINVAL;
526 			break;
527 		default:
528 			if (code->index != 0)
529 				return -EINVAL;
530 		}
531 
532 		break;
533 
534 	default:
535 		return -EINVAL;
536 	}
537 
538 	return 0;
539 }
540 
541 static int resizer_enum_frame_size(struct v4l2_subdev *sd,
542 				   struct v4l2_subdev_state *sd_state,
543 				   struct v4l2_subdev_frame_size_enum *fse)
544 {
545 	struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd);
546 	struct v4l2_mbus_framefmt format;
547 
548 	if (fse->index != 0)
549 		return -EINVAL;
550 
551 	format.code = fse->code;
552 	format.width = 1;
553 	format.height = 1;
554 	resizer_try_format(resizer, sd_state, fse->pad, &format, fse->which);
555 	fse->min_width = format.width;
556 	fse->min_height = format.height;
557 
558 	if (format.code != fse->code)
559 		return -EINVAL;
560 
561 	format.code = fse->code;
562 	format.width = -1;
563 	format.height = -1;
564 	resizer_try_format(resizer, sd_state, fse->pad, &format, fse->which);
565 	fse->max_width = format.width;
566 	fse->max_height = format.height;
567 
568 	return 0;
569 }
570 
571 /*
572  * resizer_get_format - Retrieve the video format on a pad
573  * @sd : ISP RESIZER V4L2 subdevice
574  * @sd_state: V4L2 subdev state
575  * @fmt: Format
576  *
577  * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
578  * to the format type.
579  */
580 static int resizer_get_format(struct v4l2_subdev *sd,
581 			      struct v4l2_subdev_state *sd_state,
582 			      struct v4l2_subdev_format *fmt)
583 {
584 	struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd);
585 	struct v4l2_mbus_framefmt *format;
586 
587 	format = __resizer_get_format(resizer, sd_state, fmt->pad, fmt->which);
588 	if (!format)
589 		return -EINVAL;
590 
591 	fmt->format = *format;
592 	return 0;
593 }
594 
595 /*
596  * resizer_set_format - Set the video format on a pad
597  * @sd : ISP RESIZER V4L2 subdevice
598  * @sd_state: V4L2 subdev state
599  * @fmt: Format
600  *
601  * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
602  * to the format type.
603  */
604 static int resizer_set_format(struct v4l2_subdev *sd,
605 			      struct v4l2_subdev_state *sd_state,
606 			      struct v4l2_subdev_format *fmt)
607 {
608 	struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd);
609 	struct v4l2_mbus_framefmt *format;
610 
611 	format = __resizer_get_format(resizer, sd_state, fmt->pad, fmt->which);
612 	if (!format)
613 		return -EINVAL;
614 
615 	resizer_try_format(resizer, sd_state, fmt->pad, &fmt->format,
616 			   fmt->which);
617 	*format = fmt->format;
618 
619 	/* Propagate the format from sink to source */
620 	if (fmt->pad == RESIZER_PAD_SINK) {
621 		format = __resizer_get_format(resizer, sd_state,
622 					      RESIZER_PAD_SOURCE_MEM,
623 					      fmt->which);
624 		*format = fmt->format;
625 		resizer_try_format(resizer, sd_state, RESIZER_PAD_SOURCE_MEM,
626 				   format,
627 				   fmt->which);
628 	}
629 
630 	return 0;
631 }
632 
633 static int resizer_link_validate(struct v4l2_subdev *sd,
634 				 struct media_link *link,
635 				 struct v4l2_subdev_format *source_fmt,
636 				 struct v4l2_subdev_format *sink_fmt)
637 {
638 	/* Check if the two ends match */
639 	if (source_fmt->format.width != sink_fmt->format.width ||
640 	    source_fmt->format.height != sink_fmt->format.height)
641 		return -EPIPE;
642 
643 	if (source_fmt->format.code != sink_fmt->format.code)
644 		return -EPIPE;
645 
646 	return 0;
647 }
648 
649 /*
650  * resizer_init_formats - Initialize formats on all pads
651  * @sd: ISP RESIZER V4L2 subdevice
652  * @fh: V4L2 subdev file handle
653  *
654  * Initialize all pad formats with default values. If fh is not NULL, try
655  * formats are initialized on the file handle. Otherwise active formats are
656  * initialized on the device.
657  */
658 static int resizer_init_formats(struct v4l2_subdev *sd,
659 				struct v4l2_subdev_fh *fh)
660 {
661 	struct v4l2_subdev_format format;
662 
663 	memset(&format, 0, sizeof(format));
664 	format.pad = RESIZER_PAD_SINK;
665 	format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
666 	format.format.code = MEDIA_BUS_FMT_UYVY8_1X16;
667 	format.format.width = 4096;
668 	format.format.height = 4096;
669 	resizer_set_format(sd, fh ? fh->state : NULL, &format);
670 
671 	return 0;
672 }
673 
674 /* V4L2 subdev video operations */
675 static const struct v4l2_subdev_video_ops resizer_v4l2_video_ops = {
676 	.s_stream = resizer_set_stream,
677 };
678 
679 /* V4L2 subdev pad operations */
680 static const struct v4l2_subdev_pad_ops resizer_v4l2_pad_ops = {
681 	.enum_mbus_code = resizer_enum_mbus_code,
682 	.enum_frame_size = resizer_enum_frame_size,
683 	.get_fmt = resizer_get_format,
684 	.set_fmt = resizer_set_format,
685 	.link_validate = resizer_link_validate,
686 };
687 
688 /* V4L2 subdev operations */
689 static const struct v4l2_subdev_ops resizer_v4l2_ops = {
690 	.video = &resizer_v4l2_video_ops,
691 	.pad = &resizer_v4l2_pad_ops,
692 };
693 
694 /* V4L2 subdev internal operations */
695 static const struct v4l2_subdev_internal_ops resizer_v4l2_internal_ops = {
696 	.open = resizer_init_formats,
697 };
698 
699 /* -----------------------------------------------------------------------------
700  * Media entity operations
701  */
702 
703 /*
704  * resizer_link_setup - Setup RESIZER connections
705  * @entity: RESIZER media entity
706  * @local: Pad at the local end of the link
707  * @remote: Pad at the remote end of the link
708  * @flags: Link flags
709  *
710  * return -EINVAL or zero on success
711  */
712 static int resizer_link_setup(struct media_entity *entity,
713 			      const struct media_pad *local,
714 			      const struct media_pad *remote, u32 flags)
715 {
716 	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
717 	struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd);
718 	struct iss_device *iss = to_iss_device(resizer);
719 	unsigned int index = local->index;
720 
721 	/* FIXME: this is actually a hack! */
722 	if (is_media_entity_v4l2_subdev(remote->entity))
723 		index |= 2 << 16;
724 
725 	switch (index) {
726 	case RESIZER_PAD_SINK | 2 << 16:
727 		/* Read from IPIPE or IPIPEIF. */
728 		if (!(flags & MEDIA_LNK_FL_ENABLED)) {
729 			resizer->input = RESIZER_INPUT_NONE;
730 			break;
731 		}
732 
733 		if (resizer->input != RESIZER_INPUT_NONE)
734 			return -EBUSY;
735 
736 		if (remote->entity == &iss->ipipeif.subdev.entity)
737 			resizer->input = RESIZER_INPUT_IPIPEIF;
738 		else if (remote->entity == &iss->ipipe.subdev.entity)
739 			resizer->input = RESIZER_INPUT_IPIPE;
740 
741 		break;
742 
743 	case RESIZER_PAD_SOURCE_MEM:
744 		/* Write to memory */
745 		if (flags & MEDIA_LNK_FL_ENABLED) {
746 			if (resizer->output & ~RESIZER_OUTPUT_MEMORY)
747 				return -EBUSY;
748 			resizer->output |= RESIZER_OUTPUT_MEMORY;
749 		} else {
750 			resizer->output &= ~RESIZER_OUTPUT_MEMORY;
751 		}
752 		break;
753 
754 	default:
755 		return -EINVAL;
756 	}
757 
758 	return 0;
759 }
760 
761 /* media operations */
762 static const struct media_entity_operations resizer_media_ops = {
763 	.link_setup = resizer_link_setup,
764 	.link_validate = v4l2_subdev_link_validate,
765 };
766 
767 /*
768  * resizer_init_entities - Initialize V4L2 subdev and media entity
769  * @resizer: ISS ISP RESIZER module
770  *
771  * Return 0 on success and a negative error code on failure.
772  */
773 static int resizer_init_entities(struct iss_resizer_device *resizer)
774 {
775 	struct v4l2_subdev *sd = &resizer->subdev;
776 	struct media_pad *pads = resizer->pads;
777 	struct media_entity *me = &sd->entity;
778 	int ret;
779 
780 	resizer->input = RESIZER_INPUT_NONE;
781 
782 	v4l2_subdev_init(sd, &resizer_v4l2_ops);
783 	sd->internal_ops = &resizer_v4l2_internal_ops;
784 	strscpy(sd->name, "OMAP4 ISS ISP resizer", sizeof(sd->name));
785 	sd->grp_id = BIT(16);	/* group ID for iss subdevs */
786 	v4l2_set_subdevdata(sd, resizer);
787 	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
788 
789 	pads[RESIZER_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
790 	pads[RESIZER_PAD_SOURCE_MEM].flags = MEDIA_PAD_FL_SOURCE;
791 
792 	me->ops = &resizer_media_ops;
793 	ret = media_entity_pads_init(me, RESIZER_PADS_NUM, pads);
794 	if (ret < 0)
795 		return ret;
796 
797 	resizer_init_formats(sd, NULL);
798 
799 	resizer->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
800 	resizer->video_out.ops = &resizer_video_ops;
801 	resizer->video_out.iss = to_iss_device(resizer);
802 	resizer->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 3;
803 	resizer->video_out.bpl_alignment = 32;
804 	resizer->video_out.bpl_zero_padding = 1;
805 	resizer->video_out.bpl_max = 0x1ffe0;
806 
807 	return omap4iss_video_init(&resizer->video_out, "ISP resizer a");
808 }
809 
810 void omap4iss_resizer_unregister_entities(struct iss_resizer_device *resizer)
811 {
812 	v4l2_device_unregister_subdev(&resizer->subdev);
813 	omap4iss_video_unregister(&resizer->video_out);
814 }
815 
816 int omap4iss_resizer_register_entities(struct iss_resizer_device *resizer,
817 				       struct v4l2_device *vdev)
818 {
819 	int ret;
820 
821 	/* Register the subdev and video node. */
822 	ret = v4l2_device_register_subdev(vdev, &resizer->subdev);
823 	if (ret < 0)
824 		goto error;
825 
826 	ret = omap4iss_video_register(&resizer->video_out, vdev);
827 	if (ret < 0)
828 		goto error;
829 
830 	return 0;
831 
832 error:
833 	omap4iss_resizer_unregister_entities(resizer);
834 	return ret;
835 }
836 
837 /* -----------------------------------------------------------------------------
838  * ISP RESIZER initialisation and cleanup
839  */
840 
841 /*
842  * omap4iss_resizer_init - RESIZER module initialization.
843  * @iss: Device pointer specific to the OMAP4 ISS.
844  *
845  * TODO: Get the initialisation values from platform data.
846  *
847  * Return 0 on success or a negative error code otherwise.
848  */
849 int omap4iss_resizer_init(struct iss_device *iss)
850 {
851 	struct iss_resizer_device *resizer = &iss->resizer;
852 
853 	resizer->state = ISS_PIPELINE_STREAM_STOPPED;
854 	init_waitqueue_head(&resizer->wait);
855 
856 	return resizer_init_entities(resizer);
857 }
858 
859 /*
860  * omap4iss_resizer_create_links() - RESIZER pads links creation
861  * @iss: Pointer to ISS device
862  *
863  * return negative error code or zero on success
864  */
865 int omap4iss_resizer_create_links(struct iss_device *iss)
866 {
867 	struct iss_resizer_device *resizer = &iss->resizer;
868 
869 	/* Connect the RESIZER subdev to the video node. */
870 	return media_create_pad_link(&resizer->subdev.entity,
871 				     RESIZER_PAD_SOURCE_MEM,
872 				     &resizer->video_out.video.entity, 0, 0);
873 }
874 
875 /*
876  * omap4iss_resizer_cleanup - RESIZER module cleanup.
877  * @iss: Device pointer specific to the OMAP4 ISS.
878  */
879 void omap4iss_resizer_cleanup(struct iss_device *iss)
880 {
881 	struct iss_resizer_device *resizer = &iss->resizer;
882 
883 	media_entity_cleanup(&resizer->subdev.entity);
884 }
885