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