2 * Copyright (C) 2011-2012 Freescale Semiconductor, Inc. All Rights Reserved.
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:
10 * http://www.opensource.org/licenses/gpl-license.html
11 * http://www.gnu.org/copyleft/gpl.html
14 #include <linux/init.h>
15 #include <linux/module.h>
16 #include <linux/vmalloc.h>
17 #include <linux/sched.h>
18 #include <linux/types.h>
19 #include <linux/platform_device.h>
20 #include <linux/dma-mapping.h>
21 #include <linux/videodev2.h>
22 #include <linux/mxcfb.h>
23 #include <linux/console.h>
24 #include <linux/mxc_v4l2.h>
25 #include <mach/ipu-v3.h>
27 #include <media/videobuf-dma-contig.h>
28 #include <media/v4l2-device.h>
29 #include <media/v4l2-ioctl.h>
33 #define VALID_HEIGHT_1080P (1080)
34 #define FRAME_HEIGHT_1080P (1088)
35 #define FRAME_WIDTH_1080P (1920)
36 #define MAX_INTERLACED_WIDTH (1024)
37 #define CHECK_TILED_1080P_DISPLAY(vout) \
38 (((vout)->task.input.format == IPU_PIX_FMT_TILED_NV12) && \
39 ((vout)->task.input.width == FRAME_WIDTH_1080P) && \
40 ((vout)->task.output.crop.w == FRAME_WIDTH_1080P) && \
41 ((vout)->task.input.height == FRAME_HEIGHT_1080P) && \
42 ((vout)->task.output.crop.h == VALID_HEIGHT_1080P))
43 #define CHECK_TILED_1080P_STREAM(vout) \
44 (((vout)->task.input.format == IPU_PIX_FMT_TILED_NV12) && \
45 ((vout)->task.input.width == FRAME_WIDTH_1080P) && \
46 ((vout)->task.input.crop.w == FRAME_WIDTH_1080P) && \
47 ((vout)->task.input.height == FRAME_HEIGHT_1080P) && \
48 ((vout)->task.input.crop.h == FRAME_HEIGHT_1080P))
53 struct v4l2_rect crop_bounds;
54 unsigned int disp_fmt;
55 bool disp_support_csc;
56 bool disp_support_windows;
59 struct mxc_vout_output {
62 struct video_device *vfd;
64 struct mutex task_lock;
65 enum v4l2_buf_type type;
67 struct videobuf_queue vbq;
70 struct list_head queue_list;
71 struct list_head active_list;
73 struct v4l2_rect crop_bounds;
74 unsigned int disp_fmt;
75 struct mxcfb_pos win_pos;
76 bool disp_support_windows;
77 bool disp_support_csc;
83 struct ipu_task vdoa_task;
91 struct timer_list timer;
92 struct workqueue_struct *v4l_wq;
93 struct work_struct disp_work;
94 unsigned long frame_count;
95 unsigned long start_jiffies;
101 dma_addr_t disp_bufs[FB_BUFS];
103 struct videobuf_buffer *pre_vb;
106 struct mxc_vout_dev {
108 struct v4l2_device v4l2_dev;
109 struct mxc_vout_output *out[MAX_FB_NUM];
113 /* Driver Configuration macros */
114 #define VOUT_NAME "mxc_vout"
116 /* Variables configurable through module params*/
118 static int video_nr = 16;
120 /* Module parameters */
121 module_param(video_nr, int, S_IRUGO);
122 MODULE_PARM_DESC(video_nr, "video device numbers");
123 module_param(debug, int, 0600);
124 MODULE_PARM_DESC(debug, "Debug level (0-1)");
126 const static struct v4l2_fmtdesc mxc_formats[] = {
128 .description = "RGB565",
129 .pixelformat = V4L2_PIX_FMT_RGB565,
132 .description = "BGR24",
133 .pixelformat = V4L2_PIX_FMT_BGR24,
136 .description = "RGB24",
137 .pixelformat = V4L2_PIX_FMT_RGB24,
140 .description = "RGB32",
141 .pixelformat = V4L2_PIX_FMT_RGB32,
144 .description = "BGR32",
145 .pixelformat = V4L2_PIX_FMT_BGR32,
148 .description = "NV12",
149 .pixelformat = V4L2_PIX_FMT_NV12,
152 .description = "UYVY",
153 .pixelformat = V4L2_PIX_FMT_UYVY,
156 .description = "YUYV",
157 .pixelformat = V4L2_PIX_FMT_YUYV,
160 .description = "YUV422 planar",
161 .pixelformat = V4L2_PIX_FMT_YUV422P,
164 .description = "YUV444",
165 .pixelformat = V4L2_PIX_FMT_YUV444,
168 .description = "YUV420",
169 .pixelformat = V4L2_PIX_FMT_YUV420,
172 .description = "TILED NV12P",
173 .pixelformat = IPU_PIX_FMT_TILED_NV12,
176 .description = "TILED NV12F",
177 .pixelformat = IPU_PIX_FMT_TILED_NV12F,
181 #define NUM_MXC_VOUT_FORMATS (ARRAY_SIZE(mxc_formats))
183 #define DEF_INPUT_WIDTH 320
184 #define DEF_INPUT_HEIGHT 240
186 static int mxc_vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i);
188 static struct mxc_vout_fb g_fb_setting[MAX_FB_NUM];
189 static int config_disp_output(struct mxc_vout_output *vout);
190 static void release_disp_output(struct mxc_vout_output *vout);
192 static unsigned int get_frame_size(struct mxc_vout_output *vout)
196 if (IPU_PIX_FMT_TILED_NV12 == vout->task.input.format)
197 size = TILED_NV12_FRAME_SIZE(vout->task.input.width,
198 vout->task.input.height);
199 else if (IPU_PIX_FMT_TILED_NV12F == vout->task.input.format) {
200 size = TILED_NV12_FRAME_SIZE(vout->task.input.width,
201 vout->task.input.height/2);
204 size = vout->task.input.width * vout->task.input.height *
205 fmt_to_bpp(vout->task.input.format)/8;
210 static ipu_channel_t get_ipu_channel(struct fb_info *fbi)
212 ipu_channel_t ipu_ch = CHAN_NONE;
215 if (fbi->fbops->fb_ioctl) {
218 fbi->fbops->fb_ioctl(fbi, MXCFB_GET_FB_IPU_CHAN,
219 (unsigned long)&ipu_ch);
226 static unsigned int get_ipu_fmt(struct fb_info *fbi)
231 if (fbi->fbops->fb_ioctl) {
234 fbi->fbops->fb_ioctl(fbi, MXCFB_GET_DIFMT,
235 (unsigned long)&fb_fmt);
242 static void update_display_setting(void)
246 struct v4l2_rect bg_crop_bounds[2];
248 for (i = 0; i < num_registered_fb; i++) {
249 fbi = registered_fb[i];
251 memset(&g_fb_setting[i], 0, sizeof(struct mxc_vout_fb));
253 if (!strncmp(fbi->fix.id, "DISP3", 5))
254 g_fb_setting[i].ipu_id = 0;
256 g_fb_setting[i].ipu_id = 1;
258 g_fb_setting[i].name = fbi->fix.id;
259 g_fb_setting[i].crop_bounds.left = 0;
260 g_fb_setting[i].crop_bounds.top = 0;
261 g_fb_setting[i].crop_bounds.width = fbi->var.xres;
262 g_fb_setting[i].crop_bounds.height = fbi->var.yres;
263 g_fb_setting[i].disp_fmt = get_ipu_fmt(fbi);
265 if (get_ipu_channel(fbi) == MEM_BG_SYNC) {
266 bg_crop_bounds[g_fb_setting[i].ipu_id] =
267 g_fb_setting[i].crop_bounds;
268 g_fb_setting[i].disp_support_csc = true;
269 } else if (get_ipu_channel(fbi) == MEM_FG_SYNC) {
270 g_fb_setting[i].disp_support_csc = true;
271 g_fb_setting[i].disp_support_windows = true;
275 for (i = 0; i < num_registered_fb; i++) {
276 fbi = registered_fb[i];
278 if (get_ipu_channel(fbi) == MEM_FG_SYNC)
279 g_fb_setting[i].crop_bounds =
280 bg_crop_bounds[g_fb_setting[i].ipu_id];
284 /* called after g_fb_setting filled by update_display_setting */
285 static int update_setting_from_fbi(struct mxc_vout_output *vout,
291 for (i = 0; i < MAX_FB_NUM; i++) {
292 if (g_fb_setting[i].name) {
293 if (!strcmp(fbi->fix.id, g_fb_setting[i].name)) {
294 vout->crop_bounds = g_fb_setting[i].crop_bounds;
295 vout->disp_fmt = g_fb_setting[i].disp_fmt;
296 vout->disp_support_csc = g_fb_setting[i].disp_support_csc;
297 vout->disp_support_windows =
298 g_fb_setting[i].disp_support_windows;
306 v4l2_err(vout->vfd->v4l2_dev, "can not find output\n");
309 strlcpy(vout->vfd->name, fbi->fix.id, sizeof(vout->vfd->name));
311 memset(&vout->task, 0, sizeof(struct ipu_task));
313 vout->task.input.width = DEF_INPUT_WIDTH;
314 vout->task.input.height = DEF_INPUT_HEIGHT;
315 vout->task.input.crop.pos.x = 0;
316 vout->task.input.crop.pos.y = 0;
317 vout->task.input.crop.w = DEF_INPUT_WIDTH;
318 vout->task.input.crop.h = DEF_INPUT_HEIGHT;
320 vout->task.output.width = vout->crop_bounds.width;
321 vout->task.output.height = vout->crop_bounds.height;
322 vout->task.output.crop.pos.x = 0;
323 vout->task.output.crop.pos.y = 0;
324 vout->task.output.crop.w = vout->crop_bounds.width;
325 vout->task.output.crop.h = vout->crop_bounds.height;
326 if (colorspaceofpixel(vout->disp_fmt) == YUV_CS)
327 vout->task.output.format = IPU_PIX_FMT_UYVY;
329 vout->task.output.format = IPU_PIX_FMT_RGB565;
334 static inline unsigned long get_jiffies(struct timeval *t)
338 if (t->tv_usec >= 1000000) {
339 t->tv_sec += t->tv_usec / 1000000;
340 t->tv_usec = t->tv_usec % 1000000;
343 do_gettimeofday(&cur);
344 if ((t->tv_sec < cur.tv_sec)
345 || ((t->tv_sec == cur.tv_sec) && (t->tv_usec < cur.tv_usec)))
348 if (t->tv_usec < cur.tv_usec) {
349 cur.tv_sec = t->tv_sec - cur.tv_sec - 1;
350 cur.tv_usec = t->tv_usec + 1000000 - cur.tv_usec;
352 cur.tv_sec = t->tv_sec - cur.tv_sec;
353 cur.tv_usec = t->tv_usec - cur.tv_usec;
356 return jiffies + timeval_to_jiffies(&cur);
359 static bool deinterlace_3_field(struct mxc_vout_output *vout)
361 return (vout->task.input.deinterlace.enable &&
362 (vout->task.input.deinterlace.motion != HIGH_MOTION));
365 static bool is_pp_bypass(struct mxc_vout_output *vout)
367 if ((IPU_PIX_FMT_TILED_NV12 == vout->task.input.format) ||
368 (IPU_PIX_FMT_TILED_NV12F == vout->task.input.format))
370 if ((vout->task.input.width == vout->task.output.width) &&
371 (vout->task.input.height == vout->task.output.height) &&
372 (vout->task.input.crop.w == vout->task.output.crop.w) &&
373 (vout->task.input.crop.h == vout->task.output.crop.h) &&
374 (vout->task.output.rotate < IPU_ROTATE_HORIZ_FLIP) &&
375 !vout->task.input.deinterlace.enable) {
376 if (vout->disp_support_csc)
378 else if (!need_csc(vout->task.input.format, vout->disp_fmt))
380 /* input crop show to full output which can show based on xres_virtual/yres_virtual */
381 } else if ((vout->task.input.crop.w == vout->task.output.crop.w) &&
382 (vout->task.output.crop.w == vout->task.output.width) &&
383 (vout->task.input.crop.h == vout->task.output.crop.h) &&
384 (vout->task.output.crop.h == vout->task.output.height) &&
385 (vout->task.output.rotate < IPU_ROTATE_HORIZ_FLIP) &&
386 !vout->task.input.deinterlace.enable) {
387 if (vout->disp_support_csc)
389 else if (!need_csc(vout->task.input.format, vout->disp_fmt))
395 static void setup_buf_timer(struct mxc_vout_output *vout,
396 struct videobuf_buffer *vb)
398 unsigned long timeout;
400 /* if timestamp is 0, then default to 30fps */
401 if ((vb->ts.tv_sec == 0)
402 && (vb->ts.tv_usec == 0)
403 && vout->start_jiffies)
405 vout->start_jiffies + vout->frame_count * HZ / 30;
407 timeout = get_jiffies(&vb->ts);
409 if (jiffies >= timeout) {
410 v4l2_dbg(1, debug, vout->vfd->v4l2_dev,
411 "warning: timer timeout already expired.\n");
414 if (mod_timer(&vout->timer, timeout)) {
415 v4l2_warn(vout->vfd->v4l2_dev,
416 "warning: timer was already set\n");
419 v4l2_dbg(1, debug, vout->vfd->v4l2_dev,
420 "timer handler next schedule: %lu\n", timeout);
423 static int show_buf(struct mxc_vout_output *vout, int idx,
424 struct ipu_pos *ipos)
426 struct fb_info *fbi = vout->fbi;
427 struct fb_var_screeninfo var;
432 memcpy(&var, &fbi->var, sizeof(var));
434 if (vout->bypass_pp) {
437 * NOTE: should not do other fb operation during v4l2
440 fbi->fix.smem_start = vout->task.output.paddr;
441 fbi->var.yoffset = ipos->y + 1;
442 var.xoffset = ipos->x;
443 var.yoffset = ipos->y;
444 ret = fb_pan_display(fbi, &var);
448 is_1080p = CHECK_TILED_1080P_DISPLAY(vout);
450 yres = fbi->var.yres;
451 fbi->var.yres = FRAME_HEIGHT_1080P;
454 var.yoffset = idx * fbi->var.yres;
455 ret = fb_pan_display(fbi, &var);
457 fbi->var.yres = yres;
464 static void disp_work_func(struct work_struct *work)
466 struct mxc_vout_output *vout =
467 container_of(work, struct mxc_vout_output, disp_work);
468 struct videobuf_queue *q = &vout->vbq;
469 struct videobuf_buffer *vb, *vb_next = NULL;
470 unsigned long flags = 0;
475 u32 tiled_interlaced = 0;
477 v4l2_dbg(1, debug, vout->vfd->v4l2_dev, "disp work begin one frame\n");
479 spin_lock_irqsave(q->irqlock, flags);
481 if (deinterlace_3_field(vout)) {
482 if (list_is_singular(&vout->active_list)) {
483 v4l2_warn(vout->vfd->v4l2_dev,
484 "deinterlacing: no enough entry in active_list\n");
485 spin_unlock_irqrestore(q->irqlock, flags);
489 if (list_empty(&vout->active_list)) {
490 v4l2_warn(vout->vfd->v4l2_dev,
491 "no entry in active_list, should not be here\n");
492 spin_unlock_irqrestore(q->irqlock, flags);
496 vb = list_first_entry(&vout->active_list,
497 struct videobuf_buffer, queue);
499 if (deinterlace_3_field(vout))
500 vb_next = list_first_entry(vout->active_list.next,
501 struct videobuf_buffer, queue);
503 spin_unlock_irqrestore(q->irqlock, flags);
505 mutex_lock(&vout->task_lock);
507 if (vb->memory == V4L2_MEMORY_USERPTR)
508 vout->task.input.paddr = vb->baddr;
510 vout->task.input.paddr = videobuf_to_dma_contig(vb);
512 if (vout->bypass_pp) {
513 vout->task.output.paddr = vout->task.input.paddr;
514 ipos.x = vout->task.input.crop.pos.x;
515 ipos.y = vout->task.input.crop.pos.y;
517 if (deinterlace_3_field(vout)) {
518 if (vb->memory == V4L2_MEMORY_USERPTR)
519 vout->task.input.paddr_n = vb_next->baddr;
521 vout->task.input.paddr_n =
522 videobuf_to_dma_contig(vb_next);
524 vout->task.output.paddr =
525 vout->disp_bufs[vout->frame_count % FB_BUFS];
527 is_1080p = CHECK_TILED_1080P_DISPLAY(vout);
529 vout->task.input.crop.h = FRAME_HEIGHT_1080P;
530 vout->task.output.height = FRAME_HEIGHT_1080P;
531 ocrop_h = vout->task.output.crop.h;
532 vout->task.output.crop.h = FRAME_HEIGHT_1080P;
534 if (vout->is_vdoaipu_task) {
535 vout->vdoa_task.input.paddr = vout->task.input.paddr;
536 if (deinterlace_3_field(vout))
537 vout->vdoa_task.input.paddr_n =
538 vout->task.input.paddr_n;
539 vout->vdoa_task.output.paddr = vout->vdoa_dma.paddr;
540 ret = ipu_queue_task(&vout->vdoa_task);
542 mutex_unlock(&vout->task_lock);
545 vout->task.input.paddr = vout->vdoa_task.output.paddr;
546 if (vout->task.input.deinterlace.enable) {
547 tiled_interlaced = 1;
548 vout->task.input.deinterlace.enable = 0;
551 ret = ipu_queue_task(&vout->task);
552 if (tiled_interlaced)
553 vout->task.input.deinterlace.enable = 1;
555 mutex_unlock(&vout->task_lock);
559 vout->task.output.crop.h = ocrop_h;
562 mutex_unlock(&vout->task_lock);
564 ret = show_buf(vout, vout->frame_count % FB_BUFS, &ipos);
566 v4l2_dbg(1, debug, vout->vfd->v4l2_dev, "show buf with ret %d\n", ret);
568 spin_lock_irqsave(q->irqlock, flags);
570 list_del(&vb->queue);
573 * previous videobuf finish show, set VIDEOBUF_DONE state here
574 * to avoid tearing issue in pp bypass case, which make sure
575 * showing buffer will not be dequeue to write new data. It also
576 * bring side-effect that the last buffer can not be dequeue
577 * correctly, app need take care about it.
580 vout->pre_vb->state = VIDEOBUF_DONE;
581 wake_up_interruptible(&vout->pre_vb->done);
588 vb->state = VIDEOBUF_DONE;
589 wake_up_interruptible(&vb->done);
594 /* pick next queue buf to setup timer */
595 if (list_empty(&vout->queue_list))
596 vout->timer_stop = true;
598 vb = list_first_entry(&vout->queue_list,
599 struct videobuf_buffer, queue);
600 setup_buf_timer(vout, vb);
603 spin_unlock_irqrestore(q->irqlock, flags);
605 v4l2_dbg(1, debug, vout->vfd->v4l2_dev, "disp work finish one frame\n");
609 v4l2_err(vout->vfd->v4l2_dev, "display work fail ret = %d\n", ret);
610 vout->timer_stop = true;
611 vb->state = VIDEOBUF_ERROR;
615 static void mxc_vout_timer_handler(unsigned long arg)
617 struct mxc_vout_output *vout =
618 (struct mxc_vout_output *) arg;
619 struct videobuf_queue *q = &vout->vbq;
620 struct videobuf_buffer *vb;
621 unsigned long flags = 0;
623 spin_lock_irqsave(q->irqlock, flags);
626 * put first queued entry into active, if previous entry did not
627 * finish, setup current entry's timer again.
629 if (list_empty(&vout->queue_list)) {
630 spin_unlock_irqrestore(q->irqlock, flags);
634 /* move videobuf from queued list to active list */
635 vb = list_first_entry(&vout->queue_list,
636 struct videobuf_buffer, queue);
637 list_del(&vb->queue);
638 list_add_tail(&vb->queue, &vout->active_list);
640 if (queue_work(vout->v4l_wq, &vout->disp_work) == 0) {
641 v4l2_warn(vout->vfd->v4l2_dev,
642 "disp work was in queue already, queue buf again next time\n");
643 list_del(&vb->queue);
644 list_add(&vb->queue, &vout->queue_list);
645 spin_unlock_irqrestore(q->irqlock, flags);
649 vb->state = VIDEOBUF_ACTIVE;
651 spin_unlock_irqrestore(q->irqlock, flags);
654 /* Video buffer call backs */
657 * Buffer setup function is called by videobuf layer when REQBUF ioctl is
658 * called. This is used to setup buffers and return size and count of
659 * buffers allocated. After the call to this buffer, videobuf layer will
660 * setup buffer queue depending on the size and count of buffers
662 static int mxc_vout_buffer_setup(struct videobuf_queue *q, unsigned int *count,
665 struct mxc_vout_output *vout = q->priv_data;
666 unsigned int frame_size;
671 if (V4L2_BUF_TYPE_VIDEO_OUTPUT != q->type)
674 frame_size = get_frame_size(vout);
675 *size = PAGE_ALIGN(frame_size);
681 * This function will be called when VIDIOC_QBUF ioctl is called.
682 * It prepare buffers before give out for the display. This function
683 * converts user space virtual address into physical address if userptr memory
684 * exchange mechanism is used.
686 static int mxc_vout_buffer_prepare(struct videobuf_queue *q,
687 struct videobuf_buffer *vb,
688 enum v4l2_field field)
690 vb->state = VIDEOBUF_PREPARED;
695 * Buffer queue funtion will be called from the videobuf layer when _QBUF
696 * ioctl is called. It is used to enqueue buffer, which is ready to be
698 * This function is protected by q->irqlock.
700 static void mxc_vout_buffer_queue(struct videobuf_queue *q,
701 struct videobuf_buffer *vb)
703 struct mxc_vout_output *vout = q->priv_data;
705 list_add_tail(&vb->queue, &vout->queue_list);
706 vb->state = VIDEOBUF_QUEUED;
708 if (vout->timer_stop) {
709 if (deinterlace_3_field(vout) &&
710 list_empty(&vout->active_list)) {
711 vb = list_first_entry(&vout->queue_list,
712 struct videobuf_buffer, queue);
713 list_del(&vb->queue);
714 list_add_tail(&vb->queue, &vout->active_list);
716 setup_buf_timer(vout, vb);
717 vout->timer_stop = false;
723 * Buffer release function is called from videobuf layer to release buffer
724 * which are already allocated
726 static void mxc_vout_buffer_release(struct videobuf_queue *q,
727 struct videobuf_buffer *vb)
729 vb->state = VIDEOBUF_NEEDS_INIT;
732 static int mxc_vout_mmap(struct file *file, struct vm_area_struct *vma)
735 struct mxc_vout_output *vout = file->private_data;
740 ret = videobuf_mmap_mapper(&vout->vbq, vma);
742 v4l2_err(vout->vfd->v4l2_dev,
743 "offset invalid [offset=0x%lx]\n",
744 (vma->vm_pgoff << PAGE_SHIFT));
749 static int mxc_vout_release(struct file *file)
751 unsigned int ret = 0;
752 struct videobuf_queue *q;
753 struct mxc_vout_output *vout = file->private_data;
758 if (--vout->open_cnt == 0) {
761 mxc_vidioc_streamoff(file, vout, vout->type);
763 release_disp_output(vout);
764 videobuf_queue_cancel(q);
766 destroy_workqueue(vout->v4l_wq);
767 ret = videobuf_mmap_free(q);
773 static int mxc_vout_open(struct file *file)
775 struct mxc_vout_output *vout = NULL;
778 vout = video_drvdata(file);
783 if (vout->open_cnt++ == 0) {
784 vout->ctrl_rotate = 0;
785 vout->ctrl_vflip = 0;
786 vout->ctrl_hflip = 0;
787 update_display_setting();
788 ret = update_setting_from_fbi(vout, vout->fbi);
792 vout->v4l_wq = create_singlethread_workqueue("v4l2q");
794 v4l2_err(vout->vfd->v4l2_dev,
795 "Could not create work queue\n");
800 INIT_WORK(&vout->disp_work, disp_work_func);
802 INIT_LIST_HEAD(&vout->queue_list);
803 INIT_LIST_HEAD(&vout->active_list);
805 vout->fmt_init = false;
806 vout->frame_count = 0;
812 file->private_data = vout;
821 static int mxc_vidioc_querycap(struct file *file, void *fh,
822 struct v4l2_capability *cap)
824 struct mxc_vout_output *vout = fh;
826 strlcpy(cap->driver, VOUT_NAME, sizeof(cap->driver));
827 strlcpy(cap->card, vout->vfd->name, sizeof(cap->card));
828 cap->bus_info[0] = '\0';
829 cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OUTPUT;
834 static int mxc_vidioc_enum_fmt_vid_out(struct file *file, void *fh,
835 struct v4l2_fmtdesc *fmt)
837 if (fmt->index >= NUM_MXC_VOUT_FORMATS)
840 strlcpy(fmt->description, mxc_formats[fmt->index].description,
841 sizeof(fmt->description));
842 fmt->pixelformat = mxc_formats[fmt->index].pixelformat;
847 static int mxc_vidioc_g_fmt_vid_out(struct file *file, void *fh,
848 struct v4l2_format *f)
850 struct mxc_vout_output *vout = fh;
851 struct v4l2_rect rect;
853 f->fmt.pix.width = vout->task.input.width;
854 f->fmt.pix.height = vout->task.input.height;
855 f->fmt.pix.pixelformat = vout->task.input.format;
856 f->fmt.pix.sizeimage = get_frame_size(vout);
858 if (f->fmt.pix.priv) {
859 rect.left = vout->task.input.crop.pos.x;
860 rect.top = vout->task.input.crop.pos.y;
861 rect.width = vout->task.input.crop.w;
862 rect.height = vout->task.input.crop.h;
863 if (copy_to_user((void __user *)f->fmt.pix.priv,
864 &rect, sizeof(rect)))
867 v4l2_dbg(1, debug, vout->vfd->v4l2_dev,
868 "frame_size:0x%x, pix_fmt:0x%x\n",
869 f->fmt.pix.sizeimage,
870 vout->task.input.format);
875 static inline int ipu_try_task(struct mxc_vout_output *vout)
878 struct ipu_task *task = &vout->task;
881 ret = ipu_check_task(task);
882 if (ret != IPU_CHECK_OK) {
883 if (ret > IPU_CHECK_ERR_MIN) {
884 if (ret == IPU_CHECK_ERR_SPLIT_INPUTW_OVER) {
885 task->input.crop.w -= 8;
888 if (ret == IPU_CHECK_ERR_SPLIT_INPUTH_OVER) {
889 task->input.crop.h -= 8;
892 if (ret == IPU_CHECK_ERR_SPLIT_OUTPUTW_OVER) {
893 if (vout->disp_support_windows) {
894 task->output.width -= 8;
895 task->output.crop.w = task->output.width;
897 task->output.crop.w -= 8;
900 if (ret == IPU_CHECK_ERR_SPLIT_OUTPUTH_OVER) {
901 if (vout->disp_support_windows) {
902 task->output.height -= 8;
903 task->output.crop.h = task->output.height;
905 task->output.crop.h -= 8;
916 static inline int vdoaipu_try_task(struct mxc_vout_output *vout)
919 u32 icrop_h = 0, icrop_w = 0;
922 struct ipu_task *ipu_task = &vout->task;
923 struct ipu_task *vdoa_task = &vout->vdoa_task;
926 if (vout->task.input.deinterlace.enable)
928 is_1080p_stream = CHECK_TILED_1080P_STREAM(vout);
930 ipu_task->input.crop.h = VALID_HEIGHT_1080P;
932 if (ipu_task->input.crop.h % IPU_PIX_FMT_TILED_NV12_MBALIGN) {
933 icrop_h = ipu_task->input.crop.h;
934 ipu_task->input.crop.h = ALIGN(ipu_task->input.crop.h,
935 IPU_PIX_FMT_TILED_NV12_MBALIGN);
937 if (ipu_task->input.crop.w % IPU_PIX_FMT_TILED_NV12_MBALIGN) {
938 icrop_w = ipu_task->input.crop.w;
939 ipu_task->input.crop.w = ALIGN(ipu_task->input.crop.w,
940 IPU_PIX_FMT_TILED_NV12_MBALIGN);
943 memset(vdoa_task, 0, sizeof(*vdoa_task));
944 memcpy(&vdoa_task->input, &ipu_task->input, sizeof(ipu_task->input));
945 vdoa_task->output.format = IPU_PIX_FMT_NV12;
946 vdoa_task->output.width = ipu_task->input.crop.w;
947 vdoa_task->output.height = ipu_task->input.crop.h;
948 vdoa_task->output.crop.w = ipu_task->input.crop.w;
949 vdoa_task->output.crop.h = ipu_task->input.crop.h;
951 size = PAGE_ALIGN(ipu_task->input.crop.w *
952 ipu_task->input.crop.h *
953 fmt_to_bpp(vdoa_task->output.format)/8);
954 if (size > vout->vdoa_dma.size) {
955 if (vout->vdoa_dma.vaddr) {
956 dma_free_coherent(vout->vbq.dev, vout->vdoa_dma.size,
957 vout->vdoa_dma.vaddr, vout->vdoa_dma.paddr);
958 v4l2_dbg(1, debug, vout->vfd->v4l2_dev,
959 "free vdoa_dma.size:0x%x, paddr:0x%x\n",
961 vout->vdoa_dma.paddr);
962 memset(&vout->vdoa_dma, 0, sizeof(vout->vdoa_dma));
964 vout->vdoa_dma.size = size;
965 vout->vdoa_dma.vaddr = dma_alloc_coherent(vout->vbq.dev,
967 &vout->vdoa_dma.paddr,
969 if (!vout->vdoa_dma.vaddr) {
970 v4l2_err(vout->vfd->v4l2_dev,
971 "cannot get vdoa dma buf size:0x%x\n", size);
974 v4l2_dbg(1, debug, vout->vfd->v4l2_dev,
975 "alloc vdoa_dma.size:0x%x, paddr:0x%x\n",
977 vout->vdoa_dma.paddr);
979 ret = ipu_check_task(vdoa_task);
980 if (ret != IPU_CHECK_OK)
983 ipu_task->input.format = vdoa_task->output.format;
985 ipu_task->input.height = vdoa_task->output.height;
986 ipu_task->input.crop.h = icrop_h;
989 ipu_task->input.width = vdoa_task->output.width;
990 ipu_task->input.crop.w = icrop_w;
993 ipu_task->input.deinterlace.enable = 0;
994 ret = ipu_try_task(vout);
996 ipu_task->input.deinterlace.enable = 1;
1001 static int mxc_vout_try_task(struct mxc_vout_output *vout)
1004 struct ipu_output *output = &vout->task.output;
1005 struct ipu_input *input = &vout->task.input;
1007 input->crop.w -= input->crop.w%8;
1008 input->crop.h -= input->crop.h%8;
1009 /* assume task.output already set by S_CROP */
1010 if (is_pp_bypass(vout)) {
1011 v4l2_info(vout->vfd->v4l2_dev, "Bypass IC.\n");
1012 vout->bypass_pp = true;
1013 output->format = input->format;
1015 /* if need CSC, choose IPU-DP or IPU_IC do it */
1016 vout->bypass_pp = false;
1017 if (vout->disp_support_csc) {
1018 if (colorspaceofpixel(input->format) == YUV_CS)
1019 output->format = IPU_PIX_FMT_UYVY;
1021 output->format = IPU_PIX_FMT_RGB565;
1023 if (colorspaceofpixel(vout->disp_fmt) == YUV_CS)
1024 output->format = IPU_PIX_FMT_UYVY;
1026 output->format = IPU_PIX_FMT_RGB565;
1029 vout->is_vdoaipu_task = 0;
1030 if ((IPU_PIX_FMT_TILED_NV12 == input->format) ||
1031 (IPU_PIX_FMT_TILED_NV12F == input->format)) {
1032 /* check resize/rotate/flip, or csc task */
1033 if ((IPU_ROTATE_NONE != output->rotate) ||
1034 (input->crop.w != output->crop.w) ||
1035 (input->crop.h != output->crop.h) ||
1036 (!vout->disp_support_csc &&
1037 (colorspaceofpixel(vout->disp_fmt) == RGB_CS)))
1038 vout->is_vdoaipu_task = 1;
1041 output->format = IPU_PIX_FMT_NV12;
1044 if (vout->is_vdoaipu_task)
1045 ret = vdoaipu_try_task(vout);
1047 ret = ipu_try_task(vout);
1053 static int mxc_vout_try_format(struct mxc_vout_output *vout, struct v4l2_format *f)
1056 struct v4l2_rect rect;
1061 if (f->fmt.pix.priv && copy_from_user(&rect,
1062 (void __user *)f->fmt.pix.priv, sizeof(rect)))
1065 vout->task.input.width = f->fmt.pix.width;
1066 vout->task.input.height = f->fmt.pix.height;
1067 vout->task.input.format = f->fmt.pix.pixelformat;
1069 if (IPU_PIX_FMT_TILED_NV12F == vout->task.input.format) {
1070 if ((vout->task.input.width > MAX_INTERLACED_WIDTH) ||
1071 (vout->task.input.deinterlace.motion == HIGH_MOTION))
1073 v4l2_info(vout->vfd->v4l2_dev,
1074 "tiled fmt enable deinterlace.\n");
1075 vout->task.input.deinterlace.enable = true;
1076 vout->task.input.deinterlace.field_fmt =
1077 IPU_DEINTERLACE_FIELD_TOP;
1079 switch (f->fmt.pix.field) {
1080 /* Images are in progressive format, not interlaced */
1081 case V4L2_FIELD_NONE:
1083 /* The two fields of a frame are passed in separate buffers,
1084 in temporal order, i. e. the older one first. */
1085 case V4L2_FIELD_ALTERNATE:
1086 v4l2_err(vout->vfd->v4l2_dev,
1087 "V4L2_FIELD_ALTERNATE field format not supported yet!\n");
1089 case V4L2_FIELD_INTERLACED_TB:
1090 v4l2_info(vout->vfd->v4l2_dev, "Enable deinterlace TB.\n");
1091 vout->task.input.deinterlace.enable = true;
1092 vout->task.input.deinterlace.field_fmt =
1093 IPU_DEINTERLACE_FIELD_TOP;
1095 case V4L2_FIELD_INTERLACED_BT:
1096 v4l2_info(vout->vfd->v4l2_dev, "Enable deinterlace BT.\n");
1097 vout->task.input.deinterlace.enable = true;
1098 vout->task.input.deinterlace.field_fmt =
1099 IPU_DEINTERLACE_FIELD_BOTTOM;
1105 if (f->fmt.pix.priv) {
1106 vout->task.input.crop.pos.x = rect.left;
1107 vout->task.input.crop.pos.y = rect.top;
1108 vout->task.input.crop.w = rect.width;
1109 vout->task.input.crop.h = rect.height;
1111 vout->task.input.crop.pos.x = 0;
1112 vout->task.input.crop.pos.y = 0;
1113 vout->task.input.crop.w = f->fmt.pix.width;
1114 vout->task.input.crop.h = f->fmt.pix.height;
1117 is_1080p = CHECK_TILED_1080P_DISPLAY(vout);
1119 vout->task.input.crop.h = FRAME_HEIGHT_1080P;
1120 o_height = vout->task.output.height;
1121 ocrop_h = vout->task.output.crop.h;
1122 vout->task.output.height = FRAME_HEIGHT_1080P;
1123 vout->task.output.crop.h = FRAME_HEIGHT_1080P;
1126 ret = mxc_vout_try_task(vout);
1128 if (f->fmt.pix.priv) {
1129 rect.width = vout->task.input.crop.w;
1130 rect.height = vout->task.input.crop.h;
1131 if (copy_to_user((void __user *)f->fmt.pix.priv,
1132 &rect, sizeof(rect)))
1135 f->fmt.pix.width = vout->task.input.crop.w;
1136 f->fmt.pix.height = vout->task.input.crop.h;
1141 vout->task.output.height = o_height;
1142 vout->task.output.crop.h = ocrop_h;
1148 static int mxc_vidioc_s_fmt_vid_out(struct file *file, void *fh,
1149 struct v4l2_format *f)
1151 struct mxc_vout_output *vout = fh;
1154 if (vout->vbq.streaming)
1157 mutex_lock(&vout->task_lock);
1158 ret = mxc_vout_try_format(vout, f);
1160 vout->fmt_init = true;
1161 mutex_unlock(&vout->task_lock);
1166 static int mxc_vidioc_cropcap(struct file *file, void *fh,
1167 struct v4l2_cropcap *cropcap)
1169 struct mxc_vout_output *vout = fh;
1171 if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
1174 cropcap->bounds = vout->crop_bounds;
1175 cropcap->defrect = vout->crop_bounds;
1180 static int mxc_vidioc_g_crop(struct file *file, void *fh, struct v4l2_crop *crop)
1182 struct mxc_vout_output *vout = fh;
1184 if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
1187 if (vout->disp_support_windows) {
1188 crop->c.left = vout->win_pos.x;
1189 crop->c.top = vout->win_pos.y;
1190 crop->c.width = vout->task.output.width;
1191 crop->c.height = vout->task.output.height;
1193 if (vout->task.output.crop.w && vout->task.output.crop.h) {
1194 crop->c.left = vout->task.output.crop.pos.x;
1195 crop->c.top = vout->task.output.crop.pos.y;
1196 crop->c.width = vout->task.output.crop.w;
1197 crop->c.height = vout->task.output.crop.h;
1201 crop->c.width = vout->task.output.width;
1202 crop->c.height = vout->task.output.height;
1209 static int mxc_vidioc_s_crop(struct file *file, void *fh, struct v4l2_crop *crop)
1211 struct mxc_vout_output *vout = fh;
1212 struct v4l2_rect *b = &vout->crop_bounds;
1215 if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
1218 if (crop->c.width < 0 || crop->c.height < 0)
1221 if (crop->c.width == 0)
1222 crop->c.width = b->width - b->left;
1223 if (crop->c.height == 0)
1224 crop->c.height = b->height - b->top;
1226 if (crop->c.top < b->top)
1227 crop->c.top = b->top;
1228 if (crop->c.top >= b->top + b->height)
1229 crop->c.top = b->top + b->height - 1;
1230 if (crop->c.height > b->top - crop->c.top + b->height)
1232 b->top - crop->c.top + b->height;
1234 if (crop->c.left < b->left)
1235 crop->c.left = b->left;
1236 if (crop->c.left >= b->left + b->width)
1237 crop->c.left = b->left + b->width - 1;
1238 if (crop->c.width > b->left - crop->c.left + b->width)
1240 b->left - crop->c.left + b->width;
1242 /* stride line limitation */
1243 crop->c.height -= crop->c.height % 8;
1244 crop->c.width -= crop->c.width % 8;
1246 /* the same setting, return */
1247 if (vout->disp_support_windows) {
1248 if ((vout->win_pos.x == crop->c.left) &&
1249 (vout->win_pos.y == crop->c.top) &&
1250 (vout->task.output.crop.w == crop->c.width) &&
1251 (vout->task.output.crop.h == crop->c.height))
1254 if ((vout->task.output.crop.pos.x == crop->c.left) &&
1255 (vout->task.output.crop.pos.y == crop->c.top) &&
1256 (vout->task.output.crop.w == crop->c.width) &&
1257 (vout->task.output.crop.h == crop->c.height))
1261 /* wait current work finish */
1262 if (vout->vbq.streaming)
1263 cancel_work_sync(&vout->disp_work);
1265 mutex_lock(&vout->task_lock);
1267 if (vout->disp_support_windows) {
1268 vout->task.output.crop.pos.x = 0;
1269 vout->task.output.crop.pos.y = 0;
1270 vout->win_pos.x = crop->c.left;
1271 vout->win_pos.y = crop->c.top;
1272 vout->task.output.width = crop->c.width;
1273 vout->task.output.height = crop->c.height;
1275 vout->task.output.crop.pos.x = crop->c.left;
1276 vout->task.output.crop.pos.y = crop->c.top;
1279 vout->task.output.crop.w = crop->c.width;
1280 vout->task.output.crop.h = crop->c.height;
1283 * must S_CROP before S_FMT, for fist time S_CROP, will not check
1284 * ipu task, it will check in S_FMT, after S_FMT, S_CROP should
1285 * check ipu task too.
1287 if (vout->fmt_init) {
1288 if (vout->vbq.streaming)
1289 release_disp_output(vout);
1291 ret = mxc_vout_try_task(vout);
1293 v4l2_err(vout->vfd->v4l2_dev,
1294 "vout check task failed\n");
1297 if (vout->vbq.streaming) {
1298 ret = config_disp_output(vout);
1300 v4l2_err(vout->vfd->v4l2_dev,
1301 "Config display output failed\n");
1308 mutex_unlock(&vout->task_lock);
1313 static int mxc_vidioc_queryctrl(struct file *file, void *fh,
1314 struct v4l2_queryctrl *ctrl)
1319 case V4L2_CID_ROTATE:
1320 ret = v4l2_ctrl_query_fill(ctrl, 0, 270, 90, 0);
1322 case V4L2_CID_VFLIP:
1323 ret = v4l2_ctrl_query_fill(ctrl, 0, 1, 1, 0);
1325 case V4L2_CID_HFLIP:
1326 ret = v4l2_ctrl_query_fill(ctrl, 0, 1, 1, 0);
1328 case V4L2_CID_MXC_MOTION:
1329 ret = v4l2_ctrl_query_fill(ctrl, 0, 2, 1, 0);
1332 ctrl->name[0] = '\0';
1338 static int mxc_vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *ctrl)
1341 struct mxc_vout_output *vout = fh;
1344 case V4L2_CID_ROTATE:
1345 ctrl->value = vout->ctrl_rotate;
1347 case V4L2_CID_VFLIP:
1348 ctrl->value = vout->ctrl_vflip;
1350 case V4L2_CID_HFLIP:
1351 ctrl->value = vout->ctrl_hflip;
1353 case V4L2_CID_MXC_MOTION:
1354 if (vout->task.input.deinterlace.enable)
1355 ctrl->value = vout->task.input.deinterlace.motion;
1365 static void setup_task_rotation(struct mxc_vout_output *vout)
1367 if (vout->ctrl_rotate == 0) {
1368 if (vout->ctrl_vflip && vout->ctrl_hflip)
1369 vout->task.output.rotate = IPU_ROTATE_180;
1370 else if (vout->ctrl_vflip)
1371 vout->task.output.rotate = IPU_ROTATE_VERT_FLIP;
1372 else if (vout->ctrl_hflip)
1373 vout->task.output.rotate = IPU_ROTATE_HORIZ_FLIP;
1375 vout->task.output.rotate = IPU_ROTATE_NONE;
1376 } else if (vout->ctrl_rotate == 90) {
1377 if (vout->ctrl_vflip && vout->ctrl_hflip)
1378 vout->task.output.rotate = IPU_ROTATE_90_LEFT;
1379 else if (vout->ctrl_vflip)
1380 vout->task.output.rotate = IPU_ROTATE_90_RIGHT_VFLIP;
1381 else if (vout->ctrl_hflip)
1382 vout->task.output.rotate = IPU_ROTATE_90_RIGHT_HFLIP;
1384 vout->task.output.rotate = IPU_ROTATE_90_RIGHT;
1385 } else if (vout->ctrl_rotate == 180) {
1386 if (vout->ctrl_vflip && vout->ctrl_hflip)
1387 vout->task.output.rotate = IPU_ROTATE_NONE;
1388 else if (vout->ctrl_vflip)
1389 vout->task.output.rotate = IPU_ROTATE_HORIZ_FLIP;
1390 else if (vout->ctrl_hflip)
1391 vout->task.output.rotate = IPU_ROTATE_VERT_FLIP;
1393 vout->task.output.rotate = IPU_ROTATE_180;
1394 } else if (vout->ctrl_rotate == 270) {
1395 if (vout->ctrl_vflip && vout->ctrl_hflip)
1396 vout->task.output.rotate = IPU_ROTATE_90_RIGHT;
1397 else if (vout->ctrl_vflip)
1398 vout->task.output.rotate = IPU_ROTATE_90_RIGHT_HFLIP;
1399 else if (vout->ctrl_hflip)
1400 vout->task.output.rotate = IPU_ROTATE_90_RIGHT_VFLIP;
1402 vout->task.output.rotate = IPU_ROTATE_90_LEFT;
1406 static int mxc_vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *ctrl)
1409 struct mxc_vout_output *vout = fh;
1411 /* wait current work finish */
1412 if (vout->vbq.streaming)
1413 cancel_work_sync(&vout->disp_work);
1415 mutex_lock(&vout->task_lock);
1417 case V4L2_CID_ROTATE:
1419 vout->ctrl_rotate = (ctrl->value/90) * 90;
1420 if (vout->ctrl_rotate > 270)
1421 vout->ctrl_rotate = 270;
1422 setup_task_rotation(vout);
1425 case V4L2_CID_VFLIP:
1427 vout->ctrl_vflip = ctrl->value;
1428 setup_task_rotation(vout);
1431 case V4L2_CID_HFLIP:
1433 vout->ctrl_hflip = ctrl->value;
1434 setup_task_rotation(vout);
1437 case V4L2_CID_MXC_MOTION:
1439 vout->task.input.deinterlace.motion = ctrl->value;
1447 if (vout->fmt_init) {
1448 if (vout->vbq.streaming)
1449 release_disp_output(vout);
1451 ret = mxc_vout_try_task(vout);
1453 v4l2_err(vout->vfd->v4l2_dev,
1454 "vout check task failed\n");
1457 if (vout->vbq.streaming) {
1458 ret = config_disp_output(vout);
1460 v4l2_err(vout->vfd->v4l2_dev,
1461 "Config display output failed\n");
1468 mutex_unlock(&vout->task_lock);
1473 static int mxc_vidioc_reqbufs(struct file *file, void *fh,
1474 struct v4l2_requestbuffers *req)
1477 struct mxc_vout_output *vout = fh;
1478 struct videobuf_queue *q = &vout->vbq;
1480 if (req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
1483 /* should not be here after streaming, videobuf_reqbufs will control */
1484 mutex_lock(&vout->task_lock);
1486 ret = videobuf_reqbufs(q, req);
1488 mutex_unlock(&vout->task_lock);
1492 static int mxc_vidioc_querybuf(struct file *file, void *fh,
1493 struct v4l2_buffer *b)
1496 struct mxc_vout_output *vout = fh;
1498 ret = videobuf_querybuf(&vout->vbq, b);
1500 /* return physical address */
1501 struct videobuf_buffer *vb = vout->vbq.bufs[b->index];
1502 if (b->flags & V4L2_BUF_FLAG_MAPPED)
1503 b->m.offset = videobuf_to_dma_contig(vb);
1509 static int mxc_vidioc_qbuf(struct file *file, void *fh,
1510 struct v4l2_buffer *buffer)
1512 struct mxc_vout_output *vout = fh;
1514 return videobuf_qbuf(&vout->vbq, buffer);
1517 static int mxc_vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
1519 struct mxc_vout_output *vout = fh;
1521 if (!vout->vbq.streaming)
1524 if (file->f_flags & O_NONBLOCK)
1525 return videobuf_dqbuf(&vout->vbq, (struct v4l2_buffer *)b, 1);
1527 return videobuf_dqbuf(&vout->vbq, (struct v4l2_buffer *)b, 0);
1530 static int set_window_position(struct mxc_vout_output *vout, struct mxcfb_pos *pos)
1532 struct fb_info *fbi = vout->fbi;
1533 mm_segment_t old_fs;
1536 if (vout->disp_support_windows) {
1539 ret = fbi->fbops->fb_ioctl(fbi, MXCFB_SET_OVERLAY_POS,
1540 (unsigned long)pos);
1547 static int config_disp_output(struct mxc_vout_output *vout)
1549 struct fb_info *fbi = vout->fbi;
1550 struct fb_var_screeninfo var;
1551 int i, display_buf_size, fb_num, ret;
1554 memcpy(&var, &fbi->var, sizeof(var));
1556 var.xres = vout->task.output.width;
1557 var.yres = vout->task.output.height;
1558 if (vout->bypass_pp) {
1561 if (vout->task.input.width > vout->task.output.width)
1562 var.xres_virtual = vout->task.input.width;
1564 var.xres_virtual = var.xres;
1565 if (vout->task.input.height > vout->task.output.height)
1566 var.yres_virtual = vout->task.input.height;
1568 var.yres_virtual = var.yres;
1569 var.rotate = vout->task.output.rotate;
1570 var.vmode |= FB_VMODE_YWRAP;
1573 var.xres_virtual = var.xres;
1574 is_1080p = CHECK_TILED_1080P_DISPLAY(vout);
1576 var.yres_virtual = fb_num * FRAME_HEIGHT_1080P;
1578 var.yres_virtual = fb_num * var.yres;
1579 var.vmode &= ~FB_VMODE_YWRAP;
1581 var.bits_per_pixel = fmt_to_bpp(vout->task.output.format);
1582 var.nonstd = vout->task.output.format;
1584 v4l2_dbg(1, debug, vout->vfd->v4l2_dev,
1585 "set display fb to %d %d\n",
1586 var.xres, var.yres);
1588 ret = set_window_position(vout, &vout->win_pos);
1592 /* Init display channel through fb API */
1594 var.activate |= FB_ACTIVATE_FORCE;
1596 fbi->flags |= FBINFO_MISC_USEREVENT;
1597 ret = fb_set_var(fbi, &var);
1598 fbi->flags &= ~FBINFO_MISC_USEREVENT;
1603 is_1080p = CHECK_TILED_1080P_DISPLAY(vout);
1605 display_buf_size = fbi->fix.line_length * FRAME_HEIGHT_1080P;
1607 display_buf_size = fbi->fix.line_length * fbi->var.yres;
1608 for (i = 0; i < fb_num; i++)
1609 vout->disp_bufs[i] = fbi->fix.smem_start + i * display_buf_size;
1612 fbi->flags |= FBINFO_MISC_USEREVENT;
1613 ret = fb_blank(fbi, FB_BLANK_UNBLANK);
1614 fbi->flags &= ~FBINFO_MISC_USEREVENT;
1620 static void release_disp_output(struct mxc_vout_output *vout)
1622 struct fb_info *fbi = vout->fbi;
1623 struct mxcfb_pos pos;
1626 fbi->flags |= FBINFO_MISC_USEREVENT;
1627 fb_blank(fbi, FB_BLANK_POWERDOWN);
1628 fbi->flags &= ~FBINFO_MISC_USEREVENT;
1631 /* restore pos to 0,0 avoid fb pan display hang? */
1634 set_window_position(vout, &pos);
1636 /* fix if ic bypass crack smem_start */
1637 if (vout->bypass_pp) {
1639 fbi->fix.smem_start = vout->disp_bufs[0];
1643 if (get_ipu_channel(fbi) == MEM_BG_SYNC) {
1645 fbi->flags |= FBINFO_MISC_USEREVENT;
1646 fb_blank(fbi, FB_BLANK_UNBLANK);
1647 fbi->flags &= ~FBINFO_MISC_USEREVENT;
1652 static int mxc_vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
1654 struct mxc_vout_output *vout = fh;
1655 struct videobuf_queue *q = &vout->vbq;
1659 v4l2_err(vout->vfd->v4l2_dev,
1660 "video output already run\n");
1665 if (deinterlace_3_field(vout) && list_is_singular(&q->stream)) {
1666 v4l2_err(vout->vfd->v4l2_dev,
1667 "deinterlacing: need queue 2 frame before streamon\n");
1672 ret = config_disp_output(vout);
1674 v4l2_err(vout->vfd->v4l2_dev,
1675 "Config display output failed\n");
1679 init_timer(&vout->timer);
1680 vout->timer.function = mxc_vout_timer_handler;
1681 vout->timer.data = (unsigned long)vout;
1682 vout->timer_stop = true;
1684 vout->start_jiffies = jiffies;
1686 vout->pre_vb = NULL;
1688 ret = videobuf_streamon(q);
1693 static int mxc_vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
1695 struct mxc_vout_output *vout = fh;
1696 struct videobuf_queue *q = &vout->vbq;
1700 cancel_work_sync(&vout->disp_work);
1701 flush_workqueue(vout->v4l_wq);
1703 del_timer_sync(&vout->timer);
1705 release_disp_output(vout);
1707 ret = videobuf_streamoff(&vout->vbq);
1709 INIT_LIST_HEAD(&vout->queue_list);
1710 INIT_LIST_HEAD(&vout->active_list);
1715 static const struct v4l2_ioctl_ops mxc_vout_ioctl_ops = {
1716 .vidioc_querycap = mxc_vidioc_querycap,
1717 .vidioc_enum_fmt_vid_out = mxc_vidioc_enum_fmt_vid_out,
1718 .vidioc_g_fmt_vid_out = mxc_vidioc_g_fmt_vid_out,
1719 .vidioc_s_fmt_vid_out = mxc_vidioc_s_fmt_vid_out,
1720 .vidioc_cropcap = mxc_vidioc_cropcap,
1721 .vidioc_g_crop = mxc_vidioc_g_crop,
1722 .vidioc_s_crop = mxc_vidioc_s_crop,
1723 .vidioc_queryctrl = mxc_vidioc_queryctrl,
1724 .vidioc_g_ctrl = mxc_vidioc_g_ctrl,
1725 .vidioc_s_ctrl = mxc_vidioc_s_ctrl,
1726 .vidioc_reqbufs = mxc_vidioc_reqbufs,
1727 .vidioc_querybuf = mxc_vidioc_querybuf,
1728 .vidioc_qbuf = mxc_vidioc_qbuf,
1729 .vidioc_dqbuf = mxc_vidioc_dqbuf,
1730 .vidioc_streamon = mxc_vidioc_streamon,
1731 .vidioc_streamoff = mxc_vidioc_streamoff,
1734 static const struct v4l2_file_operations mxc_vout_fops = {
1735 .owner = THIS_MODULE,
1736 .unlocked_ioctl = video_ioctl2,
1737 .mmap = mxc_vout_mmap,
1738 .open = mxc_vout_open,
1739 .release = mxc_vout_release,
1742 static struct video_device mxc_vout_template = {
1743 .name = "MXC Video Output",
1744 .fops = &mxc_vout_fops,
1745 .ioctl_ops = &mxc_vout_ioctl_ops,
1746 .release = video_device_release,
1749 static struct videobuf_queue_ops mxc_vout_vbq_ops = {
1750 .buf_setup = mxc_vout_buffer_setup,
1751 .buf_prepare = mxc_vout_buffer_prepare,
1752 .buf_release = mxc_vout_buffer_release,
1753 .buf_queue = mxc_vout_buffer_queue,
1756 static void mxc_vout_free_output(struct mxc_vout_dev *dev)
1759 struct mxc_vout_output *vout;
1760 struct video_device *vfd;
1762 for (i = 0; i < dev->out_num; i++) {
1765 if (vout->vdoa_dma.vaddr) {
1766 dma_free_coherent(vout->vbq.dev, vout->vdoa_dma.size,
1767 vout->vdoa_dma.vaddr, vout->vdoa_dma.paddr);
1768 v4l2_dbg(1, debug, vout->vfd->v4l2_dev,
1769 "free vdoa_dma.size:0x%x, paddr:0x%x\n",
1770 vout->vdoa_dma.size,
1771 vout->vdoa_dma.paddr);
1772 memset(&vout->vdoa_dma, 0, sizeof(vout->vdoa_dma));
1775 if (!video_is_registered(vfd))
1776 video_device_release(vfd);
1778 video_unregister_device(vfd);
1784 static int __init mxc_vout_setup_output(struct mxc_vout_dev *dev)
1786 struct videobuf_queue *q;
1787 struct fb_info *fbi;
1788 struct mxc_vout_output *vout;
1791 update_display_setting();
1793 /* all output/overlay based on fb */
1794 for (i = 0; i < num_registered_fb; i++) {
1795 fbi = registered_fb[i];
1797 vout = kzalloc(sizeof(struct mxc_vout_output), GFP_KERNEL);
1803 dev->out[dev->out_num] = vout;
1807 vout->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
1808 vout->vfd = video_device_alloc();
1814 *vout->vfd = mxc_vout_template;
1815 vout->vfd->debug = debug;
1816 vout->vfd->v4l2_dev = &dev->v4l2_dev;
1817 vout->vfd->lock = &vout->mutex;
1819 mutex_init(&vout->mutex);
1820 mutex_init(&vout->task_lock);
1822 strlcpy(vout->vfd->name, fbi->fix.id, sizeof(vout->vfd->name));
1824 video_set_drvdata(vout->vfd, vout);
1826 if (video_register_device(vout->vfd,
1827 VFL_TYPE_GRABBER, video_nr + i) < 0) {
1834 spin_lock_init(&vout->vbq_lock);
1835 videobuf_queue_dma_contig_init(q, &mxc_vout_vbq_ops, q->dev,
1836 &vout->vbq_lock, vout->type, V4L2_FIELD_NONE,
1837 sizeof(struct videobuf_buffer), vout, NULL);
1839 v4l2_info(vout->vfd->v4l2_dev, "V4L2 device registered as %s\n",
1840 video_device_node_name(vout->vfd));
1847 static int mxc_vout_probe(struct platform_device *pdev)
1850 struct mxc_vout_dev *dev;
1852 dev = kzalloc(sizeof(*dev), GFP_KERNEL);
1856 dev->dev = &pdev->dev;
1857 dev->dev->dma_mask = kmalloc(sizeof(*dev->dev->dma_mask), GFP_KERNEL);
1858 *dev->dev->dma_mask = DMA_BIT_MASK(32);
1859 dev->dev->coherent_dma_mask = DMA_BIT_MASK(32);
1861 ret = v4l2_device_register(dev->dev, &dev->v4l2_dev);
1863 dev_err(dev->dev, "v4l2_device_register failed\n");
1867 ret = mxc_vout_setup_output(dev);
1874 mxc_vout_free_output(dev);
1875 v4l2_device_unregister(&dev->v4l2_dev);
1881 static int mxc_vout_remove(struct platform_device *pdev)
1883 struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
1884 struct mxc_vout_dev *dev = container_of(v4l2_dev, struct
1885 mxc_vout_dev, v4l2_dev);
1887 mxc_vout_free_output(dev);
1888 v4l2_device_unregister(v4l2_dev);
1893 static struct platform_driver mxc_vout_driver = {
1895 .name = "mxc_v4l2_output",
1897 .probe = mxc_vout_probe,
1898 .remove = mxc_vout_remove,
1901 static int __init mxc_vout_init(void)
1903 if (platform_driver_register(&mxc_vout_driver) != 0) {
1904 printk(KERN_ERR VOUT_NAME ":Could not register Video driver\n");
1910 static void mxc_vout_cleanup(void)
1912 platform_driver_unregister(&mxc_vout_driver);
1915 module_init(mxc_vout_init);
1916 module_exit(mxc_vout_cleanup);
1918 MODULE_AUTHOR("Freescale Semiconductor, Inc.");
1919 MODULE_DESCRIPTION("V4L2-driver for MXC video output");
1920 MODULE_LICENSE("GPL");