aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLiu Ying <Ying.Liu@freescale.com>2013-08-05 03:47:22 -0400
committerNitin Garg <nitin.garg@freescale.com>2014-04-16 09:01:04 -0400
commit57069a5dd4f48dbf88caf9c959a4353db5301032 (patch)
tree268447a1bb3ad843a43bc88d72084057b4fde8fa
parent7cf40c09dd83a769823e1339a83148fe8efdcb25 (diff)
ENGR00273974-1 media: port mxc vout driver from 3.5.7 to 3.10
This is porting mxc vout driver from imx_3.5.7 kernel to imx_3.10 kernel. * Put the driver in drivers/media/platform/ directory instead of drivers/media/video/ directory, since the later one is renamed to the former one in 3.10 kernel. * Change the ipu-v3.h header file from <mach/ipu-v3.h> to <linux/ipu-v3.h>. * Change the mxc_vidioc_s_crop() function's implementation since the definition of the vidioc_s_crop() function is modified to make the last argument be constant. * Set vfl_dir to be VFL_DIR_TX since the ioctrl validity checks are improved in 3.10 kernel. * Remove 'defaut y' setting for VIDEO_MXC_OUTPUT and VIDEO_MXC_IPU_OUTPUT Kconfigs. They may be configured by kernel default configure or by user. * Make VIDEO_MXC_OUTPUT Kconfig depend on FB_MXC Kconfig since we need framebuffers to be rendered. * Make VIDEO_MXC_IPU_OUTPUT Kconfig be tristate. * Split <linux/mxc_v4l2.h> header file up into include/linux/ and include/uapi/linux/ directories so that the userspace may include the mxc_v4l2.h header file. Signed-off-by: Liu Ying <Ying.Liu@freescale.com>
-rw-r--r--drivers/media/platform/Kconfig8
-rw-r--r--drivers/media/platform/Makefile2
-rw-r--r--drivers/media/platform/mxc/output/Kconfig5
-rw-r--r--drivers/media/platform/mxc/output/Makefile1
-rw-r--r--drivers/media/platform/mxc/output/mxc_vout.c2189
-rw-r--r--include/linux/mxc_v4l2.h27
-rw-r--r--include/uapi/linux/Kbuild1
-rw-r--r--include/uapi/linux/mxc_v4l2.h56
8 files changed, 2289 insertions, 0 deletions
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 0494d2769fd7..1698a4e0cf74 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -121,6 +121,14 @@ config VIDEO_S3C_CAMIF
121 To compile this driver as a module, choose M here: the module 121 To compile this driver as a module, choose M here: the module
122 will be called s3c-camif. 122 will be called s3c-camif.
123 123
124config VIDEO_MXC_OUTPUT
125 tristate "MXC Video For Linux Video Output"
126 depends on VIDEO_DEV && ARCH_MXC && FB_MXC
127 select VIDEOBUF_DMA_CONTIG
128 ---help---
129 This is the video4linux2 output driver based on MXC module.
130
131source "drivers/media/platform/mxc/output/Kconfig"
124source "drivers/media/platform/soc_camera/Kconfig" 132source "drivers/media/platform/soc_camera/Kconfig"
125source "drivers/media/platform/exynos4-is/Kconfig" 133source "drivers/media/platform/exynos4-is/Kconfig"
126source "drivers/media/platform/s5p-tv/Kconfig" 134source "drivers/media/platform/s5p-tv/Kconfig"
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index eee28dd78d7d..43b85c3828cc 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -50,4 +50,6 @@ obj-y += davinci/
50 50
51obj-$(CONFIG_ARCH_OMAP) += omap/ 51obj-$(CONFIG_ARCH_OMAP) += omap/
52 52
53obj-$(CONFIG_VIDEO_MXC_OUTPUT) += mxc/output/
54
53ccflags-y += -I$(srctree)/drivers/media/i2c 55ccflags-y += -I$(srctree)/drivers/media/i2c
diff --git a/drivers/media/platform/mxc/output/Kconfig b/drivers/media/platform/mxc/output/Kconfig
new file mode 100644
index 000000000000..b684060abc18
--- /dev/null
+++ b/drivers/media/platform/mxc/output/Kconfig
@@ -0,0 +1,5 @@
1config VIDEO_MXC_IPU_OUTPUT
2 tristate "IPU v4l2 output support"
3 depends on VIDEO_MXC_OUTPUT && MXC_IPU
4 ---help---
5 This is the video4linux2 driver for IPU post processing video output.
diff --git a/drivers/media/platform/mxc/output/Makefile b/drivers/media/platform/mxc/output/Makefile
new file mode 100644
index 000000000000..7b524fe8e616
--- /dev/null
+++ b/drivers/media/platform/mxc/output/Makefile
@@ -0,0 +1 @@
obj-$(CONFIG_VIDEO_MXC_IPU_OUTPUT) += mxc_vout.o
diff --git a/drivers/media/platform/mxc/output/mxc_vout.c b/drivers/media/platform/mxc/output/mxc_vout.c
new file mode 100644
index 000000000000..0dfe387aab2c
--- /dev/null
+++ b/drivers/media/platform/mxc/output/mxc_vout.c
@@ -0,0 +1,2189 @@
1/*
2 * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved.
3 */
4
5/*
6 * The code contained herein is licensed under the GNU General Public
7 * License. You may obtain a copy of the GNU General Public License
8 * Version 2 or later at the following locations:
9 *
10 * http://www.opensource.org/licenses/gpl-license.html
11 * http://www.gnu.org/copyleft/gpl.html
12 */
13
14#include <linux/console.h>
15#include <linux/dma-mapping.h>
16#include <linux/init.h>
17#include <linux/ipu-v3.h>
18#include <linux/module.h>
19#include <linux/mxcfb.h>
20#include <linux/mxc_v4l2.h>
21#include <linux/platform_device.h>
22#include <linux/sched.h>
23#include <linux/types.h>
24#include <linux/videodev2.h>
25#include <linux/vmalloc.h>
26
27#include <media/videobuf-dma-contig.h>
28#include <media/v4l2-device.h>
29#include <media/v4l2-ioctl.h>
30
31#define UYVY_BLACK (0x00800080)
32#define RGB_BLACK (0x0)
33#define UV_BLACK (0x80)
34#define Y_BLACK (0x0)
35
36#define MAX_FB_NUM 6
37#define FB_BUFS 3
38#define VDOA_FB_BUFS (FB_BUFS - 1)
39#define VALID_HEIGHT_1080P (1080)
40#define FRAME_HEIGHT_1080P (1088)
41#define FRAME_WIDTH_1080P (1920)
42#define CHECK_TILED_1080P_DISPLAY(vout) \
43 ((((vout)->task.input.format == IPU_PIX_FMT_TILED_NV12) || \
44 ((vout)->task.input.format == IPU_PIX_FMT_TILED_NV12F)) &&\
45 ((vout)->task.input.width == FRAME_WIDTH_1080P) && \
46 ((vout)->task.input.height == FRAME_HEIGHT_1080P) && \
47 ((vout)->task.input.crop.w == FRAME_WIDTH_1080P) && \
48 (((vout)->task.input.crop.h == FRAME_HEIGHT_1080P) || \
49 ((vout)->task.input.crop.h == VALID_HEIGHT_1080P)) && \
50 ((vout)->task.output.width == FRAME_WIDTH_1080P) && \
51 ((vout)->task.output.height == VALID_HEIGHT_1080P) && \
52 ((vout)->task.output.crop.w == FRAME_WIDTH_1080P) && \
53 ((vout)->task.output.crop.h == VALID_HEIGHT_1080P))
54#define CHECK_TILED_1080P_STREAM(vout) \
55 ((((vout)->task.input.format == IPU_PIX_FMT_TILED_NV12) || \
56 ((vout)->task.input.format == IPU_PIX_FMT_TILED_NV12F)) &&\
57 ((vout)->task.input.width == FRAME_WIDTH_1080P) && \
58 ((vout)->task.input.crop.w == FRAME_WIDTH_1080P) && \
59 ((vout)->task.input.height == FRAME_HEIGHT_1080P) && \
60 ((vout)->task.input.crop.h == FRAME_HEIGHT_1080P))
61#define IS_PLANAR_PIXEL_FORMAT(format) \
62 (format == IPU_PIX_FMT_NV12 || \
63 format == IPU_PIX_FMT_YUV420P2 || \
64 format == IPU_PIX_FMT_YUV420P || \
65 format == IPU_PIX_FMT_YVU420P || \
66 format == IPU_PIX_FMT_YUV422P || \
67 format == IPU_PIX_FMT_YVU422P || \
68 format == IPU_PIX_FMT_YUV444P)
69
70#define NSEC_PER_FRAME_30FPS (33333333)
71
72struct mxc_vout_fb {
73 char *name;
74 int ipu_id;
75 struct v4l2_rect crop_bounds;
76 unsigned int disp_fmt;
77 bool disp_support_csc;
78 bool disp_support_windows;
79};
80
81struct dma_mem {
82 void *vaddr;
83 dma_addr_t paddr;
84 size_t size;
85};
86
87struct mxc_vout_output {
88 int open_cnt;
89 struct fb_info *fbi;
90 unsigned long fb_smem_start;
91 unsigned long fb_smem_len;
92 struct video_device *vfd;
93 struct mutex mutex;
94 struct mutex task_lock;
95 enum v4l2_buf_type type;
96
97 struct videobuf_queue vbq;
98 spinlock_t vbq_lock;
99
100 struct list_head queue_list;
101 struct list_head active_list;
102
103 struct v4l2_rect crop_bounds;
104 unsigned int disp_fmt;
105 struct mxcfb_pos win_pos;
106 bool disp_support_windows;
107 bool disp_support_csc;
108
109 bool fmt_init;
110 bool release;
111 bool linear_bypass_pp;
112 bool vdoa_1080p;
113 bool tiled_bypass_pp;
114 struct v4l2_rect in_rect;
115 struct ipu_task task;
116 struct ipu_task vdoa_task;
117 struct dma_mem vdoa_work;
118 struct dma_mem vdoa_output[VDOA_FB_BUFS];
119
120 bool timer_stop;
121 struct hrtimer timer;
122 struct workqueue_struct *v4l_wq;
123 struct work_struct disp_work;
124 unsigned long frame_count;
125 unsigned long vdi_frame_cnt;
126 ktime_t start_ktime;
127
128 int ctrl_rotate;
129 int ctrl_vflip;
130 int ctrl_hflip;
131
132 dma_addr_t disp_bufs[FB_BUFS];
133
134 struct videobuf_buffer *pre1_vb;
135 struct videobuf_buffer *pre2_vb;
136};
137
138struct mxc_vout_dev {
139 struct device *dev;
140 struct v4l2_device v4l2_dev;
141 struct mxc_vout_output *out[MAX_FB_NUM];
142 int out_num;
143};
144
145/* Driver Configuration macros */
146#define VOUT_NAME "mxc_vout"
147
148/* Variables configurable through module params*/
149static int debug;
150static int vdi_rate_double;
151static int video_nr = 16;
152
153/* Module parameters */
154module_param(video_nr, int, S_IRUGO);
155MODULE_PARM_DESC(video_nr, "video device numbers");
156module_param(debug, int, 0600);
157MODULE_PARM_DESC(debug, "Debug level (0-1)");
158module_param(vdi_rate_double, int, 0600);
159MODULE_PARM_DESC(vdi_rate_double, "vdi frame rate double on/off");
160
161static const struct v4l2_fmtdesc mxc_formats[] = {
162 {
163 .description = "RGB565",
164 .pixelformat = V4L2_PIX_FMT_RGB565,
165 },
166 {
167 .description = "BGR24",
168 .pixelformat = V4L2_PIX_FMT_BGR24,
169 },
170 {
171 .description = "RGB24",
172 .pixelformat = V4L2_PIX_FMT_RGB24,
173 },
174 {
175 .description = "RGB32",
176 .pixelformat = V4L2_PIX_FMT_RGB32,
177 },
178 {
179 .description = "BGR32",
180 .pixelformat = V4L2_PIX_FMT_BGR32,
181 },
182 {
183 .description = "NV12",
184 .pixelformat = V4L2_PIX_FMT_NV12,
185 },
186 {
187 .description = "UYVY",
188 .pixelformat = V4L2_PIX_FMT_UYVY,
189 },
190 {
191 .description = "YUYV",
192 .pixelformat = V4L2_PIX_FMT_YUYV,
193 },
194 {
195 .description = "YUV422 planar",
196 .pixelformat = V4L2_PIX_FMT_YUV422P,
197 },
198 {
199 .description = "YUV444",
200 .pixelformat = V4L2_PIX_FMT_YUV444,
201 },
202 {
203 .description = "YUV420",
204 .pixelformat = V4L2_PIX_FMT_YUV420,
205 },
206 {
207 .description = "YVU420",
208 .pixelformat = V4L2_PIX_FMT_YVU420,
209 },
210 {
211 .description = "TILED NV12P",
212 .pixelformat = IPU_PIX_FMT_TILED_NV12,
213 },
214 {
215 .description = "TILED NV12F",
216 .pixelformat = IPU_PIX_FMT_TILED_NV12F,
217 },
218 {
219 .description = "YUV444 planar",
220 .pixelformat = IPU_PIX_FMT_YUV444P,
221 },
222};
223
224#define NUM_MXC_VOUT_FORMATS (ARRAY_SIZE(mxc_formats))
225
226#define DEF_INPUT_WIDTH 320
227#define DEF_INPUT_HEIGHT 240
228
229static int mxc_vidioc_streamoff(struct file *file, void *fh,
230 enum v4l2_buf_type i);
231
232static struct mxc_vout_fb g_fb_setting[MAX_FB_NUM];
233static int config_disp_output(struct mxc_vout_output *vout);
234static void release_disp_output(struct mxc_vout_output *vout);
235
236static unsigned int get_frame_size(struct mxc_vout_output *vout)
237{
238 unsigned int size;
239
240 if (IPU_PIX_FMT_TILED_NV12 == vout->task.input.format)
241 size = TILED_NV12_FRAME_SIZE(vout->task.input.width,
242 vout->task.input.height);
243 else if (IPU_PIX_FMT_TILED_NV12F == vout->task.input.format) {
244 size = TILED_NV12_FRAME_SIZE(vout->task.input.width,
245 vout->task.input.height/2);
246 size *= 2;
247 } else
248 size = vout->task.input.width * vout->task.input.height *
249 fmt_to_bpp(vout->task.input.format)/8;
250
251 return size;
252}
253
254static void free_dma_buf(struct mxc_vout_output *vout, struct dma_mem *buf)
255{
256 dma_free_coherent(vout->vbq.dev, buf->size, buf->vaddr, buf->paddr);
257 v4l2_dbg(1, debug, vout->vfd->v4l2_dev,
258 "free dma size:0x%x, paddr:0x%x\n",
259 buf->size, buf->paddr);
260 memset(buf, 0, sizeof(*buf));
261}
262
263static int alloc_dma_buf(struct mxc_vout_output *vout, struct dma_mem *buf)
264{
265
266 buf->vaddr = dma_alloc_coherent(vout->vbq.dev, buf->size, &buf->paddr,
267 GFP_DMA | GFP_KERNEL);
268 if (!buf->vaddr) {
269 v4l2_err(vout->vfd->v4l2_dev,
270 "cannot get dma buf size:0x%x\n", buf->size);
271 return -ENOMEM;
272 }
273 v4l2_dbg(1, debug, vout->vfd->v4l2_dev,
274 "alloc dma buf size:0x%x, paddr:0x%x\n", buf->size, buf->paddr);
275 return 0;
276}
277
278static ipu_channel_t get_ipu_channel(struct fb_info *fbi)
279{
280 ipu_channel_t ipu_ch = CHAN_NONE;
281 mm_segment_t old_fs;
282
283 if (fbi->fbops->fb_ioctl) {
284 old_fs = get_fs();
285 set_fs(KERNEL_DS);
286 fbi->fbops->fb_ioctl(fbi, MXCFB_GET_FB_IPU_CHAN,
287 (unsigned long)&ipu_ch);
288 set_fs(old_fs);
289 }
290
291 return ipu_ch;
292}
293
294static unsigned int get_ipu_fmt(struct fb_info *fbi)
295{
296 mm_segment_t old_fs;
297 unsigned int fb_fmt;
298
299 if (fbi->fbops->fb_ioctl) {
300 old_fs = get_fs();
301 set_fs(KERNEL_DS);
302 fbi->fbops->fb_ioctl(fbi, MXCFB_GET_DIFMT,
303 (unsigned long)&fb_fmt);
304 set_fs(old_fs);
305 }
306
307 return fb_fmt;
308}
309
310static void update_display_setting(void)
311{
312 int i;
313 struct fb_info *fbi;
314 struct v4l2_rect bg_crop_bounds[2];
315
316 for (i = 0; i < num_registered_fb; i++) {
317 fbi = registered_fb[i];
318
319 memset(&g_fb_setting[i], 0, sizeof(struct mxc_vout_fb));
320
321 if (!strncmp(fbi->fix.id, "DISP3", 5))
322 g_fb_setting[i].ipu_id = 0;
323 else
324 g_fb_setting[i].ipu_id = 1;
325
326 g_fb_setting[i].name = fbi->fix.id;
327 g_fb_setting[i].crop_bounds.left = 0;
328 g_fb_setting[i].crop_bounds.top = 0;
329 g_fb_setting[i].crop_bounds.width = fbi->var.xres;
330 g_fb_setting[i].crop_bounds.height = fbi->var.yres;
331 g_fb_setting[i].disp_fmt = get_ipu_fmt(fbi);
332
333 if (get_ipu_channel(fbi) == MEM_BG_SYNC) {
334 bg_crop_bounds[g_fb_setting[i].ipu_id] =
335 g_fb_setting[i].crop_bounds;
336 g_fb_setting[i].disp_support_csc = true;
337 } else if (get_ipu_channel(fbi) == MEM_FG_SYNC) {
338 g_fb_setting[i].disp_support_csc = true;
339 g_fb_setting[i].disp_support_windows = true;
340 }
341 }
342
343 for (i = 0; i < num_registered_fb; i++) {
344 fbi = registered_fb[i];
345
346 if (get_ipu_channel(fbi) == MEM_FG_SYNC)
347 g_fb_setting[i].crop_bounds =
348 bg_crop_bounds[g_fb_setting[i].ipu_id];
349 }
350}
351
352/* called after g_fb_setting filled by update_display_setting */
353static int update_setting_from_fbi(struct mxc_vout_output *vout,
354 struct fb_info *fbi)
355{
356 int i;
357 bool found = false;
358
359 for (i = 0; i < MAX_FB_NUM; i++) {
360 if (g_fb_setting[i].name) {
361 if (!strcmp(fbi->fix.id, g_fb_setting[i].name)) {
362 vout->crop_bounds = g_fb_setting[i].crop_bounds;
363 vout->disp_fmt = g_fb_setting[i].disp_fmt;
364 vout->disp_support_csc =
365 g_fb_setting[i].disp_support_csc;
366 vout->disp_support_windows =
367 g_fb_setting[i].disp_support_windows;
368 found = true;
369 break;
370 }
371 }
372 }
373
374 if (!found) {
375 v4l2_err(vout->vfd->v4l2_dev, "can not find output\n");
376 return -EINVAL;
377 }
378 strlcpy(vout->vfd->name, fbi->fix.id, sizeof(vout->vfd->name));
379
380 memset(&vout->task, 0, sizeof(struct ipu_task));
381
382 vout->task.input.width = DEF_INPUT_WIDTH;
383 vout->task.input.height = DEF_INPUT_HEIGHT;
384 vout->task.input.crop.pos.x = 0;
385 vout->task.input.crop.pos.y = 0;
386 vout->task.input.crop.w = DEF_INPUT_WIDTH;
387 vout->task.input.crop.h = DEF_INPUT_HEIGHT;
388
389 vout->task.output.width = vout->crop_bounds.width;
390 vout->task.output.height = vout->crop_bounds.height;
391 vout->task.output.crop.pos.x = 0;
392 vout->task.output.crop.pos.y = 0;
393 vout->task.output.crop.w = vout->crop_bounds.width;
394 vout->task.output.crop.h = vout->crop_bounds.height;
395 if (colorspaceofpixel(vout->disp_fmt) == YUV_CS)
396 vout->task.output.format = IPU_PIX_FMT_UYVY;
397 else
398 vout->task.output.format = IPU_PIX_FMT_RGB565;
399
400 return 0;
401}
402
403static inline unsigned long get_jiffies(struct timeval *t)
404{
405 struct timeval cur;
406
407 if (t->tv_usec >= 1000000) {
408 t->tv_sec += t->tv_usec / 1000000;
409 t->tv_usec = t->tv_usec % 1000000;
410 }
411
412 do_gettimeofday(&cur);
413 if ((t->tv_sec < cur.tv_sec)
414 || ((t->tv_sec == cur.tv_sec) && (t->tv_usec < cur.tv_usec)))
415 return jiffies;
416
417 if (t->tv_usec < cur.tv_usec) {
418 cur.tv_sec = t->tv_sec - cur.tv_sec - 1;
419 cur.tv_usec = t->tv_usec + 1000000 - cur.tv_usec;
420 } else {
421 cur.tv_sec = t->tv_sec - cur.tv_sec;
422 cur.tv_usec = t->tv_usec - cur.tv_usec;
423 }
424
425 return jiffies + timeval_to_jiffies(&cur);
426}
427
428static bool deinterlace_3_field(struct mxc_vout_output *vout)
429{
430 return (vout->task.input.deinterlace.enable &&
431 (vout->task.input.deinterlace.motion != HIGH_MOTION));
432}
433
434static int set_field_fmt(struct mxc_vout_output *vout, enum v4l2_field field)
435{
436 struct ipu_deinterlace *deinterlace = &vout->task.input.deinterlace;
437
438 switch (field) {
439 /* Images are in progressive format, not interlaced */
440 case V4L2_FIELD_NONE:
441 case V4L2_FIELD_ANY:
442 deinterlace->enable = false;
443 deinterlace->field_fmt = 0;
444 v4l2_dbg(1, debug, vout->vfd->v4l2_dev, "Progressive frame.\n");
445 break;
446 case V4L2_FIELD_INTERLACED_TB:
447 v4l2_dbg(1, debug, vout->vfd->v4l2_dev,
448 "Enable deinterlace TB.\n");
449 deinterlace->enable = true;
450 deinterlace->field_fmt = IPU_DEINTERLACE_FIELD_TOP;
451 break;
452 case V4L2_FIELD_INTERLACED_BT:
453 v4l2_dbg(1, debug, vout->vfd->v4l2_dev,
454 "Enable deinterlace BT.\n");
455 deinterlace->enable = true;
456 deinterlace->field_fmt = IPU_DEINTERLACE_FIELD_BOTTOM;
457 break;
458 default:
459 v4l2_err(vout->vfd->v4l2_dev,
460 "field format:%d not supported yet!\n", field);
461 return -EINVAL;
462 }
463
464 if (IPU_PIX_FMT_TILED_NV12F == vout->task.input.format) {
465 v4l2_dbg(1, debug, vout->vfd->v4l2_dev,
466 "tiled fmt enable deinterlace.\n");
467 deinterlace->enable = true;
468 }
469
470 if (deinterlace->enable && vdi_rate_double)
471 deinterlace->field_fmt |= IPU_DEINTERLACE_RATE_EN;
472
473 return 0;
474}
475
476static bool is_pp_bypass(struct mxc_vout_output *vout)
477{
478 if ((IPU_PIX_FMT_TILED_NV12 == vout->task.input.format) ||
479 (IPU_PIX_FMT_TILED_NV12F == vout->task.input.format))
480 return false;
481 if ((vout->task.input.width == vout->task.output.width) &&
482 (vout->task.input.height == vout->task.output.height) &&
483 (vout->task.input.crop.w == vout->task.output.crop.w) &&
484 (vout->task.input.crop.h == vout->task.output.crop.h) &&
485 (vout->task.output.rotate < IPU_ROTATE_HORIZ_FLIP) &&
486 !vout->task.input.deinterlace.enable) {
487 if (vout->disp_support_csc)
488 return true;
489 else if (!need_csc(vout->task.input.format, vout->disp_fmt))
490 return true;
491 /*
492 * input crop show to full output which can show based on
493 * xres_virtual/yres_virtual
494 */
495 } else if ((vout->task.input.crop.w == vout->task.output.crop.w) &&
496 (vout->task.output.crop.w == vout->task.output.width) &&
497 (vout->task.input.crop.h == vout->task.output.crop.h) &&
498 (vout->task.output.crop.h ==
499 vout->task.output.height) &&
500 (vout->task.output.rotate < IPU_ROTATE_HORIZ_FLIP) &&
501 !vout->task.input.deinterlace.enable) {
502 if (vout->disp_support_csc)
503 return true;
504 else if (!need_csc(vout->task.input.format, vout->disp_fmt))
505 return true;
506 }
507 return false;
508}
509
510static void setup_buf_timer(struct mxc_vout_output *vout,
511 struct videobuf_buffer *vb)
512{
513 ktime_t expiry_time, now;
514
515 /* if timestamp is 0, then default to 30fps */
516 if ((vb->ts.tv_sec == 0) && (vb->ts.tv_usec == 0))
517 expiry_time = ktime_add_ns(vout->start_ktime,
518 NSEC_PER_FRAME_30FPS * vout->frame_count);
519 else
520 expiry_time = timeval_to_ktime(vb->ts);
521
522 now = hrtimer_cb_get_time(&vout->timer);
523 if ((now.tv64 > expiry_time.tv64)) {
524 v4l2_dbg(1, debug, vout->vfd->v4l2_dev,
525 "warning: timer timeout already expired.\n");
526 expiry_time = now;
527 }
528
529 hrtimer_start(&vout->timer, expiry_time, HRTIMER_MODE_ABS);
530
531 v4l2_dbg(1, debug, vout->vfd->v4l2_dev, "timer handler next "
532 "schedule: %lldnsecs\n", expiry_time.tv64);
533}
534
535static int show_buf(struct mxc_vout_output *vout, int idx,
536 struct ipu_pos *ipos)
537{
538 struct fb_info *fbi = vout->fbi;
539 struct fb_var_screeninfo var;
540 int ret;
541 u32 fb_base = 0;
542
543 memcpy(&var, &fbi->var, sizeof(var));
544
545 if (vout->linear_bypass_pp || vout->tiled_bypass_pp) {
546 /*
547 * crack fb base
548 * NOTE: should not do other fb operation during v4l2
549 */
550 console_lock();
551 fb_base = fbi->fix.smem_start;
552 fbi->fix.smem_start = vout->task.output.paddr;
553 fbi->var.yoffset = ipos->y + 1;
554 var.xoffset = ipos->x;
555 var.yoffset = ipos->y;
556 var.vmode |= FB_VMODE_YWRAP;
557 ret = fb_pan_display(fbi, &var);
558 fbi->fix.smem_start = fb_base;
559 console_unlock();
560 } else {
561 console_lock();
562 var.yoffset = idx * fbi->var.yres;
563 var.vmode &= ~FB_VMODE_YWRAP;
564 ret = fb_pan_display(fbi, &var);
565 console_unlock();
566 }
567
568 return ret;
569}
570
571static void disp_work_func(struct work_struct *work)
572{
573 struct mxc_vout_output *vout =
574 container_of(work, struct mxc_vout_output, disp_work);
575 struct videobuf_queue *q = &vout->vbq;
576 struct videobuf_buffer *vb, *vb_next = NULL;
577 unsigned long flags = 0;
578 struct ipu_pos ipos;
579 int ret = 0;
580 u32 in_fmt = 0;
581 u32 vdi_cnt = 0;
582 u32 vdi_frame;
583 u32 index = 0;
584 u32 ocrop_h = 0;
585 u32 o_height = 0;
586 u32 tiled_interlaced = 0;
587 bool tiled_fmt = false;
588
589 v4l2_dbg(1, debug, vout->vfd->v4l2_dev, "disp work begin one frame\n");
590
591 spin_lock_irqsave(q->irqlock, flags);
592
593 if (list_empty(&vout->active_list)) {
594 v4l2_warn(vout->vfd->v4l2_dev,
595 "no entry in active_list, should not be here\n");
596 spin_unlock_irqrestore(q->irqlock, flags);
597 return;
598 }
599
600 vb = list_first_entry(&vout->active_list,
601 struct videobuf_buffer, queue);
602 ret = set_field_fmt(vout, vb->field);
603 if (ret < 0) {
604 spin_unlock_irqrestore(q->irqlock, flags);
605 return;
606 }
607 if (deinterlace_3_field(vout)) {
608 if (list_is_singular(&vout->active_list)) {
609 if (list_empty(&vout->queue_list)) {
610 vout->timer_stop = true;
611 spin_unlock_irqrestore(q->irqlock, flags);
612 v4l2_warn(vout->vfd->v4l2_dev,
613 "no enough entry for 3 fields "
614 "deinterlacer\n");
615 return;
616 }
617
618 /*
619 * We need to use the next vb even if it is
620 * not on the active list.
621 */
622 vb_next = list_first_entry(&vout->queue_list,
623 struct videobuf_buffer, queue);
624 } else
625 vb_next = list_first_entry(vout->active_list.next,
626 struct videobuf_buffer, queue);
627 v4l2_dbg(1, debug, vout->vfd->v4l2_dev,
628 "cur field_fmt:%d, next field_fmt:%d.\n",
629 vb->field, vb_next->field);
630 /* repeat the last field during field format changing */
631 if ((vb->field != vb_next->field) &&
632 (vb_next->field != V4L2_FIELD_NONE))
633 vb_next = vb;
634 }
635
636 spin_unlock_irqrestore(q->irqlock, flags);
637
638vdi_frame_rate_double:
639 mutex_lock(&vout->task_lock);
640
641 v4l2_dbg(1, debug, vout->vfd->v4l2_dev,
642 "v4l2 frame_cnt:%ld, vb_field:%d, fmt:%d\n",
643 vout->frame_count, vb->field,
644 vout->task.input.deinterlace.field_fmt);
645 if (vb->memory == V4L2_MEMORY_USERPTR)
646 vout->task.input.paddr = vb->baddr;
647 else
648 vout->task.input.paddr = videobuf_to_dma_contig(vb);
649
650 if (vout->task.input.deinterlace.field_fmt & IPU_DEINTERLACE_RATE_EN)
651 index = vout->vdi_frame_cnt % FB_BUFS;
652 else
653 index = vout->frame_count % FB_BUFS;
654 if (vout->linear_bypass_pp) {
655 vout->task.output.paddr = vout->task.input.paddr;
656 ipos.x = vout->task.input.crop.pos.x;
657 ipos.y = vout->task.input.crop.pos.y;
658 } else {
659 if (deinterlace_3_field(vout)) {
660 if (vb->memory == V4L2_MEMORY_USERPTR)
661 vout->task.input.paddr_n = vb_next->baddr;
662 else
663 vout->task.input.paddr_n =
664 videobuf_to_dma_contig(vb_next);
665 }
666 vout->task.output.paddr = vout->disp_bufs[index];
667 if (vout->vdoa_1080p) {
668 o_height = vout->task.output.height;
669 ocrop_h = vout->task.output.crop.h;
670 vout->task.output.height = FRAME_HEIGHT_1080P;
671 vout->task.output.crop.h = FRAME_HEIGHT_1080P;
672 }
673 tiled_fmt =
674 (IPU_PIX_FMT_TILED_NV12 == vout->task.input.format) ||
675 (IPU_PIX_FMT_TILED_NV12F == vout->task.input.format);
676 if (vout->tiled_bypass_pp) {
677 ipos.x = vout->task.input.crop.pos.x;
678 ipos.y = vout->task.input.crop.pos.y;
679 } else if (tiled_fmt) {
680 vout->vdoa_task.input.paddr = vout->task.input.paddr;
681 if (deinterlace_3_field(vout))
682 vout->vdoa_task.input.paddr_n =
683 vout->task.input.paddr_n;
684 vout->vdoa_task.output.paddr = vout->vdoa_work.paddr;
685 ret = ipu_queue_task(&vout->vdoa_task);
686 if (ret < 0) {
687 mutex_unlock(&vout->task_lock);
688 goto err;
689 }
690 vout->task.input.paddr = vout->vdoa_task.output.paddr;
691 in_fmt = vout->task.input.format;
692 vout->task.input.format = vout->vdoa_task.output.format;
693 if (vout->task.input.deinterlace.enable) {
694 tiled_interlaced = 1;
695 vout->task.input.deinterlace.enable = 0;
696 }
697 v4l2_dbg(1, debug, vout->vfd->v4l2_dev,
698 "tiled queue task\n");
699 }
700 ret = ipu_queue_task(&vout->task);
701 if ((!vout->tiled_bypass_pp) && tiled_fmt)
702 vout->task.input.format = in_fmt;
703 if (tiled_interlaced)
704 vout->task.input.deinterlace.enable = 1;
705 if (ret < 0) {
706 mutex_unlock(&vout->task_lock);
707 goto err;
708 }
709 if (vout->vdoa_1080p) {
710 vout->task.output.crop.h = ocrop_h;
711 vout->task.output.height = o_height;
712 }
713 }
714
715 mutex_unlock(&vout->task_lock);
716
717 ret = show_buf(vout, index, &ipos);
718 if (ret < 0)
719 v4l2_dbg(1, debug, vout->vfd->v4l2_dev,
720 "show buf with ret %d\n", ret);
721
722 if (vout->task.input.deinterlace.field_fmt & IPU_DEINTERLACE_RATE_EN) {
723 vdi_frame = vout->task.input.deinterlace.field_fmt
724 & IPU_DEINTERLACE_RATE_FRAME1;
725 if (vdi_frame)
726 vout->task.input.deinterlace.field_fmt &=
727 ~IPU_DEINTERLACE_RATE_FRAME1;
728 else
729 vout->task.input.deinterlace.field_fmt |=
730 IPU_DEINTERLACE_RATE_FRAME1;
731 vout->vdi_frame_cnt++;
732 vdi_cnt++;
733 if (vdi_cnt < IPU_DEINTERLACE_MAX_FRAME)
734 goto vdi_frame_rate_double;
735 }
736 spin_lock_irqsave(q->irqlock, flags);
737
738 list_del(&vb->queue);
739
740 /*
741 * The videobuf before the last one has been shown. Set
742 * VIDEOBUF_DONE state here to avoid tearing issue in ic bypass
743 * case, which makes sure a buffer being shown will not be
744 * dequeued to be overwritten. It also brings side-effect that
745 * the last 2 buffers can not be dequeued correctly, apps need
746 * to take care of it.
747 */
748 if (vout->pre2_vb) {
749 vout->pre2_vb->state = VIDEOBUF_DONE;
750 wake_up_interruptible(&vout->pre2_vb->done);
751 vout->pre2_vb = NULL;
752 }
753
754 if (vout->linear_bypass_pp) {
755 vout->pre2_vb = vout->pre1_vb;
756 vout->pre1_vb = vb;
757 } else {
758 if (vout->pre1_vb) {
759 vout->pre1_vb->state = VIDEOBUF_DONE;
760 wake_up_interruptible(&vout->pre1_vb->done);
761 vout->pre1_vb = NULL;
762 }
763 vb->state = VIDEOBUF_DONE;
764 wake_up_interruptible(&vb->done);
765 }
766
767 vout->frame_count++;
768
769 /* pick next queue buf to setup timer */
770 if (list_empty(&vout->queue_list))
771 vout->timer_stop = true;
772 else {
773 vb = list_first_entry(&vout->queue_list,
774 struct videobuf_buffer, queue);
775 setup_buf_timer(vout, vb);
776 }
777
778 spin_unlock_irqrestore(q->irqlock, flags);
779
780 v4l2_dbg(1, debug, vout->vfd->v4l2_dev, "disp work finish one frame\n");
781
782 return;
783err:
784 v4l2_err(vout->vfd->v4l2_dev, "display work fail ret = %d\n", ret);
785 vout->timer_stop = true;
786 vb->state = VIDEOBUF_ERROR;
787 return;
788}
789
790static enum hrtimer_restart mxc_vout_timer_handler(struct hrtimer *timer)
791{
792 struct mxc_vout_output *vout = container_of(timer,
793 struct mxc_vout_output,
794 timer);
795 struct videobuf_queue *q = &vout->vbq;
796 struct videobuf_buffer *vb;
797 unsigned long flags = 0;
798
799 spin_lock_irqsave(q->irqlock, flags);
800
801 /*
802 * put first queued entry into active, if previous entry did not
803 * finish, setup current entry's timer again.
804 */
805 if (list_empty(&vout->queue_list)) {
806 spin_unlock_irqrestore(q->irqlock, flags);
807 return HRTIMER_NORESTART;
808 }
809
810 /* move videobuf from queued list to active list */
811 vb = list_first_entry(&vout->queue_list,
812 struct videobuf_buffer, queue);
813 list_del(&vb->queue);
814 list_add_tail(&vb->queue, &vout->active_list);
815
816 if (queue_work(vout->v4l_wq, &vout->disp_work) == 0) {
817 v4l2_warn(vout->vfd->v4l2_dev,
818 "disp work was in queue already, queue buf again next time\n");
819 list_del(&vb->queue);
820 list_add(&vb->queue, &vout->queue_list);
821 spin_unlock_irqrestore(q->irqlock, flags);
822 return HRTIMER_NORESTART;
823 }
824
825 vb->state = VIDEOBUF_ACTIVE;
826
827 spin_unlock_irqrestore(q->irqlock, flags);
828
829 return HRTIMER_NORESTART;
830}
831
832/* Video buffer call backs */
833
834/*
835 * Buffer setup function is called by videobuf layer when REQBUF ioctl is
836 * called. This is used to setup buffers and return size and count of
837 * buffers allocated. After the call to this buffer, videobuf layer will
838 * setup buffer queue depending on the size and count of buffers
839 */
840static int mxc_vout_buffer_setup(struct videobuf_queue *q, unsigned int *count,
841 unsigned int *size)
842{
843 struct mxc_vout_output *vout = q->priv_data;
844 unsigned int frame_size;
845
846 if (!vout)
847 return -EINVAL;
848
849 if (V4L2_BUF_TYPE_VIDEO_OUTPUT != q->type)
850 return -EINVAL;
851
852 frame_size = get_frame_size(vout);
853 *size = PAGE_ALIGN(frame_size);
854
855 return 0;
856}
857
858/*
859 * This function will be called when VIDIOC_QBUF ioctl is called.
860 * It prepare buffers before give out for the display. This function
861 * converts user space virtual address into physical address if userptr memory
862 * exchange mechanism is used.
863 */
864static int mxc_vout_buffer_prepare(struct videobuf_queue *q,
865 struct videobuf_buffer *vb,
866 enum v4l2_field field)
867{
868 vb->state = VIDEOBUF_PREPARED;
869 return 0;
870}
871
872/*
873 * Buffer queue funtion will be called from the videobuf layer when _QBUF
874 * ioctl is called. It is used to enqueue buffer, which is ready to be
875 * displayed.
876 * This function is protected by q->irqlock.
877 */
878static void mxc_vout_buffer_queue(struct videobuf_queue *q,
879 struct videobuf_buffer *vb)
880{
881 struct mxc_vout_output *vout = q->priv_data;
882 struct videobuf_buffer *active_vb;
883
884 list_add_tail(&vb->queue, &vout->queue_list);
885 vb->state = VIDEOBUF_QUEUED;
886
887 if (vout->timer_stop) {
888 if (deinterlace_3_field(vout) &&
889 !list_empty(&vout->active_list)) {
890 active_vb = list_first_entry(&vout->active_list,
891 struct videobuf_buffer, queue);
892 setup_buf_timer(vout, active_vb);
893 } else {
894 setup_buf_timer(vout, vb);
895 }
896 vout->timer_stop = false;
897 }
898}
899
900/*
901 * Buffer release function is called from videobuf layer to release buffer
902 * which are already allocated
903 */
904static void mxc_vout_buffer_release(struct videobuf_queue *q,
905 struct videobuf_buffer *vb)
906{
907 vb->state = VIDEOBUF_NEEDS_INIT;
908}
909
910static int mxc_vout_mmap(struct file *file, struct vm_area_struct *vma)
911{
912 int ret;
913 struct mxc_vout_output *vout = file->private_data;
914
915 if (!vout)
916 return -ENODEV;
917
918 ret = videobuf_mmap_mapper(&vout->vbq, vma);
919 if (ret < 0)
920 v4l2_err(vout->vfd->v4l2_dev,
921 "offset invalid [offset=0x%lx]\n",
922 (vma->vm_pgoff << PAGE_SHIFT));
923
924 return ret;
925}
926
927static int mxc_vout_release(struct file *file)
928{
929 unsigned int ret = 0;
930 struct videobuf_queue *q;
931 struct mxc_vout_output *vout = file->private_data;
932
933 if (!vout)
934 return 0;
935
936 if (--vout->open_cnt == 0) {
937 q = &vout->vbq;
938 if (q->streaming)
939 mxc_vidioc_streamoff(file, vout, vout->type);
940 else {
941 release_disp_output(vout);
942 videobuf_queue_cancel(q);
943 }
944 destroy_workqueue(vout->v4l_wq);
945 ret = videobuf_mmap_free(q);
946 }
947
948 return ret;
949}
950
951static int mxc_vout_open(struct file *file)
952{
953 struct mxc_vout_output *vout = NULL;
954 int ret = 0;
955
956 vout = video_drvdata(file);
957
958 if (vout == NULL)
959 return -ENODEV;
960
961 if (vout->open_cnt++ == 0) {
962 vout->ctrl_rotate = 0;
963 vout->ctrl_vflip = 0;
964 vout->ctrl_hflip = 0;
965 update_display_setting();
966 ret = update_setting_from_fbi(vout, vout->fbi);
967 if (ret < 0)
968 goto err;
969
970 vout->v4l_wq = create_singlethread_workqueue("v4l2q");
971 if (!vout->v4l_wq) {
972 v4l2_err(vout->vfd->v4l2_dev,
973 "Could not create work queue\n");
974 ret = -ENOMEM;
975 goto err;
976 }
977
978 INIT_WORK(&vout->disp_work, disp_work_func);
979
980 INIT_LIST_HEAD(&vout->queue_list);
981 INIT_LIST_HEAD(&vout->active_list);
982
983 vout->fmt_init = false;
984 vout->frame_count = 0;
985 vout->vdi_frame_cnt = 0;
986
987 vout->win_pos.x = 0;
988 vout->win_pos.y = 0;
989 vout->release = true;
990 }
991
992 file->private_data = vout;
993
994err:
995 return ret;
996}
997
998/*
999 * V4L2 ioctls
1000 */
1001static int mxc_vidioc_querycap(struct file *file, void *fh,
1002 struct v4l2_capability *cap)
1003{
1004 struct mxc_vout_output *vout = fh;
1005
1006 strlcpy(cap->driver, VOUT_NAME, sizeof(cap->driver));
1007 strlcpy(cap->card, vout->vfd->name, sizeof(cap->card));
1008 cap->bus_info[0] = '\0';
1009 cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OUTPUT;
1010
1011 return 0;
1012}
1013
1014static int mxc_vidioc_enum_fmt_vid_out(struct file *file, void *fh,
1015 struct v4l2_fmtdesc *fmt)
1016{
1017 if (fmt->index >= NUM_MXC_VOUT_FORMATS)
1018 return -EINVAL;
1019
1020 strlcpy(fmt->description, mxc_formats[fmt->index].description,
1021 sizeof(fmt->description));
1022 fmt->pixelformat = mxc_formats[fmt->index].pixelformat;
1023
1024 return 0;
1025}
1026
1027static int mxc_vidioc_g_fmt_vid_out(struct file *file, void *fh,
1028 struct v4l2_format *f)
1029{
1030 struct mxc_vout_output *vout = fh;
1031 struct v4l2_rect rect;
1032
1033 f->fmt.pix.width = vout->task.input.width;
1034 f->fmt.pix.height = vout->task.input.height;
1035 f->fmt.pix.pixelformat = vout->task.input.format;
1036 f->fmt.pix.sizeimage = get_frame_size(vout);
1037
1038 if (f->fmt.pix.priv) {
1039 rect.left = vout->task.input.crop.pos.x;
1040 rect.top = vout->task.input.crop.pos.y;
1041 rect.width = vout->task.input.crop.w;
1042 rect.height = vout->task.input.crop.h;
1043 if (copy_to_user((void __user *)f->fmt.pix.priv,
1044 &rect, sizeof(rect)))
1045 return -EFAULT;
1046 }
1047 v4l2_dbg(1, debug, vout->vfd->v4l2_dev,
1048 "frame_size:0x%x, pix_fmt:0x%x\n",
1049 f->fmt.pix.sizeimage,
1050 vout->task.input.format);
1051
1052 return 0;
1053}
1054
1055static inline int ipu_try_task(struct mxc_vout_output *vout)
1056{
1057 int ret;
1058 struct ipu_task *task = &vout->task;
1059
1060again:
1061 ret = ipu_check_task(task);
1062 if (ret != IPU_CHECK_OK) {
1063 if (ret > IPU_CHECK_ERR_MIN) {
1064 if (ret == IPU_CHECK_ERR_SPLIT_INPUTW_OVER) {
1065 task->input.crop.w -= 8;
1066 goto again;
1067 }
1068 if (ret == IPU_CHECK_ERR_SPLIT_INPUTH_OVER) {
1069 task->input.crop.h -= 8;
1070 goto again;
1071 }
1072 if (ret == IPU_CHECK_ERR_SPLIT_OUTPUTW_OVER) {
1073 if (vout->disp_support_windows) {
1074 task->output.width -= 8;
1075 task->output.crop.w =
1076 task->output.width;
1077 } else
1078 task->output.crop.w -= 8;
1079 goto again;
1080 }
1081 if (ret == IPU_CHECK_ERR_SPLIT_OUTPUTH_OVER) {
1082 if (vout->disp_support_windows) {
1083 task->output.height -= 8;
1084 task->output.crop.h =
1085 task->output.height;
1086 } else
1087 task->output.crop.h -= 8;
1088 goto again;
1089 }
1090 ret = -EINVAL;
1091 }
1092 } else
1093 ret = 0;
1094
1095 return ret;
1096}
1097
1098static inline int vdoaipu_try_task(struct mxc_vout_output *vout)
1099{
1100 int ret;
1101 int is_1080p_stream;
1102 size_t size;
1103 struct ipu_task *ipu_task = &vout->task;
1104 struct ipu_crop *icrop = &ipu_task->input.crop;
1105 struct ipu_task *vdoa_task = &vout->vdoa_task;
1106 u32 deinterlace = 0;
1107 u32 in_fmt;
1108
1109 if (vout->task.input.deinterlace.enable)
1110 deinterlace = 1;
1111
1112 memset(vdoa_task, 0, sizeof(*vdoa_task));
1113 vdoa_task->output.format = IPU_PIX_FMT_NV12;
1114 memcpy(&vdoa_task->input, &ipu_task->input,
1115 sizeof(ipu_task->input));
1116 if ((icrop->w % IPU_PIX_FMT_TILED_NV12_MBALIGN) ||
1117 (icrop->h % IPU_PIX_FMT_TILED_NV12_MBALIGN)) {
1118 vdoa_task->input.crop.w =
1119 ALIGN(icrop->w, IPU_PIX_FMT_TILED_NV12_MBALIGN);
1120 vdoa_task->input.crop.h =
1121 ALIGN(icrop->h, IPU_PIX_FMT_TILED_NV12_MBALIGN);
1122 }
1123 vdoa_task->output.width = vdoa_task->input.crop.w;
1124 vdoa_task->output.height = vdoa_task->input.crop.h;
1125 vdoa_task->output.crop.w = vdoa_task->input.crop.w;
1126 vdoa_task->output.crop.h = vdoa_task->input.crop.h;
1127
1128 size = PAGE_ALIGN(vdoa_task->input.crop.w *
1129 vdoa_task->input.crop.h *
1130 fmt_to_bpp(vdoa_task->output.format)/8);
1131 if (size > vout->vdoa_work.size) {
1132 if (vout->vdoa_work.vaddr)
1133 free_dma_buf(vout, &vout->vdoa_work);
1134 vout->vdoa_work.size = size;
1135 ret = alloc_dma_buf(vout, &vout->vdoa_work);
1136 if (ret < 0)
1137 return ret;
1138 }
1139 ret = ipu_check_task(vdoa_task);
1140 if (ret != IPU_CHECK_OK)
1141 return -EINVAL;
1142
1143 is_1080p_stream = CHECK_TILED_1080P_STREAM(vout);
1144 if (is_1080p_stream)
1145 ipu_task->input.crop.h = VALID_HEIGHT_1080P;
1146 in_fmt = ipu_task->input.format;
1147 ipu_task->input.format = vdoa_task->output.format;
1148 ipu_task->input.height = vdoa_task->output.height;
1149 ipu_task->input.width = vdoa_task->output.width;
1150 if (deinterlace)
1151 ipu_task->input.deinterlace.enable = 0;
1152 ret = ipu_try_task(vout);
1153 if (deinterlace)
1154 ipu_task->input.deinterlace.enable = 1;
1155 ipu_task->input.format = in_fmt;
1156
1157 return ret;
1158}
1159
1160static int mxc_vout_try_task(struct mxc_vout_output *vout)
1161{
1162 int ret = 0;
1163 struct ipu_output *output = &vout->task.output;
1164 struct ipu_input *input = &vout->task.input;
1165 struct ipu_crop *crop = &input->crop;
1166 u32 o_height = 0;
1167 u32 ocrop_h = 0;
1168 bool tiled_fmt = false;
1169 bool tiled_need_pp = false;
1170
1171 vout->vdoa_1080p = CHECK_TILED_1080P_DISPLAY(vout);
1172 if (vout->vdoa_1080p) {
1173 input->crop.h = FRAME_HEIGHT_1080P;
1174 o_height = output->height;
1175 ocrop_h = output->crop.h;
1176 output->height = FRAME_HEIGHT_1080P;
1177 output->crop.h = FRAME_HEIGHT_1080P;
1178 }
1179
1180 if ((IPU_PIX_FMT_TILED_NV12 == input->format) ||
1181 (IPU_PIX_FMT_TILED_NV12F == input->format)) {
1182 if ((input->width % IPU_PIX_FMT_TILED_NV12_MBALIGN) ||
1183 (input->height % IPU_PIX_FMT_TILED_NV12_MBALIGN) ||
1184 (crop->pos.x % IPU_PIX_FMT_TILED_NV12_MBALIGN) ||
1185 (crop->pos.y % IPU_PIX_FMT_TILED_NV12_MBALIGN)) {
1186 v4l2_err(vout->vfd->v4l2_dev,
1187 "ERR: tiled fmt needs 16 pixel align.\n");
1188 return -EINVAL;
1189 }
1190 if ((crop->w % IPU_PIX_FMT_TILED_NV12_MBALIGN) ||
1191 (crop->h % IPU_PIX_FMT_TILED_NV12_MBALIGN))
1192 tiled_need_pp = true;
1193 } else {
1194 crop->w -= crop->w % 8;
1195 crop->h -= crop->h % 8;
1196 }
1197 /* assume task.output already set by S_CROP */
1198 vout->linear_bypass_pp = is_pp_bypass(vout);
1199 if (vout->linear_bypass_pp) {
1200 v4l2_info(vout->vfd->v4l2_dev, "Bypass IC.\n");
1201 output->format = input->format;
1202 } else {
1203 /* if need CSC, choose IPU-DP or IPU_IC do it */
1204 if (vout->disp_support_csc) {
1205 if (colorspaceofpixel(input->format) == YUV_CS)
1206 output->format = IPU_PIX_FMT_UYVY;
1207 else
1208 output->format = IPU_PIX_FMT_RGB565;
1209 } else {
1210 if (colorspaceofpixel(vout->disp_fmt) == YUV_CS)
1211 output->format = IPU_PIX_FMT_UYVY;
1212 else
1213 output->format = IPU_PIX_FMT_RGB565;
1214 }
1215
1216 vout->tiled_bypass_pp = false;
1217 if ((IPU_PIX_FMT_TILED_NV12 == input->format) ||
1218 (IPU_PIX_FMT_TILED_NV12F == input->format)) {
1219 /* check resize/rotate/flip, or csc task */
1220 if (!(tiled_need_pp ||
1221 (IPU_ROTATE_NONE != output->rotate) ||
1222 (input->crop.w != output->crop.w) ||
1223 (input->crop.h != output->crop.h) ||
1224 (!vout->disp_support_csc &&
1225 (colorspaceofpixel(vout->disp_fmt) == RGB_CS)))
1226 ) {
1227 /* IC bypass */
1228 output->format = IPU_PIX_FMT_NV12;
1229 v4l2_dbg(1, debug, vout->vfd->v4l2_dev,
1230 "tiled bypass pp\n");
1231 vout->tiled_bypass_pp = true;
1232 }
1233 tiled_fmt = true;
1234 }
1235
1236 if ((!vout->tiled_bypass_pp) && tiled_fmt)
1237 ret = vdoaipu_try_task(vout);
1238 else
1239 ret = ipu_try_task(vout);
1240 }
1241
1242 if (vout->vdoa_1080p) {
1243 output->height = o_height;
1244 output->crop.h = ocrop_h;
1245 }
1246
1247 v4l2_dbg(1, debug, vout->vfd->v4l2_dev,
1248 "icrop.w:%u, icrop.h:%u, iw:%u, ih:%u,"
1249 "ocrop.w:%u, ocrop.h:%u, ow:%u, oh:%u\n",
1250 input->crop.w, input->crop.h,
1251 input->width, input->height,
1252 output->crop.w, output->crop.h,
1253 output->width, output->height);
1254 return ret;
1255}
1256
1257static int mxc_vout_try_format(struct mxc_vout_output *vout,
1258 struct v4l2_format *f)
1259{
1260 int ret = 0;
1261 struct v4l2_rect rect;
1262
1263 if ((f->fmt.pix.field != V4L2_FIELD_NONE) &&
1264 (IPU_PIX_FMT_TILED_NV12 == vout->task.input.format)) {
1265 v4l2_err(vout->vfd->v4l2_dev,
1266 "progressive tiled fmt should used V4L2_FIELD_NONE!\n");
1267 return -EINVAL;
1268 }
1269
1270 if (f->fmt.pix.priv && copy_from_user(&rect,
1271 (void __user *)f->fmt.pix.priv, sizeof(rect)))
1272 return -EFAULT;
1273
1274 vout->task.input.width = f->fmt.pix.width;
1275 vout->task.input.height = f->fmt.pix.height;
1276 vout->task.input.format = f->fmt.pix.pixelformat;
1277
1278 ret = set_field_fmt(vout, f->fmt.pix.field);
1279 if (ret < 0)
1280 return ret;
1281
1282 if (f->fmt.pix.priv) {
1283 vout->task.input.crop.pos.x = rect.left;
1284 vout->task.input.crop.pos.y = rect.top;
1285 vout->task.input.crop.w = rect.width;
1286 vout->task.input.crop.h = rect.height;
1287 } else {
1288 vout->task.input.crop.pos.x = 0;
1289 vout->task.input.crop.pos.y = 0;
1290 vout->task.input.crop.w = f->fmt.pix.width;
1291 vout->task.input.crop.h = f->fmt.pix.height;
1292 }
1293 memcpy(&vout->in_rect, &vout->task.input.crop, sizeof(vout->in_rect));
1294
1295 ret = mxc_vout_try_task(vout);
1296 if (!ret) {
1297 if (f->fmt.pix.priv) {
1298 rect.width = vout->task.input.crop.w;
1299 rect.height = vout->task.input.crop.h;
1300 if (copy_to_user((void __user *)f->fmt.pix.priv,
1301 &rect, sizeof(rect)))
1302 ret = -EFAULT;
1303 } else {
1304 f->fmt.pix.width = vout->task.input.crop.w;
1305 f->fmt.pix.height = vout->task.input.crop.h;
1306 }
1307 }
1308
1309 return ret;
1310}
1311
1312static int mxc_vidioc_s_fmt_vid_out(struct file *file, void *fh,
1313 struct v4l2_format *f)
1314{
1315 struct mxc_vout_output *vout = fh;
1316 int ret = 0;
1317
1318 if (vout->vbq.streaming)
1319 return -EBUSY;
1320
1321 mutex_lock(&vout->task_lock);
1322 ret = mxc_vout_try_format(vout, f);
1323 if (ret >= 0)
1324 vout->fmt_init = true;
1325 mutex_unlock(&vout->task_lock);
1326
1327 return ret;
1328}
1329
1330static int mxc_vidioc_cropcap(struct file *file, void *fh,
1331 struct v4l2_cropcap *cropcap)
1332{
1333 struct mxc_vout_output *vout = fh;
1334
1335 if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
1336 return -EINVAL;
1337
1338 cropcap->bounds = vout->crop_bounds;
1339 cropcap->defrect = vout->crop_bounds;
1340
1341 return 0;
1342}
1343
1344static int mxc_vidioc_g_crop(struct file *file, void *fh,
1345 struct v4l2_crop *crop)
1346{
1347 struct mxc_vout_output *vout = fh;
1348
1349 if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
1350 return -EINVAL;
1351
1352 if (vout->disp_support_windows) {
1353 crop->c.left = vout->win_pos.x;
1354 crop->c.top = vout->win_pos.y;
1355 crop->c.width = vout->task.output.width;
1356 crop->c.height = vout->task.output.height;
1357 } else {
1358 if (vout->task.output.crop.w && vout->task.output.crop.h) {
1359 crop->c.left = vout->task.output.crop.pos.x;
1360 crop->c.top = vout->task.output.crop.pos.y;
1361 crop->c.width = vout->task.output.crop.w;
1362 crop->c.height = vout->task.output.crop.h;
1363 } else {
1364 crop->c.left = 0;
1365 crop->c.top = 0;
1366 crop->c.width = vout->task.output.width;
1367 crop->c.height = vout->task.output.height;
1368 }
1369 }
1370
1371 return 0;
1372}
1373
1374static int mxc_vidioc_s_crop(struct file *file, void *fh,
1375 const struct v4l2_crop *crop)
1376{
1377 struct mxc_vout_output *vout = fh;
1378 struct v4l2_rect *b = &vout->crop_bounds;
1379 struct v4l2_crop fix_up_crop;
1380 int ret = 0;
1381
1382 memcpy(&fix_up_crop, crop, sizeof(*crop));
1383
1384 if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
1385 return -EINVAL;
1386
1387 if (crop->c.width < 0 || crop->c.height < 0)
1388 return -EINVAL;
1389
1390 if (crop->c.width == 0)
1391 fix_up_crop.c.width = b->width - b->left;
1392 if (crop->c.height == 0)
1393 fix_up_crop.c.height = b->height - b->top;
1394
1395 if (crop->c.top < b->top)
1396 fix_up_crop.c.top = b->top;
1397 if (crop->c.top >= b->top + b->height)
1398 fix_up_crop.c.top = b->top + b->height - 1;
1399 if (crop->c.height > b->top - crop->c.top + b->height)
1400 fix_up_crop.c.height =
1401 b->top - fix_up_crop.c.top + b->height;
1402
1403 if (crop->c.left < b->left)
1404 fix_up_crop.c.left = b->left;
1405 if (crop->c.left >= b->left + b->width)
1406 fix_up_crop.c.left = b->left + b->width - 1;
1407 if (crop->c.width > b->left - crop->c.left + b->width)
1408 fix_up_crop.c.width =
1409 b->left - fix_up_crop.c.left + b->width;
1410
1411 /* stride line limitation */
1412 fix_up_crop.c.height -= fix_up_crop.c.height % 8;
1413 fix_up_crop.c.width -= fix_up_crop.c.width % 8;
1414 if ((fix_up_crop.c.width <= 0) || (fix_up_crop.c.height <= 0) ||
1415 ((fix_up_crop.c.left + fix_up_crop.c.width) >
1416 (b->left + b->width)) ||
1417 ((fix_up_crop.c.top + fix_up_crop.c.height) >
1418 (b->top + b->height))) {
1419 v4l2_err(vout->vfd->v4l2_dev, "s_crop err: %d, %d, %d, %d",
1420 fix_up_crop.c.left, fix_up_crop.c.top,
1421 fix_up_crop.c.width, fix_up_crop.c.height);
1422 return -EINVAL;
1423 }
1424
1425 /* the same setting, return */
1426 if (vout->disp_support_windows) {
1427 if ((vout->win_pos.x == fix_up_crop.c.left) &&
1428 (vout->win_pos.y == fix_up_crop.c.top) &&
1429 (vout->task.output.crop.w == fix_up_crop.c.width) &&
1430 (vout->task.output.crop.h == fix_up_crop.c.height))
1431 return 0;
1432 } else {
1433 if ((vout->task.output.crop.pos.x == fix_up_crop.c.left) &&
1434 (vout->task.output.crop.pos.y == fix_up_crop.c.top) &&
1435 (vout->task.output.crop.w == fix_up_crop.c.width) &&
1436 (vout->task.output.crop.h == fix_up_crop.c.height))
1437 return 0;
1438 }
1439
1440 /* wait current work finish */
1441 if (vout->vbq.streaming)
1442 flush_workqueue(vout->v4l_wq);
1443
1444 mutex_lock(&vout->task_lock);
1445
1446 if (vout->disp_support_windows) {
1447 vout->task.output.crop.pos.x = 0;
1448 vout->task.output.crop.pos.y = 0;
1449 vout->win_pos.x = fix_up_crop.c.left;
1450 vout->win_pos.y = fix_up_crop.c.top;
1451 vout->task.output.width = fix_up_crop.c.width;
1452 vout->task.output.height = fix_up_crop.c.height;
1453 } else {
1454 vout->task.output.crop.pos.x = fix_up_crop.c.left;
1455 vout->task.output.crop.pos.y = fix_up_crop.c.top;
1456 }
1457
1458 vout->task.output.crop.w = fix_up_crop.c.width;
1459 vout->task.output.crop.h = fix_up_crop.c.height;
1460
1461 /*
1462 * must S_CROP before S_FMT, for fist time S_CROP, will not check
1463 * ipu task, it will check in S_FMT, after S_FMT, S_CROP should
1464 * check ipu task too.
1465 */
1466 if (vout->fmt_init) {
1467 if (vout->vbq.streaming)
1468 release_disp_output(vout);
1469
1470 memcpy(&vout->task.input.crop, &vout->in_rect,
1471 sizeof(vout->in_rect));
1472 ret = mxc_vout_try_task(vout);
1473 if (ret < 0) {
1474 v4l2_err(vout->vfd->v4l2_dev,
1475 "vout check task failed\n");
1476 goto done;
1477 }
1478 if (vout->vbq.streaming) {
1479 ret = config_disp_output(vout);
1480 if (ret < 0) {
1481 v4l2_err(vout->vfd->v4l2_dev,
1482 "Config display output failed\n");
1483 goto done;
1484 }
1485 }
1486 }
1487
1488done:
1489 mutex_unlock(&vout->task_lock);
1490
1491 return ret;
1492}
1493
1494static int mxc_vidioc_queryctrl(struct file *file, void *fh,
1495 struct v4l2_queryctrl *ctrl)
1496{
1497 int ret = 0;
1498
1499 switch (ctrl->id) {
1500 case V4L2_CID_ROTATE:
1501 ret = v4l2_ctrl_query_fill(ctrl, 0, 270, 90, 0);
1502 break;
1503 case V4L2_CID_VFLIP:
1504 ret = v4l2_ctrl_query_fill(ctrl, 0, 1, 1, 0);
1505 break;
1506 case V4L2_CID_HFLIP:
1507 ret = v4l2_ctrl_query_fill(ctrl, 0, 1, 1, 0);
1508 break;
1509 case V4L2_CID_MXC_MOTION:
1510 ret = v4l2_ctrl_query_fill(ctrl, 0, 2, 1, 0);
1511 break;
1512 default:
1513 ctrl->name[0] = '\0';
1514 ret = -EINVAL;
1515 }
1516 return ret;
1517}
1518
1519static int mxc_vidioc_g_ctrl(struct file *file, void *fh,
1520 struct v4l2_control *ctrl)
1521{
1522 int ret = 0;
1523 struct mxc_vout_output *vout = fh;
1524
1525 switch (ctrl->id) {
1526 case V4L2_CID_ROTATE:
1527 ctrl->value = vout->ctrl_rotate;
1528 break;
1529 case V4L2_CID_VFLIP:
1530 ctrl->value = vout->ctrl_vflip;
1531 break;
1532 case V4L2_CID_HFLIP:
1533 ctrl->value = vout->ctrl_hflip;
1534 break;
1535 case V4L2_CID_MXC_MOTION:
1536 if (vout->task.input.deinterlace.enable)
1537 ctrl->value = vout->task.input.deinterlace.motion;
1538 else
1539 ctrl->value = 0;
1540 break;
1541 default:
1542 ret = -EINVAL;
1543 }
1544 return ret;
1545}
1546
1547static void setup_task_rotation(struct mxc_vout_output *vout)
1548{
1549 if (vout->ctrl_rotate == 0) {
1550 if (vout->ctrl_vflip && vout->ctrl_hflip)
1551 vout->task.output.rotate = IPU_ROTATE_180;
1552 else if (vout->ctrl_vflip)
1553 vout->task.output.rotate = IPU_ROTATE_VERT_FLIP;
1554 else if (vout->ctrl_hflip)
1555 vout->task.output.rotate = IPU_ROTATE_HORIZ_FLIP;
1556 else
1557 vout->task.output.rotate = IPU_ROTATE_NONE;
1558 } else if (vout->ctrl_rotate == 90) {
1559 if (vout->ctrl_vflip && vout->ctrl_hflip)
1560 vout->task.output.rotate = IPU_ROTATE_90_LEFT;
1561 else if (vout->ctrl_vflip)
1562 vout->task.output.rotate = IPU_ROTATE_90_RIGHT_VFLIP;
1563 else if (vout->ctrl_hflip)
1564 vout->task.output.rotate = IPU_ROTATE_90_RIGHT_HFLIP;
1565 else
1566 vout->task.output.rotate = IPU_ROTATE_90_RIGHT;
1567 } else if (vout->ctrl_rotate == 180) {
1568 if (vout->ctrl_vflip && vout->ctrl_hflip)
1569 vout->task.output.rotate = IPU_ROTATE_NONE;
1570 else if (vout->ctrl_vflip)
1571 vout->task.output.rotate = IPU_ROTATE_HORIZ_FLIP;
1572 else if (vout->ctrl_hflip)
1573 vout->task.output.rotate = IPU_ROTATE_VERT_FLIP;
1574 else
1575 vout->task.output.rotate = IPU_ROTATE_180;
1576 } else if (vout->ctrl_rotate == 270) {
1577 if (vout->ctrl_vflip && vout->ctrl_hflip)
1578 vout->task.output.rotate = IPU_ROTATE_90_RIGHT;
1579 else if (vout->ctrl_vflip)
1580 vout->task.output.rotate = IPU_ROTATE_90_RIGHT_HFLIP;
1581 else if (vout->ctrl_hflip)
1582 vout->task.output.rotate = IPU_ROTATE_90_RIGHT_VFLIP;
1583 else
1584 vout->task.output.rotate = IPU_ROTATE_90_LEFT;
1585 }
1586}
1587
1588static int mxc_vidioc_s_ctrl(struct file *file, void *fh,
1589 struct v4l2_control *ctrl)
1590{
1591 int ret = 0;
1592 struct mxc_vout_output *vout = fh;
1593
1594 /* wait current work finish */
1595 if (vout->vbq.streaming)
1596 flush_workqueue(vout->v4l_wq);
1597
1598 mutex_lock(&vout->task_lock);
1599 switch (ctrl->id) {
1600 case V4L2_CID_ROTATE:
1601 {
1602 vout->ctrl_rotate = (ctrl->value/90) * 90;
1603 if (vout->ctrl_rotate > 270)
1604 vout->ctrl_rotate = 270;
1605 setup_task_rotation(vout);
1606 break;
1607 }
1608 case V4L2_CID_VFLIP:
1609 {
1610 vout->ctrl_vflip = ctrl->value;
1611 setup_task_rotation(vout);
1612 break;
1613 }
1614 case V4L2_CID_HFLIP:
1615 {
1616 vout->ctrl_hflip = ctrl->value;
1617 setup_task_rotation(vout);
1618 break;
1619 }
1620 case V4L2_CID_MXC_MOTION:
1621 {
1622 vout->task.input.deinterlace.motion = ctrl->value;
1623 break;
1624 }
1625 default:
1626 ret = -EINVAL;
1627 goto done;
1628 }
1629
1630 if (vout->fmt_init) {
1631 if (vout->vbq.streaming)
1632 release_disp_output(vout);
1633
1634 memcpy(&vout->task.input.crop, &vout->in_rect,
1635 sizeof(vout->in_rect));
1636 ret = mxc_vout_try_task(vout);
1637 if (ret < 0) {
1638 v4l2_err(vout->vfd->v4l2_dev,
1639 "vout check task failed\n");
1640 goto done;
1641 }
1642 if (vout->vbq.streaming) {
1643 ret = config_disp_output(vout);
1644 if (ret < 0) {
1645 v4l2_err(vout->vfd->v4l2_dev,
1646 "Config display output failed\n");
1647 goto done;
1648 }
1649 }
1650 }
1651
1652done:
1653 mutex_unlock(&vout->task_lock);
1654
1655 return ret;
1656}
1657
1658static int mxc_vidioc_reqbufs(struct file *file, void *fh,
1659 struct v4l2_requestbuffers *req)
1660{
1661 int ret = 0;
1662 struct mxc_vout_output *vout = fh;
1663 struct videobuf_queue *q = &vout->vbq;
1664
1665 if (req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
1666 return -EINVAL;
1667
1668 /* should not be here after streaming, videobuf_reqbufs will control */
1669 mutex_lock(&vout->task_lock);
1670
1671 ret = videobuf_reqbufs(q, req);
1672
1673 mutex_unlock(&vout->task_lock);
1674 return ret;
1675}
1676
1677static int mxc_vidioc_querybuf(struct file *file, void *fh,
1678 struct v4l2_buffer *b)
1679{
1680 int ret;
1681 struct mxc_vout_output *vout = fh;
1682
1683 ret = videobuf_querybuf(&vout->vbq, b);
1684 if (!ret) {
1685 /* return physical address */
1686 struct videobuf_buffer *vb = vout->vbq.bufs[b->index];
1687 if (b->flags & V4L2_BUF_FLAG_MAPPED)
1688 b->m.offset = videobuf_to_dma_contig(vb);
1689 }
1690
1691 return ret;
1692}
1693
1694static int mxc_vidioc_qbuf(struct file *file, void *fh,
1695 struct v4l2_buffer *buffer)
1696{
1697 struct mxc_vout_output *vout = fh;
1698
1699 return videobuf_qbuf(&vout->vbq, buffer);
1700}
1701
1702static int mxc_vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
1703{
1704 struct mxc_vout_output *vout = fh;
1705
1706 if (!vout->vbq.streaming)
1707 return -EINVAL;
1708
1709 if (file->f_flags & O_NONBLOCK)
1710 return videobuf_dqbuf(&vout->vbq, (struct v4l2_buffer *)b, 1);
1711 else
1712 return videobuf_dqbuf(&vout->vbq, (struct v4l2_buffer *)b, 0);
1713}
1714
1715static int set_window_position(struct mxc_vout_output *vout,
1716 struct mxcfb_pos *pos)
1717{
1718 struct fb_info *fbi = vout->fbi;
1719 mm_segment_t old_fs;
1720 int ret = 0;
1721
1722 if (vout->disp_support_windows) {
1723 old_fs = get_fs();
1724 set_fs(KERNEL_DS);
1725 ret = fbi->fbops->fb_ioctl(fbi, MXCFB_SET_OVERLAY_POS,
1726 (unsigned long)pos);
1727 set_fs(old_fs);
1728 }
1729
1730 return ret;
1731}
1732
1733static int config_disp_output(struct mxc_vout_output *vout)
1734{
1735 struct dma_mem *buf = NULL;
1736 struct fb_info *fbi = vout->fbi;
1737 struct fb_var_screeninfo var;
1738 int i, fb_num, ret;
1739 u32 fb_base;
1740 u32 size;
1741 u32 display_buf_size;
1742 u32 *pixel = NULL;
1743 u32 color;
1744 int j;
1745
1746 memcpy(&var, &fbi->var, sizeof(var));
1747 fb_base = fbi->fix.smem_start;
1748
1749 var.xres = vout->task.output.width;
1750 var.yres = vout->task.output.height;
1751 if (vout->linear_bypass_pp || vout->tiled_bypass_pp) {
1752 fb_num = 1;
1753 /* input crop */
1754 if (vout->task.input.width > vout->task.output.width)
1755 var.xres_virtual = vout->task.input.width;
1756 else
1757 var.xres_virtual = var.xres;
1758 if (vout->task.input.height > vout->task.output.height)
1759 var.yres_virtual = vout->task.input.height;
1760 else
1761 var.yres_virtual = var.yres;
1762 var.rotate = vout->task.output.rotate;
1763 var.vmode |= FB_VMODE_YWRAP;
1764 } else {
1765 fb_num = FB_BUFS;
1766 var.xres_virtual = var.xres;
1767 var.yres_virtual = fb_num * var.yres;
1768 var.vmode &= ~FB_VMODE_YWRAP;
1769 }
1770 var.bits_per_pixel = fmt_to_bpp(vout->task.output.format);
1771 var.nonstd = vout->task.output.format;
1772
1773 v4l2_dbg(1, debug, vout->vfd->v4l2_dev,
1774 "set display fb to %d %d\n",
1775 var.xres, var.yres);
1776
1777 ret = set_window_position(vout, &vout->win_pos);
1778 if (ret < 0) {
1779 v4l2_err(vout->vfd->v4l2_dev, "ERR: set_win_pos ret:%d\n", ret);
1780 return ret;
1781 }
1782
1783 /* Init display channel through fb API */
1784 var.yoffset = 0;
1785 var.activate |= FB_ACTIVATE_FORCE;
1786 console_lock();
1787 fbi->flags |= FBINFO_MISC_USEREVENT;
1788 ret = fb_set_var(fbi, &var);
1789 fbi->flags &= ~FBINFO_MISC_USEREVENT;
1790 console_unlock();
1791 if (ret < 0) {
1792 v4l2_err(vout->vfd->v4l2_dev,
1793 "ERR:%s fb_set_var ret:%d\n", __func__, ret);
1794 return ret;
1795 }
1796 if (vout->linear_bypass_pp || vout->tiled_bypass_pp)
1797 display_buf_size = fbi->fix.line_length * fbi->var.yres_virtual;
1798 else
1799 display_buf_size = fbi->fix.line_length * fbi->var.yres;
1800 for (i = 0; i < fb_num; i++)
1801 vout->disp_bufs[i] = fbi->fix.smem_start + i * display_buf_size;
1802 if (vout->tiled_bypass_pp) {
1803 size = PAGE_ALIGN(vout->task.input.crop.w *
1804 vout->task.input.crop.h *
1805 fmt_to_bpp(vout->task.output.format)/8);
1806 if (size > vout->vdoa_output[0].size) {
1807 for (i = 0; i < VDOA_FB_BUFS; i++) {
1808 buf = &vout->vdoa_output[i];
1809 if (buf->vaddr)
1810 free_dma_buf(vout, buf);
1811 buf->size = size;
1812 ret = alloc_dma_buf(vout, buf);
1813 if (ret < 0)
1814 goto err;
1815 }
1816 }
1817 for (i = fb_num; i < (fb_num + VDOA_FB_BUFS); i++)
1818 vout->disp_bufs[i] =
1819 vout->vdoa_output[i - fb_num].paddr;
1820 }
1821 vout->fb_smem_len = fbi->fix.smem_len;
1822 vout->fb_smem_start = fbi->fix.smem_start;
1823 if (fb_base != fbi->fix.smem_start) {
1824 v4l2_dbg(1, debug, vout->vfd->v4l2_dev,
1825 "realloc fb mem size:0x%x@0x%lx,old paddr @0x%x\n",
1826 fbi->fix.smem_len, fbi->fix.smem_start, fb_base);
1827 }
1828
1829 /* fill black when video config changed */
1830 color = colorspaceofpixel(vout->task.output.format) == YUV_CS ?
1831 UYVY_BLACK : RGB_BLACK;
1832 if (IS_PLANAR_PIXEL_FORMAT(vout->task.output.format)) {
1833 size = display_buf_size * 8 /
1834 fmt_to_bpp(vout->task.output.format);
1835 memset(fbi->screen_base, Y_BLACK, size);
1836 memset(fbi->screen_base + size, UV_BLACK,
1837 display_buf_size - size);
1838 } else {
1839 pixel = (u32 *)fbi->screen_base;
1840 for (i = 0; i < (display_buf_size >> 2); i++)
1841 *pixel++ = color;
1842 }
1843 console_lock();
1844 fbi->flags |= FBINFO_MISC_USEREVENT;
1845 ret = fb_blank(fbi, FB_BLANK_UNBLANK);
1846 fbi->flags &= ~FBINFO_MISC_USEREVENT;
1847 console_unlock();
1848 vout->release = false;
1849
1850 return ret;
1851err:
1852 for (j = i - 1; j >= 0; j--) {
1853 buf = &vout->vdoa_output[j];
1854 if (buf->vaddr)
1855 free_dma_buf(vout, buf);
1856 }
1857 return ret;
1858}
1859
1860static inline void wait_for_vsync(struct mxc_vout_output *vout)
1861{
1862 struct fb_info *fbi = vout->fbi;
1863 mm_segment_t old_fs;
1864
1865 if (fbi->fbops->fb_ioctl) {
1866 old_fs = get_fs();
1867 set_fs(KERNEL_DS);
1868 fbi->fbops->fb_ioctl(fbi, MXCFB_WAIT_FOR_VSYNC,
1869 (unsigned long)NULL);
1870 set_fs(old_fs);
1871 }
1872
1873 return;
1874}
1875
1876static void release_disp_output(struct mxc_vout_output *vout)
1877{
1878 struct fb_info *fbi = vout->fbi;
1879 struct mxcfb_pos pos;
1880
1881 if (vout->release)
1882 return;
1883 console_lock();
1884 fbi->flags |= FBINFO_MISC_USEREVENT;
1885 fb_blank(fbi, FB_BLANK_POWERDOWN);
1886 fbi->flags &= ~FBINFO_MISC_USEREVENT;
1887 console_unlock();
1888
1889 /* restore pos to 0,0 avoid fb pan display hang? */
1890 pos.x = 0;
1891 pos.y = 0;
1892 set_window_position(vout, &pos);
1893
1894 if (get_ipu_channel(fbi) == MEM_BG_SYNC) {
1895 console_lock();
1896 fbi->fix.smem_start = vout->disp_bufs[0];
1897 fbi->flags |= FBINFO_MISC_USEREVENT;
1898 fb_blank(fbi, FB_BLANK_UNBLANK);
1899 fbi->flags &= ~FBINFO_MISC_USEREVENT;
1900 console_unlock();
1901
1902 }
1903
1904 vout->release = true;
1905}
1906
1907static int mxc_vidioc_streamon(struct file *file, void *fh,
1908 enum v4l2_buf_type i)
1909{
1910 struct mxc_vout_output *vout = fh;
1911 struct videobuf_queue *q = &vout->vbq;
1912 int ret;
1913
1914 if (q->streaming) {
1915 v4l2_err(vout->vfd->v4l2_dev,
1916 "video output already run\n");
1917 ret = -EBUSY;
1918 goto done;
1919 }
1920
1921 if (deinterlace_3_field(vout) && list_is_singular(&q->stream)) {
1922 v4l2_err(vout->vfd->v4l2_dev,
1923 "deinterlacing: need queue 2 frame before streamon\n");
1924 ret = -EINVAL;
1925 goto done;
1926 }
1927
1928 ret = config_disp_output(vout);
1929 if (ret < 0) {
1930 v4l2_err(vout->vfd->v4l2_dev,
1931 "Config display output failed\n");
1932 goto done;
1933 }
1934
1935 hrtimer_init(&vout->timer, CLOCK_REALTIME, HRTIMER_MODE_ABS);
1936 vout->timer.function = mxc_vout_timer_handler;
1937 vout->timer_stop = true;
1938
1939 vout->start_ktime = hrtimer_cb_get_time(&vout->timer);
1940
1941 vout->pre1_vb = NULL;
1942 vout->pre2_vb = NULL;
1943
1944 ret = videobuf_streamon(q);
1945done:
1946 return ret;
1947}
1948
1949static int mxc_vidioc_streamoff(struct file *file, void *fh,
1950 enum v4l2_buf_type i)
1951{
1952 struct mxc_vout_output *vout = fh;
1953 struct videobuf_queue *q = &vout->vbq;
1954 int ret = 0;
1955
1956 if (q->streaming) {
1957 flush_workqueue(vout->v4l_wq);
1958
1959 hrtimer_cancel(&vout->timer);
1960
1961 /*
1962 * Wait for 2 vsyncs to make sure
1963 * frames are drained on triple
1964 * buffer.
1965 */
1966 wait_for_vsync(vout);
1967 wait_for_vsync(vout);
1968
1969 release_disp_output(vout);
1970
1971 ret = videobuf_streamoff(&vout->vbq);
1972 }
1973 INIT_LIST_HEAD(&vout->queue_list);
1974 INIT_LIST_HEAD(&vout->active_list);
1975
1976 return ret;
1977}
1978
1979static const struct v4l2_ioctl_ops mxc_vout_ioctl_ops = {
1980 .vidioc_querycap = mxc_vidioc_querycap,
1981 .vidioc_enum_fmt_vid_out = mxc_vidioc_enum_fmt_vid_out,
1982 .vidioc_g_fmt_vid_out = mxc_vidioc_g_fmt_vid_out,
1983 .vidioc_s_fmt_vid_out = mxc_vidioc_s_fmt_vid_out,
1984 .vidioc_cropcap = mxc_vidioc_cropcap,
1985 .vidioc_g_crop = mxc_vidioc_g_crop,
1986 .vidioc_s_crop = mxc_vidioc_s_crop,
1987 .vidioc_queryctrl = mxc_vidioc_queryctrl,
1988 .vidioc_g_ctrl = mxc_vidioc_g_ctrl,
1989 .vidioc_s_ctrl = mxc_vidioc_s_ctrl,
1990 .vidioc_reqbufs = mxc_vidioc_reqbufs,
1991 .vidioc_querybuf = mxc_vidioc_querybuf,
1992 .vidioc_qbuf = mxc_vidioc_qbuf,
1993 .vidioc_dqbuf = mxc_vidioc_dqbuf,
1994 .vidioc_streamon = mxc_vidioc_streamon,
1995 .vidioc_streamoff = mxc_vidioc_streamoff,
1996};
1997
1998static const struct v4l2_file_operations mxc_vout_fops = {
1999 .owner = THIS_MODULE,
2000 .unlocked_ioctl = video_ioctl2,
2001 .mmap = mxc_vout_mmap,
2002 .open = mxc_vout_open,
2003 .release = mxc_vout_release,
2004};
2005
2006static struct video_device mxc_vout_template = {
2007 .name = "MXC Video Output",
2008 .fops = &mxc_vout_fops,
2009 .ioctl_ops = &mxc_vout_ioctl_ops,
2010 .release = video_device_release,
2011};
2012
2013static struct videobuf_queue_ops mxc_vout_vbq_ops = {
2014 .buf_setup = mxc_vout_buffer_setup,
2015 .buf_prepare = mxc_vout_buffer_prepare,
2016 .buf_release = mxc_vout_buffer_release,
2017 .buf_queue = mxc_vout_buffer_queue,
2018};
2019
2020static void mxc_vout_free_output(struct mxc_vout_dev *dev)
2021{
2022 int i;
2023 int j;
2024 struct mxc_vout_output *vout;
2025 struct video_device *vfd;
2026
2027 for (i = 0; i < dev->out_num; i++) {
2028 vout = dev->out[i];
2029 vfd = vout->vfd;
2030 if (vout->vdoa_work.vaddr)
2031 free_dma_buf(vout, &vout->vdoa_work);
2032 for (j = 0; j < VDOA_FB_BUFS; j++) {
2033 if (vout->vdoa_output[j].vaddr)
2034 free_dma_buf(vout, &vout->vdoa_output[j]);
2035 }
2036 if (vfd) {
2037 if (!video_is_registered(vfd))
2038 video_device_release(vfd);
2039 else
2040 video_unregister_device(vfd);
2041 }
2042 kfree(vout);
2043 }
2044}
2045
2046static int mxc_vout_setup_output(struct mxc_vout_dev *dev)
2047{
2048 struct videobuf_queue *q;
2049 struct fb_info *fbi;
2050 struct mxc_vout_output *vout;
2051 int i, ret = 0;
2052
2053 update_display_setting();
2054
2055 /* all output/overlay based on fb */
2056 for (i = 0; i < num_registered_fb; i++) {
2057 fbi = registered_fb[i];
2058
2059 vout = kzalloc(sizeof(struct mxc_vout_output), GFP_KERNEL);
2060 if (!vout) {
2061 ret = -ENOMEM;
2062 break;
2063 }
2064
2065 dev->out[dev->out_num] = vout;
2066 dev->out_num++;
2067
2068 vout->fbi = fbi;
2069 vout->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
2070 vout->vfd = video_device_alloc();
2071 if (!vout->vfd) {
2072 ret = -ENOMEM;
2073 break;
2074 }
2075
2076 *vout->vfd = mxc_vout_template;
2077 vout->vfd->debug = debug;
2078 vout->vfd->v4l2_dev = &dev->v4l2_dev;
2079 vout->vfd->lock = &vout->mutex;
2080 vout->vfd->vfl_dir = VFL_DIR_TX;
2081
2082 mutex_init(&vout->mutex);
2083 mutex_init(&vout->task_lock);
2084
2085 strlcpy(vout->vfd->name, fbi->fix.id, sizeof(vout->vfd->name));
2086
2087 video_set_drvdata(vout->vfd, vout);
2088
2089 if (video_register_device(vout->vfd,
2090 VFL_TYPE_GRABBER, video_nr + i) < 0) {
2091 ret = -ENODEV;
2092 break;
2093 }
2094
2095 q = &vout->vbq;
2096 q->dev = dev->dev;
2097 spin_lock_init(&vout->vbq_lock);
2098 videobuf_queue_dma_contig_init(q, &mxc_vout_vbq_ops, q->dev,
2099 &vout->vbq_lock, vout->type, V4L2_FIELD_NONE,
2100 sizeof(struct videobuf_buffer), vout, NULL);
2101
2102 v4l2_info(vout->vfd->v4l2_dev, "V4L2 device registered as %s\n",
2103 video_device_node_name(vout->vfd));
2104
2105 }
2106
2107 return ret;
2108}
2109
2110static int mxc_vout_probe(struct platform_device *pdev)
2111{
2112 int ret;
2113 struct mxc_vout_dev *dev;
2114
2115 dev = kzalloc(sizeof(*dev), GFP_KERNEL);
2116 if (!dev)
2117 return -ENOMEM;
2118
2119 dev->dev = &pdev->dev;
2120 dev->dev->dma_mask = kmalloc(sizeof(*dev->dev->dma_mask), GFP_KERNEL);
2121 *dev->dev->dma_mask = DMA_BIT_MASK(32);
2122 dev->dev->coherent_dma_mask = DMA_BIT_MASK(32);
2123
2124 ret = v4l2_device_register(dev->dev, &dev->v4l2_dev);
2125 if (ret) {
2126 dev_err(dev->dev, "v4l2_device_register failed\n");
2127 goto free_dev;
2128 }
2129
2130 ret = mxc_vout_setup_output(dev);
2131 if (ret < 0)
2132 goto rel_vdev;
2133
2134 return 0;
2135
2136rel_vdev:
2137 mxc_vout_free_output(dev);
2138 v4l2_device_unregister(&dev->v4l2_dev);
2139free_dev:
2140 kfree(dev);
2141 return ret;
2142}
2143
2144static int mxc_vout_remove(struct platform_device *pdev)
2145{
2146 struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
2147 struct mxc_vout_dev *dev = container_of(v4l2_dev, struct
2148 mxc_vout_dev, v4l2_dev);
2149
2150 mxc_vout_free_output(dev);
2151 v4l2_device_unregister(v4l2_dev);
2152 kfree(dev);
2153 return 0;
2154}
2155
2156static const struct of_device_id mxc_v4l2_dt_ids[] = {
2157 { .compatible = "fsl,mxc_v4l2_output", },
2158 { /* sentinel */ }
2159};
2160
2161static struct platform_driver mxc_vout_driver = {
2162 .driver = {
2163 .name = "mxc_v4l2_output",
2164 .of_match_table = mxc_v4l2_dt_ids,
2165 },
2166 .probe = mxc_vout_probe,
2167 .remove = mxc_vout_remove,
2168};
2169
2170static int __init mxc_vout_init(void)
2171{
2172 if (platform_driver_register(&mxc_vout_driver) != 0) {
2173 printk(KERN_ERR VOUT_NAME ":Could not register Video driver\n");
2174 return -EINVAL;
2175 }
2176 return 0;
2177}
2178
2179static void mxc_vout_cleanup(void)
2180{
2181 platform_driver_unregister(&mxc_vout_driver);
2182}
2183
2184module_init(mxc_vout_init);
2185module_exit(mxc_vout_cleanup);
2186
2187MODULE_AUTHOR("Freescale Semiconductor, Inc.");
2188MODULE_DESCRIPTION("V4L2-driver for MXC video output");
2189MODULE_LICENSE("GPL");
diff --git a/include/linux/mxc_v4l2.h b/include/linux/mxc_v4l2.h
new file mode 100644
index 000000000000..e05bd043496c
--- /dev/null
+++ b/include/linux/mxc_v4l2.h
@@ -0,0 +1,27 @@
1/*
2 * Copyright 2004-2013 Freescale Semiconductor, Inc. All Rights Reserved.
3 */
4
5/*
6 * The code contained herein is licensed under the GNU Lesser General
7 * Public License. You may obtain a copy of the GNU Lesser General
8 * Public License Version 2.1 or later at the following locations:
9 *
10 * http://www.opensource.org/licenses/lgpl-license.html
11 * http://www.gnu.org/copyleft/lgpl.html
12 */
13
14/*!
15 * @file linux/mxc_v4l2.h
16 *
17 * @brief MXC V4L2 private header file
18 *
19 * @ingroup MXC V4L2
20 */
21
22#ifndef __LINUX_MXC_V4L2_H__
23#define __LINUX_MXC_V4L2_H__
24
25#include <uapi/linux/mxc_v4l2.h>
26
27#endif
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index a19cdb19899d..9c9eb267483c 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -251,6 +251,7 @@ header-y += msdos_fs.h
251header-y += msg.h 251header-y += msg.h
252header-y += mtio.h 252header-y += mtio.h
253header-y += mxcfb.h 253header-y += mxcfb.h
254header-y += mxc_v4l2.h
254header-y += n_r3964.h 255header-y += n_r3964.h
255header-y += nbd.h 256header-y += nbd.h
256header-y += ncp.h 257header-y += ncp.h
diff --git a/include/uapi/linux/mxc_v4l2.h b/include/uapi/linux/mxc_v4l2.h
new file mode 100644
index 000000000000..49345fea15ec
--- /dev/null
+++ b/include/uapi/linux/mxc_v4l2.h
@@ -0,0 +1,56 @@
1/*
2 * Copyright (C) 2013 Freescale Semiconductor, Inc. All Rights Reserved
3 */
4
5/*
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21/*!
22 * @file uapi/linux/mxc_v4l2.h
23 *
24 * @brief MXC V4L2 private header file
25 *
26 * @ingroup MXC V4L2
27 */
28
29#ifndef __ASM_ARCH_MXC_V4L2_H__
30#define __ASM_ARCH_MXC_V4L2_H__
31
32/*
33 * For IPUv1 and IPUv3, V4L2_CID_MXC_ROT means encoder ioctl ID.
34 * And V4L2_CID_MXC_VF_ROT is viewfinder ioctl ID only for IPUv1 and IPUv3.
35 */
36#define V4L2_CID_MXC_ROT (V4L2_CID_PRIVATE_BASE + 0)
37#define V4L2_CID_MXC_FLASH (V4L2_CID_PRIVATE_BASE + 1)
38#define V4L2_CID_MXC_VF_ROT (V4L2_CID_PRIVATE_BASE + 2)
39#define V4L2_CID_MXC_MOTION (V4L2_CID_PRIVATE_BASE + 3)
40#define V4L2_CID_MXC_SWITCH_CAM (V4L2_CID_PRIVATE_BASE + 6)
41
42#define V4L2_MXC_ROTATE_NONE 0
43#define V4L2_MXC_ROTATE_VERT_FLIP 1
44#define V4L2_MXC_ROTATE_HORIZ_FLIP 2
45#define V4L2_MXC_ROTATE_180 3
46#define V4L2_MXC_ROTATE_90_RIGHT 4
47#define V4L2_MXC_ROTATE_90_RIGHT_VFLIP 5
48#define V4L2_MXC_ROTATE_90_RIGHT_HFLIP 6
49#define V4L2_MXC_ROTATE_90_LEFT 7
50
51struct v4l2_mxc_offset {
52 uint32_t u_offset;
53 uint32_t v_offset;
54};
55
56#endif