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 
resizer_print_status(struct iss_resizer_device * resizer)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  */
resizer_enable(struct iss_resizer_device * resizer,u8 enable)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  */
resizer_set_outaddr(struct iss_resizer_device * resizer,u32 addr)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 
resizer_configure(struct iss_resizer_device * resizer)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 
resizer_isr_buffer(struct iss_resizer_device * resizer)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  */
omap4iss_resizer_isr(struct iss_resizer_device * resizer,u32 events)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 
resizer_video_queue(struct iss_video * video,struct iss_buffer * buffer)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  */
resizer_set_stream(struct v4l2_subdev * sd,int enable)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 *
__resizer_get_format(struct iss_resizer_device * resizer,struct v4l2_subdev_state * sd_state,unsigned int pad,enum v4l2_subdev_format_whence which)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_get_try_format(&resizer->subdev, sd_state,
424 						  pad);
425 	return &resizer->formats[pad];
426 }
427 
428 /*
429  * resizer_try_format - Try video format on a pad
430  * @resizer: ISS RESIZER device
431  * @cfg: V4L2 subdev pad config
432  * @pad: Pad number
433  * @fmt: Format
434  */
435 static void
resizer_try_format(struct iss_resizer_device * resizer,struct v4l2_subdev_state * sd_state,unsigned int pad,struct v4l2_mbus_framefmt * fmt,enum v4l2_subdev_format_whence which)436 resizer_try_format(struct iss_resizer_device *resizer,
437 		   struct v4l2_subdev_state *sd_state, unsigned int pad,
438 		   struct v4l2_mbus_framefmt *fmt,
439 		   enum v4l2_subdev_format_whence which)
440 {
441 	u32 pixelcode;
442 	struct v4l2_mbus_framefmt *format;
443 	unsigned int width = fmt->width;
444 	unsigned int height = fmt->height;
445 	unsigned int i;
446 
447 	switch (pad) {
448 	case RESIZER_PAD_SINK:
449 		for (i = 0; i < ARRAY_SIZE(resizer_fmts); i++) {
450 			if (fmt->code == resizer_fmts[i])
451 				break;
452 		}
453 
454 		/* If not found, use UYVY as default */
455 		if (i >= ARRAY_SIZE(resizer_fmts))
456 			fmt->code = MEDIA_BUS_FMT_UYVY8_1X16;
457 
458 		/* Clamp the input size. */
459 		fmt->width = clamp_t(u32, width, 1, 8192);
460 		fmt->height = clamp_t(u32, height, 1, 8192);
461 		break;
462 
463 	case RESIZER_PAD_SOURCE_MEM:
464 		pixelcode = fmt->code;
465 		format = __resizer_get_format(resizer, sd_state,
466 					      RESIZER_PAD_SINK,
467 					      which);
468 		memcpy(fmt, format, sizeof(*fmt));
469 
470 		if ((pixelcode == MEDIA_BUS_FMT_YUYV8_1_5X8) &&
471 		    (fmt->code == MEDIA_BUS_FMT_UYVY8_1X16))
472 			fmt->code = pixelcode;
473 
474 		/* The data formatter truncates the number of horizontal output
475 		 * pixels to a multiple of 16. To avoid clipping data, allow
476 		 * callers to request an output size bigger than the input size
477 		 * up to the nearest multiple of 16.
478 		 */
479 		fmt->width = clamp_t(u32, width, 32, (fmt->width + 15) & ~15);
480 		fmt->width &= ~15;
481 		fmt->height = clamp_t(u32, height, 32, fmt->height);
482 		break;
483 	}
484 
485 	fmt->colorspace = V4L2_COLORSPACE_JPEG;
486 	fmt->field = V4L2_FIELD_NONE;
487 }
488 
489 /*
490  * resizer_enum_mbus_code - Handle pixel format enumeration
491  * @sd     : pointer to v4l2 subdev structure
492  * @cfg: V4L2 subdev pad config
493  * @code   : pointer to v4l2_subdev_mbus_code_enum structure
494  * return -EINVAL or zero on success
495  */
resizer_enum_mbus_code(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_mbus_code_enum * code)496 static int resizer_enum_mbus_code(struct v4l2_subdev *sd,
497 				  struct v4l2_subdev_state *sd_state,
498 				  struct v4l2_subdev_mbus_code_enum *code)
499 {
500 	struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd);
501 	struct v4l2_mbus_framefmt *format;
502 
503 	switch (code->pad) {
504 	case RESIZER_PAD_SINK:
505 		if (code->index >= ARRAY_SIZE(resizer_fmts))
506 			return -EINVAL;
507 
508 		code->code = resizer_fmts[code->index];
509 		break;
510 
511 	case RESIZER_PAD_SOURCE_MEM:
512 		format = __resizer_get_format(resizer, sd_state,
513 					      RESIZER_PAD_SINK,
514 					      code->which);
515 
516 		if (code->index == 0) {
517 			code->code = format->code;
518 			break;
519 		}
520 
521 		switch (format->code) {
522 		case MEDIA_BUS_FMT_UYVY8_1X16:
523 			if (code->index == 1)
524 				code->code = MEDIA_BUS_FMT_YUYV8_1_5X8;
525 			else
526 				return -EINVAL;
527 			break;
528 		default:
529 			if (code->index != 0)
530 				return -EINVAL;
531 		}
532 
533 		break;
534 
535 	default:
536 		return -EINVAL;
537 	}
538 
539 	return 0;
540 }
541 
resizer_enum_frame_size(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_frame_size_enum * fse)542 static int resizer_enum_frame_size(struct v4l2_subdev *sd,
543 				   struct v4l2_subdev_state *sd_state,
544 				   struct v4l2_subdev_frame_size_enum *fse)
545 {
546 	struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd);
547 	struct v4l2_mbus_framefmt format;
548 
549 	if (fse->index != 0)
550 		return -EINVAL;
551 
552 	format.code = fse->code;
553 	format.width = 1;
554 	format.height = 1;
555 	resizer_try_format(resizer, sd_state, fse->pad, &format, fse->which);
556 	fse->min_width = format.width;
557 	fse->min_height = format.height;
558 
559 	if (format.code != fse->code)
560 		return -EINVAL;
561 
562 	format.code = fse->code;
563 	format.width = -1;
564 	format.height = -1;
565 	resizer_try_format(resizer, sd_state, fse->pad, &format, fse->which);
566 	fse->max_width = format.width;
567 	fse->max_height = format.height;
568 
569 	return 0;
570 }
571 
572 /*
573  * resizer_get_format - Retrieve the video format on a pad
574  * @sd : ISP RESIZER V4L2 subdevice
575  * @cfg: V4L2 subdev pad config
576  * @fmt: Format
577  *
578  * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
579  * to the format type.
580  */
resizer_get_format(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * fmt)581 static int resizer_get_format(struct v4l2_subdev *sd,
582 			      struct v4l2_subdev_state *sd_state,
583 			      struct v4l2_subdev_format *fmt)
584 {
585 	struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd);
586 	struct v4l2_mbus_framefmt *format;
587 
588 	format = __resizer_get_format(resizer, sd_state, fmt->pad, fmt->which);
589 	if (!format)
590 		return -EINVAL;
591 
592 	fmt->format = *format;
593 	return 0;
594 }
595 
596 /*
597  * resizer_set_format - Set the video format on a pad
598  * @sd : ISP RESIZER V4L2 subdevice
599  * @cfg: V4L2 subdev pad config
600  * @fmt: Format
601  *
602  * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
603  * to the format type.
604  */
resizer_set_format(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * fmt)605 static int resizer_set_format(struct v4l2_subdev *sd,
606 			      struct v4l2_subdev_state *sd_state,
607 			      struct v4l2_subdev_format *fmt)
608 {
609 	struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd);
610 	struct v4l2_mbus_framefmt *format;
611 
612 	format = __resizer_get_format(resizer, sd_state, fmt->pad, fmt->which);
613 	if (!format)
614 		return -EINVAL;
615 
616 	resizer_try_format(resizer, sd_state, fmt->pad, &fmt->format,
617 			   fmt->which);
618 	*format = fmt->format;
619 
620 	/* Propagate the format from sink to source */
621 	if (fmt->pad == RESIZER_PAD_SINK) {
622 		format = __resizer_get_format(resizer, sd_state,
623 					      RESIZER_PAD_SOURCE_MEM,
624 					      fmt->which);
625 		*format = fmt->format;
626 		resizer_try_format(resizer, sd_state, RESIZER_PAD_SOURCE_MEM,
627 				   format,
628 				   fmt->which);
629 	}
630 
631 	return 0;
632 }
633 
resizer_link_validate(struct v4l2_subdev * sd,struct media_link * link,struct v4l2_subdev_format * source_fmt,struct v4l2_subdev_format * sink_fmt)634 static int resizer_link_validate(struct v4l2_subdev *sd,
635 				 struct media_link *link,
636 				 struct v4l2_subdev_format *source_fmt,
637 				 struct v4l2_subdev_format *sink_fmt)
638 {
639 	/* Check if the two ends match */
640 	if (source_fmt->format.width != sink_fmt->format.width ||
641 	    source_fmt->format.height != sink_fmt->format.height)
642 		return -EPIPE;
643 
644 	if (source_fmt->format.code != sink_fmt->format.code)
645 		return -EPIPE;
646 
647 	return 0;
648 }
649 
650 /*
651  * resizer_init_formats - Initialize formats on all pads
652  * @sd: ISP RESIZER V4L2 subdevice
653  * @fh: V4L2 subdev file handle
654  *
655  * Initialize all pad formats with default values. If fh is not NULL, try
656  * formats are initialized on the file handle. Otherwise active formats are
657  * initialized on the device.
658  */
resizer_init_formats(struct v4l2_subdev * sd,struct v4l2_subdev_fh * fh)659 static int resizer_init_formats(struct v4l2_subdev *sd,
660 				struct v4l2_subdev_fh *fh)
661 {
662 	struct v4l2_subdev_format format;
663 
664 	memset(&format, 0, sizeof(format));
665 	format.pad = RESIZER_PAD_SINK;
666 	format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
667 	format.format.code = MEDIA_BUS_FMT_UYVY8_1X16;
668 	format.format.width = 4096;
669 	format.format.height = 4096;
670 	resizer_set_format(sd, fh ? fh->state : NULL, &format);
671 
672 	return 0;
673 }
674 
675 /* V4L2 subdev video operations */
676 static const struct v4l2_subdev_video_ops resizer_v4l2_video_ops = {
677 	.s_stream = resizer_set_stream,
678 };
679 
680 /* V4L2 subdev pad operations */
681 static const struct v4l2_subdev_pad_ops resizer_v4l2_pad_ops = {
682 	.enum_mbus_code = resizer_enum_mbus_code,
683 	.enum_frame_size = resizer_enum_frame_size,
684 	.get_fmt = resizer_get_format,
685 	.set_fmt = resizer_set_format,
686 	.link_validate = resizer_link_validate,
687 };
688 
689 /* V4L2 subdev operations */
690 static const struct v4l2_subdev_ops resizer_v4l2_ops = {
691 	.video = &resizer_v4l2_video_ops,
692 	.pad = &resizer_v4l2_pad_ops,
693 };
694 
695 /* V4L2 subdev internal operations */
696 static const struct v4l2_subdev_internal_ops resizer_v4l2_internal_ops = {
697 	.open = resizer_init_formats,
698 };
699 
700 /* -----------------------------------------------------------------------------
701  * Media entity operations
702  */
703 
704 /*
705  * resizer_link_setup - Setup RESIZER connections
706  * @entity: RESIZER media entity
707  * @local: Pad at the local end of the link
708  * @remote: Pad at the remote end of the link
709  * @flags: Link flags
710  *
711  * return -EINVAL or zero on success
712  */
resizer_link_setup(struct media_entity * entity,const struct media_pad * local,const struct media_pad * remote,u32 flags)713 static int resizer_link_setup(struct media_entity *entity,
714 			      const struct media_pad *local,
715 			      const struct media_pad *remote, u32 flags)
716 {
717 	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
718 	struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd);
719 	struct iss_device *iss = to_iss_device(resizer);
720 	unsigned int index = local->index;
721 
722 	/* FIXME: this is actually a hack! */
723 	if (is_media_entity_v4l2_subdev(remote->entity))
724 		index |= 2 << 16;
725 
726 	switch (index) {
727 	case RESIZER_PAD_SINK | 2 << 16:
728 		/* Read from IPIPE or IPIPEIF. */
729 		if (!(flags & MEDIA_LNK_FL_ENABLED)) {
730 			resizer->input = RESIZER_INPUT_NONE;
731 			break;
732 		}
733 
734 		if (resizer->input != RESIZER_INPUT_NONE)
735 			return -EBUSY;
736 
737 		if (remote->entity == &iss->ipipeif.subdev.entity)
738 			resizer->input = RESIZER_INPUT_IPIPEIF;
739 		else if (remote->entity == &iss->ipipe.subdev.entity)
740 			resizer->input = RESIZER_INPUT_IPIPE;
741 
742 		break;
743 
744 	case RESIZER_PAD_SOURCE_MEM:
745 		/* Write to memory */
746 		if (flags & MEDIA_LNK_FL_ENABLED) {
747 			if (resizer->output & ~RESIZER_OUTPUT_MEMORY)
748 				return -EBUSY;
749 			resizer->output |= RESIZER_OUTPUT_MEMORY;
750 		} else {
751 			resizer->output &= ~RESIZER_OUTPUT_MEMORY;
752 		}
753 		break;
754 
755 	default:
756 		return -EINVAL;
757 	}
758 
759 	return 0;
760 }
761 
762 /* media operations */
763 static const struct media_entity_operations resizer_media_ops = {
764 	.link_setup = resizer_link_setup,
765 	.link_validate = v4l2_subdev_link_validate,
766 };
767 
768 /*
769  * resizer_init_entities - Initialize V4L2 subdev and media entity
770  * @resizer: ISS ISP RESIZER module
771  *
772  * Return 0 on success and a negative error code on failure.
773  */
resizer_init_entities(struct iss_resizer_device * resizer)774 static int resizer_init_entities(struct iss_resizer_device *resizer)
775 {
776 	struct v4l2_subdev *sd = &resizer->subdev;
777 	struct media_pad *pads = resizer->pads;
778 	struct media_entity *me = &sd->entity;
779 	int ret;
780 
781 	resizer->input = RESIZER_INPUT_NONE;
782 
783 	v4l2_subdev_init(sd, &resizer_v4l2_ops);
784 	sd->internal_ops = &resizer_v4l2_internal_ops;
785 	strscpy(sd->name, "OMAP4 ISS ISP resizer", sizeof(sd->name));
786 	sd->grp_id = BIT(16);	/* group ID for iss subdevs */
787 	v4l2_set_subdevdata(sd, resizer);
788 	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
789 
790 	pads[RESIZER_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
791 	pads[RESIZER_PAD_SOURCE_MEM].flags = MEDIA_PAD_FL_SOURCE;
792 
793 	me->ops = &resizer_media_ops;
794 	ret = media_entity_pads_init(me, RESIZER_PADS_NUM, pads);
795 	if (ret < 0)
796 		return ret;
797 
798 	resizer_init_formats(sd, NULL);
799 
800 	resizer->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
801 	resizer->video_out.ops = &resizer_video_ops;
802 	resizer->video_out.iss = to_iss_device(resizer);
803 	resizer->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 3;
804 	resizer->video_out.bpl_alignment = 32;
805 	resizer->video_out.bpl_zero_padding = 1;
806 	resizer->video_out.bpl_max = 0x1ffe0;
807 
808 	return omap4iss_video_init(&resizer->video_out, "ISP resizer a");
809 }
810 
omap4iss_resizer_unregister_entities(struct iss_resizer_device * resizer)811 void omap4iss_resizer_unregister_entities(struct iss_resizer_device *resizer)
812 {
813 	v4l2_device_unregister_subdev(&resizer->subdev);
814 	omap4iss_video_unregister(&resizer->video_out);
815 }
816 
omap4iss_resizer_register_entities(struct iss_resizer_device * resizer,struct v4l2_device * vdev)817 int omap4iss_resizer_register_entities(struct iss_resizer_device *resizer,
818 				       struct v4l2_device *vdev)
819 {
820 	int ret;
821 
822 	/* Register the subdev and video node. */
823 	ret = v4l2_device_register_subdev(vdev, &resizer->subdev);
824 	if (ret < 0)
825 		goto error;
826 
827 	ret = omap4iss_video_register(&resizer->video_out, vdev);
828 	if (ret < 0)
829 		goto error;
830 
831 	return 0;
832 
833 error:
834 	omap4iss_resizer_unregister_entities(resizer);
835 	return ret;
836 }
837 
838 /* -----------------------------------------------------------------------------
839  * ISP RESIZER initialisation and cleanup
840  */
841 
842 /*
843  * omap4iss_resizer_init - RESIZER module initialization.
844  * @iss: Device pointer specific to the OMAP4 ISS.
845  *
846  * TODO: Get the initialisation values from platform data.
847  *
848  * Return 0 on success or a negative error code otherwise.
849  */
omap4iss_resizer_init(struct iss_device * iss)850 int omap4iss_resizer_init(struct iss_device *iss)
851 {
852 	struct iss_resizer_device *resizer = &iss->resizer;
853 
854 	resizer->state = ISS_PIPELINE_STREAM_STOPPED;
855 	init_waitqueue_head(&resizer->wait);
856 
857 	return resizer_init_entities(resizer);
858 }
859 
860 /*
861  * omap4iss_resizer_create_links() - RESIZER pads links creation
862  * @iss: Pointer to ISS device
863  *
864  * return negative error code or zero on success
865  */
omap4iss_resizer_create_links(struct iss_device * iss)866 int omap4iss_resizer_create_links(struct iss_device *iss)
867 {
868 	struct iss_resizer_device *resizer = &iss->resizer;
869 
870 	/* Connect the RESIZER subdev to the video node. */
871 	return media_create_pad_link(&resizer->subdev.entity,
872 				     RESIZER_PAD_SOURCE_MEM,
873 				     &resizer->video_out.video.entity, 0, 0);
874 }
875 
876 /*
877  * omap4iss_resizer_cleanup - RESIZER module cleanup.
878  * @iss: Device pointer specific to the OMAP4 ISS.
879  */
omap4iss_resizer_cleanup(struct iss_device * iss)880 void omap4iss_resizer_cleanup(struct iss_device *iss)
881 {
882 	struct iss_resizer_device *resizer = &iss->resizer;
883 
884 	media_entity_cleanup(&resizer->subdev.entity);
885 }
886