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