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
22static 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
41static 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 */
111static 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 */
134static 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
181static 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
263static 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 */
286void 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
312static 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
337static 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 */
350static 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
417static 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 */
434static void
435resizer_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 */
495static 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
541static 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 */
580static 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 */
604static 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
633static 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 */
658static 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 */
675static const struct v4l2_subdev_video_ops resizer_v4l2_video_ops = {
676	.s_stream = resizer_set_stream,
677};
678
679/* V4L2 subdev pad operations */
680static 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 */
689static 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 */
695static 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 */
712static 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 */
762static 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 */
773static 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
810void 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
816int 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
832error:
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 */
849int 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 */
865int 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 */
879void 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