diff options
author | Jason Chen <b02280@freescale.com> | 2011-11-04 16:42:40 +0800 |
---|---|---|
committer | Jason Chen <b02280@freescale.com> | 2011-11-04 16:42:40 +0800 |
commit | c321da584989837d3df356fec9adc6ca132a2f91 (patch) | |
tree | c32ae807e0c42717f3df03b889e9daad96163efe /drivers | |
parent | 457becd5a171f759001f4059985a5727d41ab193 (diff) |
ENGR00160878-2 mxc v4l2 output: new mxc v4l2 output driver based on videobuf
This new v4l2 output driver is based on videobuf, using dma contig alloc method.
It creates video dev node for each display framebuffer begin from /dev/video16
by default.
If need post-processing, this driver will do it by ipu pp driver which support:
- resizing
- CSC
- rotate
- deinterlacing
If no need post-processing, the IPU IC will be bypassed as old driver, the
buf will be set to fb buffer directly by crack fb smem_start.
The user should do setting before streamon like below:
1. set output crop
2. set ctrl like rotate/vflip/hflip/deinterlacing motion
3. set fmt
4. reqbuf
The new features compare to old driver:
- support multi-instance
- support user point buffer
- runtime suspend/resume
For suspend/resume, still has chance to meet issue on mx6q, will fix later.
Signed-off-by: Jason Chen <b02280@freescale.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/media/video/Kconfig | 1 | ||||
-rw-r--r-- | drivers/media/video/mxc/output/Makefile | 2 | ||||
-rw-r--r-- | drivers/media/video/mxc/output/mxc_v4l2_output.c | 2806 | ||||
-rw-r--r-- | drivers/media/video/mxc/output/mxc_v4l2_output.h | 158 | ||||
-rw-r--r-- | drivers/media/video/mxc/output/mxc_vout.c | 1488 | ||||
-rw-r--r-- | drivers/video/mxc/mxc_ipuv3_fb.c | 17 |
6 files changed, 1503 insertions, 2969 deletions
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 5a8be678760d..1d7eaea66efa 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -597,6 +597,7 @@ source "drivers/media/video/mxc/capture/Kconfig" config VIDEO_MXC_OUTPUT tristate "MXC Video For Linux Video Output" depends on VIDEO_DEV && ARCH_MXC + select VIDEOBUF_DMA_CONTIG default y ---help--- This is the video4linux2 output driver based on MXC IPU/eMMA module. diff --git a/drivers/media/video/mxc/output/Makefile b/drivers/media/video/mxc/output/Makefile index 500442544902..ba47857f21d9 100644 --- a/drivers/media/video/mxc/output/Makefile +++ b/drivers/media/video/mxc/output/Makefile @@ -4,7 +4,7 @@ ifeq ($(CONFIG_VIDEO_MXC_EMMA_OUTPUT),y) endif ifeq ($(CONFIG_VIDEO_MXC_IPU_OUTPUT),y) - obj-$(CONFIG_VIDEO_MXC_OUTPUT) += mxc_v4l2_output.o + obj-$(CONFIG_VIDEO_MXC_OUTPUT) += mxc_vout.o endif ifeq ($(CONFIG_VIDEO_MXC_PXP_V4L2),y) obj-$(CONFIG_VIDEO_MXC_PXP_V4L2) += mxc_pxp_v4l2.o diff --git a/drivers/media/video/mxc/output/mxc_v4l2_output.c b/drivers/media/video/mxc/output/mxc_v4l2_output.c deleted file mode 100644 index 49e538e8c463..000000000000 --- a/drivers/media/video/mxc/output/mxc_v4l2_output.c +++ /dev/null @@ -1,2806 +0,0 @@ -/* - * Copyright 2005-2011 Freescale Semiconductor, Inc. All Rights Reserved. - */ - -/* - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html - */ - -/*! - * @file drivers/media/video/mxc/output/mxc_v4l2_output.c - * - * @brief MXC V4L2 Video Output Driver - * - * Video4Linux2 Output Device using MXC IPU Post-processing functionality. - * - * @ingroup MXC_V4L2_OUTPUT - */ -#include <linux/module.h> -#include <linux/init.h> -#include <linux/io.h> -#include <linux/semaphore.h> -#include <linux/slab.h> -#include <linux/console.h> -#include <linux/fs.h> -#include <linux/delay.h> -#include <linux/platform_device.h> -#include <linux/dma-mapping.h> -#include <linux/mxcfb.h> -#include <media/v4l2-ioctl.h> -#include <asm/cacheflush.h> -#include <mach/hardware.h> - -#include "mxc_v4l2_output.h" - -#define init_MUTEX(sem) sema_init(sem, 1) - -#define INTERLACED_CONTENT(vout) ((cpu_is_mx51() || \ - cpu_is_mx53()) && \ - (((vout)->field_fmt == V4L2_FIELD_INTERLACED_TB) || \ - ((vout)->field_fmt == V4L2_FIELD_INTERLACED_BT))) -#define LOAD_3FIELDS(vout) ((INTERLACED_CONTENT(vout)) && \ - ((vout)->motion_sel != HIGH_MOTION)) - -struct v4l2_output mxc_outputs[1] = { - { - .index = MXC_V4L2_OUT_2_SDC, - .name = "DISP3 Video Out", - .type = V4L2_OUTPUT_TYPE_ANALOG, /* not really correct, - but no other choice */ - .audioset = 0, - .modulator = 0, - .std = V4L2_STD_UNKNOWN} -}; - -static int video_nr = 16; -static DEFINE_SPINLOCK(g_lock); -static int last_index_n; -static unsigned int ipu_ic_out_max_width_size; -static unsigned int ipu_ic_out_max_height_size; -/* debug counters */ -uint32_t g_irq_cnt; -uint32_t g_buf_output_cnt; -uint32_t g_buf_q_cnt; -uint32_t g_buf_dq_cnt; - -#define QUEUE_SIZE (MAX_FRAME_NUM + 1) -static __inline int queue_size(v4l_queue *q) -{ - if (q->tail >= q->head) - return q->tail - q->head; - else - return (q->tail + QUEUE_SIZE) - q->head; -} - -static __inline int queue_buf(v4l_queue *q, int idx) -{ - if (((q->tail + 1) % QUEUE_SIZE) == q->head) - return -1; /* queue full */ - q->list[q->tail] = idx; - q->tail = (q->tail + 1) % QUEUE_SIZE; - return 0; -} - -static __inline int dequeue_buf(v4l_queue *q) -{ - int ret; - if (q->tail == q->head) - return -1; /* queue empty */ - ret = q->list[q->head]; - q->head = (q->head + 1) % QUEUE_SIZE; - return ret; -} - -static __inline int peek_next_buf(v4l_queue *q) -{ - if (q->tail == q->head) - return -1; /* queue empty */ - return q->list[q->head]; -} - -static __inline unsigned long get_jiffies(struct timeval *t) -{ - struct timeval cur; - - if (t->tv_usec >= 1000000) { - t->tv_sec += t->tv_usec / 1000000; - t->tv_usec = t->tv_usec % 1000000; - } - - do_gettimeofday(&cur); - if ((t->tv_sec < cur.tv_sec) - || ((t->tv_sec == cur.tv_sec) && (t->tv_usec < cur.tv_usec))) - return jiffies; - - if (t->tv_usec < cur.tv_usec) { - cur.tv_sec = t->tv_sec - cur.tv_sec - 1; - cur.tv_usec = t->tv_usec + 1000000 - cur.tv_usec; - } else { - cur.tv_sec = t->tv_sec - cur.tv_sec; - cur.tv_usec = t->tv_usec - cur.tv_usec; - } - - return jiffies + timeval_to_jiffies(&cur); -} - -/*! - * Private function to free buffers - * - * @param bufs_paddr Array of physical address of buffers to be freed - * - * @param bufs_vaddr Array of virtual address of buffers to be freed - * - * @param num_buf Number of buffers to be freed - * - * @param size Size for each buffer to be free - * - * @return status 0 success. - */ -static int mxc_free_buffers(dma_addr_t bufs_paddr[], void *bufs_vaddr[], - int num_buf, int size) -{ - int i; - - for (i = 0; i < num_buf; i++) { - if (bufs_vaddr[i] != 0) { - dma_free_coherent(0, size, bufs_vaddr[i], - bufs_paddr[i]); - pr_debug("freed @ paddr=0x%08X\n", (u32) bufs_paddr[i]); - bufs_paddr[i] = 0; - bufs_vaddr[i] = NULL; - } - } - return 0; -} - -/*! - * Private function to allocate buffers - * - * @param bufs_paddr Output array of physical address of buffers allocated - * - * @param bufs_vaddr Output array of virtual address of buffers allocated - * - * @param num_buf Input number of buffers to allocate - * - * @param size Input size for each buffer to allocate - * - * @return status -0 Successfully allocated a buffer, -ENOBUFS failed. - */ -static int mxc_allocate_buffers(dma_addr_t bufs_paddr[], void *bufs_vaddr[], - int num_buf, int size) -{ - int i; - - for (i = 0; i < num_buf; i++) { - bufs_vaddr[i] = dma_alloc_coherent(0, size, - &bufs_paddr[i], - GFP_DMA | GFP_KERNEL); - - if (bufs_vaddr[i] == 0) { - mxc_free_buffers(bufs_paddr, bufs_vaddr, i, size); - printk(KERN_ERR "dma_alloc_coherent failed.\n"); - return -ENOBUFS; - } - pr_debug("allocated @ paddr=0x%08X, size=%d.\n", - (u32) bufs_paddr[i], size); - } - - return 0; -} - -/* - * Returns bits per pixel for given pixel format - * - * @param pixelformat V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_BGR24 or V4L2_PIX_FMT_BGR32 - * - * @return bits per pixel of pixelformat - */ -static u32 fmt_to_bpp(u32 pixelformat) -{ - u32 bpp; - - bpp = 8 * bytes_per_pixel(pixelformat); - return bpp; -} - -static bool format_is_yuv(u32 pixelformat) -{ - switch (pixelformat) { - case V4L2_PIX_FMT_YUV420: - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_YUYV: - case V4L2_PIX_FMT_YUV422P: - case V4L2_PIX_FMT_YVU420: - case V4L2_PIX_FMT_YUV444: - case V4L2_PIX_FMT_NV12: - return true; - break; - } - return false; -} - -static u32 bpp_to_fmt(struct fb_info *fbi) -{ - if (fbi->var.nonstd) - return fbi->var.nonstd; - - if (fbi->var.bits_per_pixel == 24) - return V4L2_PIX_FMT_BGR24; - else if (fbi->var.bits_per_pixel == 32) - return V4L2_PIX_FMT_BGR32; - else if (fbi->var.bits_per_pixel == 16) - return V4L2_PIX_FMT_RGB565; - - return 0; -} - -/* - * we are using double buffer for video playback, ipu need make - * sure current buffer should not be the same buffer of next display - * one. - */ -static int select_display_buffer(vout_data *vout, int next_buf) -{ - int ret = 0; - - vout->disp_buf_num = next_buf; - if (ipu_get_cur_buffer_idx(vout->ipu, vout->display_ch, IPU_INPUT_BUFFER) - != next_buf) - ret = ipu_select_buffer(vout->ipu, vout->display_ch, IPU_INPUT_BUFFER, - next_buf); - else - dev_dbg(&vout->video_dev->dev, - "display buffer not ready for select\n"); - return ret; -} - -static void setup_next_buf_timer(vout_data *vout, int index) -{ - unsigned long timeout; - - /* Setup timer for next buffer */ - /* if timestamp is 0, then default to 30fps */ - if ((vout->v4l2_bufs[index].timestamp.tv_sec == 0) - && (vout->v4l2_bufs[index].timestamp.tv_usec == 0) - && vout->start_jiffies) - timeout = - vout->start_jiffies + vout->frame_count * HZ / 30; - else - timeout = - get_jiffies(&vout->v4l2_bufs[index].timestamp); - - if (jiffies >= timeout) { - dev_dbg(&vout->video_dev->dev, - "warning: timer timeout already expired.\n"); - } - if (mod_timer(&vout->output_timer, timeout)) - dev_dbg(&vout->video_dev->dev, - "warning: timer was already set\n"); - - dev_dbg(&vout->video_dev->dev, - "timer handler next schedule: %lu\n", timeout); -} - -static int finish_previous_frame(vout_data *vout) -{ - struct fb_info *fbi = - registered_fb[vout->output_fb_num[vout->cur_disp_output]]; - mm_segment_t old_fs; - int ret = 0, try = 0; - - /* make sure buf[vout->disp_buf_num] in showing */ - while (ipu_check_buffer_ready(vout->ipu, vout->display_ch, - IPU_INPUT_BUFFER, vout->disp_buf_num)) { - if (fbi->fbops->fb_ioctl) { - old_fs = get_fs(); - set_fs(KERNEL_DS); - ret = fbi->fbops->fb_ioctl(fbi, MXCFB_WAIT_FOR_VSYNC, - (unsigned int)NULL); - set_fs(old_fs); - - if ((ret < 0) || (try == 1)) { - /* - * ic_bypass need clear display buffer ready for next update. - * when fb doing blank and unblank, it has chance to go into - * dead loop: fb unblank just after buffer 1 ready selected. - */ - ipu_clear_buffer_ready(vout->ipu, vout->display_ch, IPU_INPUT_BUFFER, - vout->disp_buf_num); - } - } - try++; - } - - return ret; -} - -static int show_current_frame(vout_data *vout) -{ - struct fb_info *fbi = - registered_fb[vout->output_fb_num[vout->cur_disp_output]]; - mm_segment_t old_fs; - int ret = 0; - - /* make sure buf[vout->disp_buf_num] begin to show */ - if (ipu_get_cur_buffer_idx(vout->ipu, vout->display_ch, IPU_INPUT_BUFFER) - != vout->disp_buf_num) { - /* wait for display frame finish */ - if (fbi->fbops->fb_ioctl) { - old_fs = get_fs(); - set_fs(KERNEL_DS); - ret = fbi->fbops->fb_ioctl(fbi, MXCFB_WAIT_FOR_VSYNC, - (unsigned int)NULL); - set_fs(old_fs); - } - } - - return ret; -} - -static void icbypass_work_func(struct work_struct *work) -{ - vout_data *vout = - container_of(work, vout_data, icbypass_work); - int index, ret; - int last_buf; - unsigned long lock_flags = 0; - - finish_previous_frame(vout); - - spin_lock_irqsave(&g_lock, lock_flags); - - index = dequeue_buf(&vout->ready_q); - if (index == -1) { /* no buffers ready, should never occur */ - dev_err(&vout->video_dev->dev, - "mxc_v4l2out: timer - no queued buffers ready\n"); - goto exit; - } - g_buf_dq_cnt++; - vout->frame_count++; - - vout->ipu_buf[vout->next_rdy_ipu_buf] = index; - ret = ipu_update_channel_buffer(vout->ipu, vout->display_ch, IPU_INPUT_BUFFER, - vout->next_rdy_ipu_buf, - vout->v4l2_bufs[index].m.offset); - ret += select_display_buffer(vout, vout->next_rdy_ipu_buf); - if (ret < 0) { - dev_err(&vout->video_dev->dev, - "unable to update buffer %d address rc=%d\n", - vout->next_rdy_ipu_buf, ret); - goto exit; - } - spin_unlock_irqrestore(&g_lock, lock_flags); - show_current_frame(vout); - spin_lock_irqsave(&g_lock, lock_flags); - vout->next_rdy_ipu_buf = !vout->next_rdy_ipu_buf; - - last_buf = vout->ipu_buf[vout->next_done_ipu_buf]; - if (last_buf != -1) { - g_buf_output_cnt++; - vout->v4l2_bufs[last_buf].flags = V4L2_BUF_FLAG_DONE; - queue_buf(&vout->done_q, last_buf); - wake_up_interruptible(&vout->v4l_bufq); - vout->ipu_buf[vout->next_done_ipu_buf] = -1; - vout->next_done_ipu_buf = !vout->next_done_ipu_buf; - } - - if (g_buf_output_cnt > 0) { - /* Setup timer for next buffer */ - index = peek_next_buf(&vout->ready_q); - if (index != -1) - setup_next_buf_timer(vout, index); - else - vout->state = STATE_STREAM_PAUSED; - - if (vout->state == STATE_STREAM_STOPPING) { - if ((vout->ipu_buf[0] == -1) && (vout->ipu_buf[1] == -1)) { - vout->state = STATE_STREAM_OFF; - } - } - } -exit: - spin_unlock_irqrestore(&g_lock, lock_flags); -} - -static int get_cur_fb_blank(vout_data *vout) -{ - struct fb_info *fbi = - registered_fb[vout->output_fb_num[vout->cur_disp_output]]; - mm_segment_t old_fs; - int ret = 0; - - /* Check BG blank first, if BG is blank, FG should be blank too */ - if (vout->display_ch == MEM_FG_SYNC) { - int i, bg_found = 0; - for (i = 0; i < num_registered_fb; i++) { - struct fb_info *bg_fbi; - char *idstr = registered_fb[i]->fix.id; - if (strncmp(idstr, "DISP3 BG", 8) == 0) { - bg_found = 1; - bg_fbi = registered_fb[i]; - if (bg_fbi->fbops->fb_ioctl) { - old_fs = get_fs(); - set_fs(KERNEL_DS); - ret = bg_fbi->fbops->fb_ioctl(bg_fbi, - MXCFB_GET_FB_BLANK, - (unsigned int)(&vout->fb_blank)); - set_fs(old_fs); - } - } - if (bg_found) { - if (vout->fb_blank == FB_BLANK_UNBLANK) - break; - else - return ret; - } - } - } - - if (fbi->fbops->fb_ioctl) { - old_fs = get_fs(); - set_fs(KERNEL_DS); - ret = fbi->fbops->fb_ioctl(fbi, MXCFB_GET_FB_BLANK, - (unsigned int)(&vout->fb_blank)); - set_fs(old_fs); - } - - return ret; -} - -static void mxc_v4l2out_timer_handler(unsigned long arg) -{ - int index, ret; - unsigned long lock_flags = 0; - vout_data *vout = (vout_data *) arg; - static int old_fb_blank = FB_BLANK_UNBLANK; - - spin_lock_irqsave(&g_lock, lock_flags); - - if ((vout->state == STATE_STREAM_STOPPING) - || (vout->state == STATE_STREAM_OFF)) - goto exit0; - - /* - * If timer occurs before IPU h/w is ready, then set the state to - * paused and the timer will be set again when next buffer is queued - * or PP comletes - */ - if (vout->ipu_buf[vout->next_rdy_ipu_buf] != -1) { - dev_dbg(&vout->video_dev->dev, "IPU buffer busy\n"); - vout->state = STATE_STREAM_PAUSED; - goto exit0; - } - - /* VDI need both buffer done before update buffer? */ - if (INTERLACED_CONTENT(vout) && - (vout->ipu_buf[!vout->next_rdy_ipu_buf] != -1)) { - dev_dbg(&vout->video_dev->dev, "IPU buffer busy\n"); - vout->state = STATE_STREAM_PAUSED; - goto exit0; - } - - /* Handle ic bypass mode in work queue */ - if (vout->ic_bypass) { - if (queue_work(vout->v4l_wq, &vout->icbypass_work) == 0) { - dev_err(&vout->video_dev->dev, - "ic bypass work was in queue already!\n "); - vout->state = STATE_STREAM_PAUSED; - } - goto exit0; - } - - get_cur_fb_blank(vout); - if (vout->fb_blank == FB_BLANK_UNBLANK) { - /* if first come back from fb blank, recover correct stack */ - if (old_fb_blank != FB_BLANK_UNBLANK) { - if (vout->next_disp_ipu_buf == 1) - ipu_select_buffer(vout->ipu, vout->display_ch, IPU_INPUT_BUFFER, 0); - else - ipu_select_buffer(vout->ipu, vout->display_ch, IPU_INPUT_BUFFER, 1); - } - if (ipu_get_cur_buffer_idx(vout->ipu, vout->display_ch, IPU_INPUT_BUFFER) - == vout->next_disp_ipu_buf) { - dev_dbg(&vout->video_dev->dev, "IPU disp busy\n"); - index = peek_next_buf(&vout->ready_q); - setup_next_buf_timer(vout, index); - old_fb_blank = vout->fb_blank; - goto exit0; - } - } - old_fb_blank = vout->fb_blank; - - /* Dequeue buffer and pass to IPU */ - index = dequeue_buf(&vout->ready_q); - if (index == -1) { /* no buffers ready, should never occur */ - dev_err(&vout->video_dev->dev, - "mxc_v4l2out: timer - no queued buffers ready\n"); - goto exit0; - } - g_buf_dq_cnt++; - vout->frame_count++; - - /* update next buffer */ - if (LOAD_3FIELDS(vout)) { - int index_n = index; - int index_p = last_index_n; - vout->ipu_buf_p[vout->next_rdy_ipu_buf] = last_index_n; - vout->ipu_buf[vout->next_rdy_ipu_buf] = index; - vout->ipu_buf_n[vout->next_rdy_ipu_buf] = index; - ret = ipu_update_channel_buffer(vout->ipu, vout->post_proc_ch, - IPU_INPUT_BUFFER, - vout->next_rdy_ipu_buf, - vout->v4l2_bufs[index].m.offset); - ret += ipu_update_channel_buffer(vout->ipu, MEM_VDI_PRP_VF_MEM_P, - IPU_INPUT_BUFFER, - vout->next_rdy_ipu_buf, - vout->v4l2_bufs[index_p].m.offset + vout->bytesperline); - ret += ipu_update_channel_buffer(vout->ipu, MEM_VDI_PRP_VF_MEM_N, - IPU_INPUT_BUFFER, - vout->next_rdy_ipu_buf, - vout->v4l2_bufs[index_n].m.offset + vout->bytesperline); - last_index_n = index; - } else { - vout->ipu_buf[vout->next_rdy_ipu_buf] = index; - if (vout->pp_split) { - vout->ipu_buf[!vout->next_rdy_ipu_buf] = index; - /* always left stripe */ - ret = ipu_update_channel_buffer(vout->ipu, vout->post_proc_ch, - IPU_INPUT_BUFFER, - 0,/* vout->next_rdy_ipu_buf,*/ - (vout->v4l2_bufs[index].m.offset) + - vout->pp_left_stripe.input_column + - vout->pp_up_stripe.input_column * vout->bytesperline); - - /* the U/V offset has to be updated inside of IDMAC */ - /* according to stripe offset */ - ret += ipu_update_channel_offset(vout->ipu, vout->post_proc_ch, - IPU_INPUT_BUFFER, - vout->v2f.fmt.pix.pixelformat, - vout->v2f.fmt.pix.width, - vout->v2f.fmt.pix.height, - vout->bytesperline, - vout->offset.u_offset, - vout->offset.v_offset, - vout->pp_up_stripe.input_column, - vout->pp_left_stripe.input_column); - } else - ret = ipu_update_channel_buffer(vout->ipu, vout->post_proc_ch, - IPU_INPUT_BUFFER, - vout->next_rdy_ipu_buf, - vout->v4l2_bufs[index].m.offset); - } - - if (ret < 0) { - dev_err(&vout->video_dev->dev, - "unable to update buffer %d address rc=%d\n", - vout->next_rdy_ipu_buf, ret); - goto exit0; - } - - /* set next buffer ready */ - if (LOAD_3FIELDS(vout)) - ret = ipu_select_multi_vdi_buffer(vout->ipu, vout->next_rdy_ipu_buf); - else - ret = ipu_select_buffer(vout->ipu, vout->post_proc_ch, IPU_INPUT_BUFFER, - vout->next_rdy_ipu_buf); - if (ret < 0) { - dev_err(&vout->video_dev->dev, - "unable to set IPU buffer ready\n"); - goto exit0; - } - - /* Split mode use buf 0 only, no need swith buf */ - if (!vout->pp_split) - vout->next_rdy_ipu_buf = !vout->next_rdy_ipu_buf; - - /* Always assume display in double buffers */ - vout->next_disp_ipu_buf = !vout->next_disp_ipu_buf; - - /* Setup timer for next buffer */ - index = peek_next_buf(&vout->ready_q); - if (index != -1) - setup_next_buf_timer(vout, index); - else - vout->state = STATE_STREAM_PAUSED; - - if (vout->state == STATE_STREAM_STOPPING) { - if ((vout->ipu_buf[0] == -1) && (vout->ipu_buf[1] == -1)) { - vout->state = STATE_STREAM_OFF; - } - } - - spin_unlock_irqrestore(&g_lock, lock_flags); - - return; - -exit0: - spin_unlock_irqrestore(&g_lock, lock_flags); -} - -static irqreturn_t mxc_v4l2out_work_irq_handler(int irq, void *dev_id) -{ - int last_buf; - int index; - unsigned long lock_flags = 0; - vout_data *vout = dev_id; - int pp_out_buf_left_right = 0; - int disp_buf_num = 0; - int disp_buf_num_next = 1; - int local_buffer = 0; - int pp_out_buf_offset = 0; - int pp_out_buf_up_down = 0; - int release_buffer = 0; - u32 eba_offset = 0; - u32 vertical_offset = 0; - u16 x_pos; - u16 y_pos; - int ret = -1; - - spin_lock_irqsave(&g_lock, lock_flags); - - g_irq_cnt++; - - /* Process previous buffer */ - if (LOAD_3FIELDS(vout)) - last_buf = vout->ipu_buf_p[vout->next_done_ipu_buf]; - else - last_buf = vout->ipu_buf[vout->next_done_ipu_buf]; - - if (last_buf != -1) { - /* If IC split mode on, update output buffer number */ - if (vout->pp_split) { - pp_out_buf_up_down = vout->pp_split_buf_num & 1;/* left/right stripe */ - pp_out_buf_left_right = (vout->pp_split_buf_num >> 1) & 1; /* up/down */ - local_buffer = (vout->pp_split == 1) ? pp_out_buf_up_down : - pp_out_buf_left_right; - disp_buf_num = vout->pp_split_buf_num >> 2; - disp_buf_num_next = - ((vout->pp_split_buf_num + (vout->pp_split << 0x1)) & 7) >> 2; - if ((!pp_out_buf_left_right) || - ((!pp_out_buf_up_down) && (vout->pp_split == 1))) { - if (vout->pp_split == 1) { - eba_offset = ((pp_out_buf_left_right + pp_out_buf_up_down) & 1) ? - vout->pp_right_stripe.input_column : - vout->pp_left_stripe.input_column; - vertical_offset = pp_out_buf_up_down ? - vout->pp_up_stripe.input_column : - vout->pp_down_stripe.input_column; - - } else { - eba_offset = pp_out_buf_left_right ? - vout->pp_left_stripe.input_column : - vout->pp_right_stripe.input_column; - vertical_offset = pp_out_buf_left_right ? - vout->pp_up_stripe.input_column : - vout->pp_down_stripe.input_column; - } - - ret = ipu_update_channel_buffer(vout->ipu, vout->post_proc_ch, - IPU_INPUT_BUFFER, - (1 - local_buffer), - (vout->v4l2_bufs[vout->ipu_buf[disp_buf_num]].m.offset) - + eba_offset + vertical_offset * vout->bytesperline); - ret += ipu_update_channel_offset(vout->ipu, vout->post_proc_ch, - IPU_INPUT_BUFFER, - vout->v2f.fmt.pix.pixelformat, - vout->v2f.fmt.pix.width, - vout->v2f.fmt.pix.height, - vout->bytesperline, - vout->offset.u_offset, - vout->offset.v_offset, - vertical_offset, - eba_offset); - - /* select right stripe */ - ret += ipu_select_buffer(vout->ipu, vout->post_proc_ch, IPU_INPUT_BUFFER, - (1 - local_buffer)); - if (ret < 0) - dev_err(&vout->video_dev->dev, - "unable to set IPU buffer ready\n"); - } - - /* offset for next buffer's EBA */ - eba_offset = 0; - if (vout->pp_split == 1) { - pp_out_buf_offset = ((vout->pp_split_buf_num >> 1) & 1) ? - vout->pp_left_stripe.output_column : - vout->pp_right_stripe.output_column; - - eba_offset = ((vout->pp_split_buf_num & 1) ? - vout->pp_down_stripe.output_column : - vout->pp_up_stripe.output_column); - - } else { - pp_out_buf_offset = ((vout->pp_split_buf_num >> 1) & 1) ? - vout->pp_right_stripe.output_column : - vout->pp_left_stripe.output_column; - eba_offset = ((vout->pp_split_buf_num >> 1) & 1) ? - vout->pp_down_stripe.output_column : - vout->pp_up_stripe.output_column; - } - - if (vout->cur_disp_output == 5) { - x_pos = (vout->crop_current.left / 8) * 8; - y_pos = vout->crop_current.top; - eba_offset += (vout->xres * y_pos + x_pos) * vout->bpp / 8; - } - - - /* next buffer update */ - eba_offset = vout->display_bufs[disp_buf_num_next] + - pp_out_buf_offset + eba_offset; - - ipu_update_channel_buffer(vout->ipu, vout->post_proc_ch, IPU_OUTPUT_BUFFER, - local_buffer, eba_offset); - - /* next buffer ready */ - ret = ipu_select_buffer(vout->ipu, vout->post_proc_ch, IPU_OUTPUT_BUFFER, local_buffer); - - /* next stripe_buffer index 0..7 */ - vout->pp_split_buf_num = (vout->pp_split_buf_num + vout->pp_split) & 0x7; - } else { - disp_buf_num = vout->next_done_ipu_buf; - ret += ipu_select_buffer(vout->ipu, vout->display_input_ch, IPU_OUTPUT_BUFFER, - vout->next_done_ipu_buf); - } - - /* release buffer. For split mode: if second stripe is done */ - release_buffer = vout->pp_split ? (!(vout->pp_split_buf_num & 0x3)) : 1; - if (release_buffer) { - if (vout->fb_blank == FB_BLANK_UNBLANK) - select_display_buffer(vout, disp_buf_num); - g_buf_output_cnt++; - vout->v4l2_bufs[last_buf].flags = V4L2_BUF_FLAG_DONE; - queue_buf(&vout->done_q, last_buf); - wake_up_interruptible(&vout->v4l_bufq); - vout->ipu_buf[vout->next_done_ipu_buf] = -1; - if (LOAD_3FIELDS(vout)) { - vout->ipu_buf_p[vout->next_done_ipu_buf] = -1; - vout->ipu_buf_n[vout->next_done_ipu_buf] = -1; - } - /* split mode use buf 0 only, no need switch buf */ - if (!vout->pp_split) - vout->next_done_ipu_buf = !vout->next_done_ipu_buf; - } - } /* end of last_buf != -1 */ - - index = peek_next_buf(&vout->ready_q); - if (vout->state == STATE_STREAM_STOPPING) { - if ((vout->ipu_buf[0] == -1) && (vout->ipu_buf[1] == -1)) { - vout->state = STATE_STREAM_OFF; - } - } else if ((vout->state == STATE_STREAM_PAUSED) - && (index != -1)) { - /* Setup timer for next buffer, when stream has been paused */ - pr_debug("next index %d\n", index); - setup_next_buf_timer(vout, index); - vout->state = STATE_STREAM_ON; - } - - spin_unlock_irqrestore(&g_lock, lock_flags); - - return IRQ_HANDLED; -} - -/*! - * Initialize VDI channels - * - * @param vout structure vout_data * - * - * @return status 0 Success - */ -static int init_VDI_channel(vout_data *vout, ipu_channel_params_t params) -{ - struct device *dev = &vout->video_dev->dev; - - if (ipu_init_channel(vout->ipu, MEM_VDI_PRP_VF_MEM, ¶ms) != 0) { - dev_dbg(dev, "Error initializing VDI current channel\n"); - return -EINVAL; - } - if (LOAD_3FIELDS(vout)) { - if (ipu_init_channel(vout->ipu, MEM_VDI_PRP_VF_MEM_P, ¶ms) != 0) { - dev_err(dev, "Error initializing VDI previous channel\n"); - return -EINVAL; - } - if (ipu_init_channel(vout->ipu, MEM_VDI_PRP_VF_MEM_N, ¶ms) != 0) { - dev_err(dev, "Error initializing VDI next channel\n"); - return -EINVAL; - } - } - return 0; -} - -/*! - * Initialize VDI channel buffers - * - * @param vout structure vout_data * - * - * @return status 0 Success - */ -static int init_VDI_in_channel_buffer(vout_data *vout, uint32_t in_pixel_fmt, - uint16_t in_width, uint16_t in_height, - uint32_t stride, - uint32_t u_offset, uint32_t v_offset) -{ - struct device *dev = &vout->video_dev->dev; - - if (ipu_init_channel_buffer(vout->ipu, MEM_VDI_PRP_VF_MEM, IPU_INPUT_BUFFER, - in_pixel_fmt, in_width, in_height, stride, - IPU_ROTATE_NONE, - vout->v4l2_bufs[vout->ipu_buf[0]].m.offset, - vout->v4l2_bufs[vout->ipu_buf[0]].m.offset, - 0, - u_offset, v_offset) != 0) { - dev_err(dev, "Error initializing VDI current input buffer\n"); - return -EINVAL; - } - if (LOAD_3FIELDS(vout)) { - if (ipu_init_channel_buffer(vout->ipu, MEM_VDI_PRP_VF_MEM_P, - IPU_INPUT_BUFFER, - in_pixel_fmt, in_width, in_height, - stride, IPU_ROTATE_NONE, - vout->v4l2_bufs[vout->ipu_buf_p[0]].m.offset+vout->bytesperline, - vout->v4l2_bufs[vout->ipu_buf_p[0]].m.offset+vout->bytesperline, - 0, - u_offset, v_offset) != 0) { - dev_err(dev, "Error initializing VDI previous input buffer\n"); - return -EINVAL; - } - if (ipu_init_channel_buffer(vout->ipu, MEM_VDI_PRP_VF_MEM_N, - IPU_INPUT_BUFFER, - in_pixel_fmt, in_width, in_height, - stride, IPU_ROTATE_NONE, - vout->v4l2_bufs[vout->ipu_buf_n[0]].m.offset+vout->bytesperline, - vout->v4l2_bufs[vout->ipu_buf_n[0]].m.offset+vout->bytesperline, - 0, - u_offset, v_offset) != 0) { - dev_err(dev, "Error initializing VDI next input buffer\n"); - return -EINVAL; - } - } - return 0; -} - -/*! - * Initialize VDI path - * - * @param vout structure vout_data * - * - * @return status 0 Success - */ -static int init_VDI(ipu_channel_params_t params, vout_data *vout, - struct device *dev, struct fb_info *fbi, - u16 out_width, u16 out_height) -{ - params.mem_prp_vf_mem.in_width = vout->v2f.fmt.pix.width; - params.mem_prp_vf_mem.in_height = vout->v2f.fmt.pix.height; - params.mem_prp_vf_mem.motion_sel = vout->motion_sel; - params.mem_prp_vf_mem.field_fmt = vout->field_fmt; - params.mem_prp_vf_mem.in_pixel_fmt = vout->v2f.fmt.pix.pixelformat; - params.mem_prp_vf_mem.out_width = out_width; - params.mem_prp_vf_mem.out_height = out_height; - params.mem_prp_vf_mem.out_pixel_fmt = bpp_to_fmt(fbi); - - if (init_VDI_channel(vout, params) != 0) { - dev_err(dev, "Error init_VDI_channel channel\n"); - return -EINVAL; - } - - if (init_VDI_in_channel_buffer(vout, - params.mem_prp_vf_mem.in_pixel_fmt, - params.mem_prp_vf_mem.in_width, - params.mem_prp_vf_mem.in_height, - bytes_per_pixel(params.mem_prp_vf_mem. - in_pixel_fmt), - vout->offset.u_offset, - vout->offset.v_offset) != 0) { - return -EINVAL; - } - - if (!ipu_can_rotate_in_place(vout->rotate)) { - if (vout->rot_pp_bufs[0]) { - mxc_free_buffers(vout->rot_pp_bufs, - vout->rot_pp_bufs_vaddr, 2, - vout->display_buf_size); - } - if (mxc_allocate_buffers - (vout->rot_pp_bufs, vout->rot_pp_bufs_vaddr, 2, - vout->display_buf_size) < 0) { - return -ENOBUFS; - } - - if (ipu_init_channel_buffer(vout->ipu, vout->post_proc_ch, - IPU_OUTPUT_BUFFER, - params.mem_prp_vf_mem. - out_pixel_fmt, out_width, - out_height, out_width, - IPU_ROTATE_NONE, - vout->rot_pp_bufs[0], - vout->rot_pp_bufs[1], 0, 0, 0) != 0) { - dev_err(dev, "Error initializing PRP output buffer\n"); - return -EINVAL; - } - - if (ipu_init_channel(vout->ipu, MEM_ROT_VF_MEM, NULL) != 0) { - dev_err(dev, "Error initializing PP ROT channel\n"); - return -EINVAL; - } - if (ipu_init_channel_buffer(vout->ipu, MEM_ROT_VF_MEM, - IPU_INPUT_BUFFER, - params.mem_prp_vf_mem. - out_pixel_fmt, out_width, - out_height, out_width, - vout->rotate, - vout->rot_pp_bufs[0], - vout->rot_pp_bufs[1], 0, 0, 0) != 0) { - dev_err(dev, - "Error initializing PP ROT input buffer\n"); - return -EINVAL; - } - - /* swap width and height */ - if (vout->rotate >= IPU_ROTATE_90_RIGHT) { - out_width = vout->crop_current.width; - out_height = vout->crop_current.height; - } - - if (ipu_init_channel_buffer(vout->ipu, MEM_ROT_VF_MEM, - IPU_OUTPUT_BUFFER, - params.mem_prp_vf_mem. - out_pixel_fmt, out_width, - out_height, out_width, - IPU_ROTATE_NONE, - vout->display_bufs[0], - vout->display_bufs[1], 0, 0, 0) != 0) { - dev_err(dev, - "Error initializing PP-VDI output buffer\n"); - return -EINVAL; - } - - if (ipu_link_channels(vout->ipu, vout->post_proc_ch, MEM_ROT_VF_MEM) < 0) - return -EINVAL; - - vout->display_input_ch = MEM_ROT_VF_MEM; - ipu_enable_channel(vout->ipu, MEM_ROT_VF_MEM); - ipu_select_buffer(vout->ipu, MEM_ROT_VF_MEM, IPU_OUTPUT_BUFFER, 0); - ipu_select_buffer(vout->ipu, MEM_ROT_VF_MEM, IPU_OUTPUT_BUFFER, 1); - } else { - if (ipu_init_channel_buffer(vout->ipu, vout->post_proc_ch, - IPU_OUTPUT_BUFFER, - params.mem_prp_vf_mem. - out_pixel_fmt, out_width, - out_height, out_width, - vout->rotate, - vout->display_bufs[0], - vout->display_bufs[1], 0, 0, 0) != 0) { - dev_err(dev, - "Error initializing PP-VDI output buffer\n"); - return -EINVAL; - } - } - return 0; -} - -/*! - * Initialize PP path - * - * @param params structure ipu_channel_params_t - * - * @param vout structure vout_data * - * - * @return status 0 Success - */ -static int init_PP(ipu_channel_params_t *params, vout_data *vout, - struct device *dev, struct fb_info *fbi, - u16 out_width, u16 out_height) -{ - u16 in_width, out_stride; /* stride of output channel */ - u32 eba_offset; - u16 x_pos; - u16 y_pos; - dma_addr_t phy_addr0; - dma_addr_t phy_addr1; - - eba_offset = 0; - x_pos = 0; - y_pos = 0; - - params->mem_pp_mem.out_pixel_fmt = bpp_to_fmt(fbi); - - if (vout->cur_disp_output == 5) { - x_pos = (vout->crop_current.left / 8) * 8; - y_pos = vout->crop_current.top; - eba_offset = (vout->xres*y_pos + x_pos) * - bytes_per_pixel(params->mem_pp_mem.out_pixel_fmt); - } - - vout->bpp = fmt_to_bpp(params->mem_pp_mem.out_pixel_fmt); - out_stride = vout->xres * - bytes_per_pixel(params->mem_pp_mem.out_pixel_fmt); - in_width = params->mem_pp_mem.in_width = vout->v2f.fmt.pix.width; - params->mem_pp_mem.in_height = vout->v2f.fmt.pix.height; - params->mem_pp_mem.in_pixel_fmt = vout->v2f.fmt.pix.pixelformat; - params->mem_pp_mem.out_width = out_width; - params->mem_pp_mem.out_height = out_height; - params->mem_pp_mem.outh_resize_ratio = 0; /* 0 means unused */ - params->mem_pp_mem.outv_resize_ratio = 0; /* 0 means unused */ - /* split IC by two stripes, the by pass is impossible*/ - if (vout->pp_split) { - vout->pp_left_stripe.input_column = 0; - vout->pp_left_stripe.output_column = 0; - vout->pp_right_stripe.input_column = 0; - vout->pp_right_stripe.output_column = 0; - vout->pp_up_stripe.input_column = 0; - vout->pp_up_stripe.output_column = 0; - vout->pp_down_stripe.input_column = 0; - vout->pp_down_stripe.output_column = 0; - if (vout->pp_split != 3) { - ipu_calc_stripes_sizes( - params->mem_pp_mem.in_width, /* input frame width;>1 */ - params->mem_pp_mem.out_width, /* output frame width; >1 */ - ipu_ic_out_max_width_size, - (((unsigned long long)1) << 32), /* 32bit for fractional*/ - 1, /* equal stripes */ - params->mem_pp_mem.in_pixel_fmt, - params->mem_pp_mem.out_pixel_fmt, - &(vout->pp_left_stripe), - &(vout->pp_right_stripe)); - - vout->pp_left_stripe.input_column = vout->pp_left_stripe.input_column * - fmt_to_bpp(vout->v2f.fmt.pix.pixelformat) / 8; - vout->pp_left_stripe.output_column = vout->pp_left_stripe.output_column * - fmt_to_bpp(params->mem_pp_mem.out_pixel_fmt) / 8; - vout->pp_right_stripe.input_column = vout->pp_right_stripe.input_column * - fmt_to_bpp(vout->v2f.fmt.pix.pixelformat) / 8; - vout->pp_right_stripe.output_column = vout->pp_right_stripe.output_column * - fmt_to_bpp(params->mem_pp_mem.out_pixel_fmt) / 8; - - - /* updare parameters */ - params->mem_pp_mem.in_width = vout->pp_left_stripe.input_width; - params->mem_pp_mem.out_width = vout->pp_left_stripe.output_width; - out_width = vout->pp_left_stripe.output_width; - /* for using in ic_init*/ - params->mem_pp_mem.outh_resize_ratio = vout->pp_left_stripe.irr; - } - if (vout->pp_split != 2) { - ipu_calc_stripes_sizes( - params->mem_pp_mem.in_height, /* input frame width;>1 */ - params->mem_pp_mem.out_height, /* output frame width; >1 */ - ipu_ic_out_max_height_size, - (((unsigned long long)1) << 32),/* 32bit for fractional */ - 1, /* equal stripes */ - params->mem_pp_mem.in_pixel_fmt, - params->mem_pp_mem.out_pixel_fmt, - &(vout->pp_up_stripe), - &(vout->pp_down_stripe)); - vout->pp_down_stripe.output_column = vout->pp_down_stripe.output_column * out_stride; - vout->pp_up_stripe.output_column = vout->pp_up_stripe.output_column * out_stride; - params->mem_pp_mem.outv_resize_ratio = vout->pp_up_stripe.irr; - params->mem_pp_mem.in_height = vout->pp_up_stripe.input_width;/*height*/ - out_height = vout->pp_up_stripe.output_width;/*height*/ - if (vout->pp_split == 3) - vout->pp_split = 2;/*2 vertical stripe as two horizontal stripes */ - } - vout->pp_split_buf_num = 0; - } - - if (ipu_init_channel(vout->ipu, vout->post_proc_ch, params) != 0) { - dev_err(dev, "Error initializing PP channel\n"); - return -EINVAL; - } - - /* always enable double buffer */ - phy_addr0 = vout->v4l2_bufs[vout->ipu_buf[0]].m.offset; - if (vout->ipu_buf[1] == -1) - phy_addr1 = phy_addr0; - else - phy_addr1 = vout->v4l2_bufs[vout->ipu_buf[1]].m.offset; - if (ipu_init_channel_buffer(vout->ipu, vout->post_proc_ch, - IPU_INPUT_BUFFER, - params->mem_pp_mem.in_pixel_fmt, - params->mem_pp_mem.in_width, - params->mem_pp_mem.in_height, - vout->v2f.fmt.pix.bytesperline / - bytes_per_pixel(params->mem_pp_mem. - in_pixel_fmt), - IPU_ROTATE_NONE, - phy_addr0, - phy_addr1, - 0, - vout->offset.u_offset, - vout->offset.v_offset) != 0) { - dev_err(dev, "Error initializing PP input buffer\n"); - return -EINVAL; - } - - if (!ipu_can_rotate_in_place(vout->rotate)) { - if (vout->rot_pp_bufs[0]) { - mxc_free_buffers(vout->rot_pp_bufs, - vout->rot_pp_bufs_vaddr, 2, - vout->display_buf_size); - } - if (mxc_allocate_buffers - (vout->rot_pp_bufs, vout->rot_pp_bufs_vaddr, 2, - vout->display_buf_size) < 0) { - return -ENOBUFS; - } - - if (ipu_init_channel_buffer(vout->ipu, vout->post_proc_ch, - IPU_OUTPUT_BUFFER, - params->mem_pp_mem. - out_pixel_fmt, out_width, - out_height, out_stride, - IPU_ROTATE_NONE, - vout->rot_pp_bufs[0] + eba_offset, - vout->rot_pp_bufs[1] + eba_offset, 0, 0, 0) != 0) { - dev_err(dev, "Error initializing PP output buffer\n"); - return -EINVAL; - } - - if (ipu_init_channel(vout->ipu, MEM_ROT_PP_MEM, NULL) != 0) { - dev_err(dev, "Error initializing PP ROT channel\n"); - return -EINVAL; - } - if (ipu_init_channel_buffer(vout->ipu, MEM_ROT_PP_MEM, - IPU_INPUT_BUFFER, - params->mem_pp_mem. - out_pixel_fmt, out_width, - out_height, out_stride, - vout->rotate, - vout->rot_pp_bufs[0], - vout->rot_pp_bufs[1], 0, 0, 0) != 0) { - dev_err(dev, - "Error initializing PP ROT input buffer\n"); - return -EINVAL; - } - - /* swap width and height */ - if (vout->rotate >= IPU_ROTATE_90_RIGHT) { - out_width = vout->crop_current.width; - out_height = vout->crop_current.height; - } - - if (ipu_init_channel_buffer(vout->ipu, MEM_ROT_PP_MEM, - IPU_OUTPUT_BUFFER, - params->mem_pp_mem. - out_pixel_fmt, out_width, - out_height, out_stride, - IPU_ROTATE_NONE, - vout->display_bufs[0] + eba_offset, - vout->display_bufs[1] + eba_offset, 0, 0, 0) != 0) { - dev_err(dev, "Error initializing PP output buffer\n"); - return -EINVAL; - } - - if (ipu_link_channels(vout->ipu, vout->post_proc_ch, MEM_ROT_PP_MEM) < 0) - return -EINVAL; - - vout->display_input_ch = MEM_ROT_PP_MEM; - ipu_enable_channel(vout->ipu, MEM_ROT_PP_MEM); - ipu_select_buffer(vout->ipu, MEM_ROT_PP_MEM, IPU_OUTPUT_BUFFER, 0); - ipu_select_buffer(vout->ipu, MEM_ROT_PP_MEM, IPU_OUTPUT_BUFFER, 1); - } else { - if (ipu_init_channel_buffer(vout->ipu, vout->post_proc_ch, - IPU_OUTPUT_BUFFER, - params->mem_pp_mem. - out_pixel_fmt, out_width, - out_height, out_stride, - vout->rotate, - vout->display_bufs[0] + eba_offset, - vout->display_bufs[1] + eba_offset, 0, 0, 0) != 0) { - dev_err(dev, "Error initializing PP output buffer\n"); - return -EINVAL; - } - } - - /* fix EBAs for IDMAC channels */ - if (vout->pp_split) { - ipu_update_channel_buffer(vout->ipu, vout->post_proc_ch, IPU_INPUT_BUFFER, - 0, - vout->v4l2_bufs[vout->ipu_buf[0]].m.offset + - vout->pp_left_stripe.input_column + - vout->pp_up_stripe.input_column * vout->bytesperline); - - - ipu_update_channel_buffer(vout->ipu, vout->post_proc_ch, IPU_INPUT_BUFFER, - 1, - vout->v4l2_bufs[vout->ipu_buf[0]].m.offset + - vout->pp_right_stripe.input_column + - vout->pp_up_stripe.input_column * vout->bytesperline); - - ipu_update_channel_buffer(vout->ipu, vout->post_proc_ch, IPU_OUTPUT_BUFFER, - 0, - vout->display_bufs[0] + eba_offset + - vout->pp_left_stripe.output_column + - vout->pp_up_stripe.output_column); - - ipu_update_channel_buffer(vout->ipu, vout->post_proc_ch, IPU_OUTPUT_BUFFER, - 1, - vout->display_bufs[0] + eba_offset + - vout->pp_right_stripe.output_column + - vout->pp_up_stripe.output_column); - } - - return 0; -} - -/*! - * Start the output stream - * - * @param vout structure vout_data * - * - * @return status 0 Success - */ -static int mxc_v4l2out_streamon(vout_data *vout) -{ - struct device *dev = &vout->video_dev->dev; - ipu_channel_params_t params; - struct mxcfb_pos fb_pos; - struct fb_var_screeninfo fbvar; - struct fb_info *fbi = - registered_fb[vout->output_fb_num[vout->cur_disp_output]]; - u16 out_width; - u16 out_height; - mm_segment_t old_fs; - unsigned int ipu_ch = CHAN_NONE; - unsigned int fb_fmt; - int rc = 0; - - dev_dbg(dev, "mxc_v4l2out_streamon: field format=%d\n", - vout->field_fmt); - - if (!vout) - return -EINVAL; - - if (vout->state != STATE_STREAM_OFF) - return -EBUSY; - - if (queue_size(&vout->ready_q) < 2) { - dev_err(dev, "2 buffers not been queued yet!\n"); - return -EINVAL; - } - - if ((vout->field_fmt == V4L2_FIELD_BOTTOM) || (vout->field_fmt == V4L2_FIELD_TOP)) { - dev_err(dev, "4 queued buffers need, not supported yet!\n"); - return -EINVAL; - } - - /* - * params init, check whether operation exceed the IC limitation: - * whether split mode used ( ipu version >= ipuv3 only) - */ - g_irq_cnt = g_buf_output_cnt = g_buf_q_cnt = g_buf_dq_cnt = 0; - out_width = vout->crop_current.width; - out_height = vout->crop_current.height; - vout->disp_buf_num = 0; - vout->next_done_ipu_buf = 0; - vout->next_rdy_ipu_buf = vout->next_disp_ipu_buf = 1; - vout->pp_split = 0; - ipu_ic_out_max_height_size = 1024; -#ifdef CONFIG_MXC_IPU_V1 - if (cpu_is_mx35()) - ipu_ic_out_max_width_size = 800; - else - ipu_ic_out_max_width_size = 720; -#else - ipu_ic_out_max_width_size = 1024; -#endif - if ((out_width > ipu_ic_out_max_width_size) || - (out_height > ipu_ic_out_max_height_size)) - vout->pp_split = 4; - if (!INTERLACED_CONTENT(vout)) { - vout->ipu_buf[0] = dequeue_buf(&vout->ready_q); - /* split IC by two stripes, the by pass is impossible*/ - if ((out_width != vout->v2f.fmt.pix.width || - out_height != vout->v2f.fmt.pix.height) && - vout->pp_split) { - vout->next_done_ipu_buf = vout->next_rdy_ipu_buf = 0; - vout->ipu_buf[1] = vout->ipu_buf[0]; - vout->frame_count = 1; - if ((out_width > ipu_ic_out_max_width_size) && - (out_height > ipu_ic_out_max_height_size)) - vout->pp_split = 1; /*4 stripes*/ - else if (!(out_height > ipu_ic_out_max_height_size)) - vout->pp_split = 2; /*two horizontal stripes */ - else - vout->pp_split = 3; /*2 vertical stripes*/ - } else { - vout->ipu_buf[1] = -1; - vout->frame_count = 1; - } - } else if (!LOAD_3FIELDS(vout)) { - vout->ipu_buf[0] = dequeue_buf(&vout->ready_q); - vout->ipu_buf[1] = -1; - vout->frame_count = 1; - } else { - vout->ipu_buf_p[0] = dequeue_buf(&vout->ready_q); - vout->ipu_buf[0] = dequeue_buf(&vout->ready_q); - vout->ipu_buf_n[0] = vout->ipu_buf[0]; - vout->ipu_buf_p[1] = -1; - vout->ipu_buf[1] = -1; - vout->ipu_buf_n[1] = -1; - last_index_n = vout->ipu_buf_n[0]; - vout->frame_count = 2; - } - - /* - * Bypass IC if resizing and rotation are not needed - * Meanwhile, apply IC bypass to SDC only - */ - fbvar = fbi->var; - vout->xres = fbvar.xres; - vout->yres = fbvar.yres; - - if (vout->cur_disp_output == 3 || vout->cur_disp_output == 5) { - fb_fmt = vout->v2f.fmt.pix.pixelformat; - - /* DC channel can not use CSC */ - if (vout->cur_disp_output == 5) { - if (fbi->fbops->fb_ioctl) { - old_fs = get_fs(); - set_fs(KERNEL_DS); - fbi->fbops->fb_ioctl(fbi, MXCFB_GET_DIFMT, - (unsigned long)&fb_fmt); - set_fs(old_fs); - } - } - - fbvar.bits_per_pixel = 16; - if (format_is_yuv(fb_fmt)) - fbvar.nonstd = IPU_PIX_FMT_UYVY; - else - fbvar.nonstd = 0; - if (vout->cur_disp_output == 3) { - fbvar.xres = out_width; - fbvar.yres = out_height; - vout->xres = fbvar.xres; - vout->yres = fbvar.yres; - } - - fbvar.xres_virtual = fbvar.xres; - fbvar.yres_virtual = fbvar.yres * 2; - } - - if (out_width == vout->v2f.fmt.pix.width && - out_height == vout->v2f.fmt.pix.height && - vout->xres == out_width && - vout->yres == out_height && - ipu_can_rotate_in_place(vout->rotate) && - (vout->bytesperline == - bytes_per_pixel(vout->v2f.fmt.pix.pixelformat) * out_width) && - !INTERLACED_CONTENT(vout)) { - vout->ic_bypass = 1; - } else { - vout->ic_bypass = 0; - } - -#ifdef CONFIG_MXC_IPU_V1 - /* IPUv1 needs IC to do CSC */ - if (format_is_yuv(vout->v2f.fmt.pix.pixelformat) != - format_is_yuv(bpp_to_fmt(fbi))) -#else - /* DC channel needs IC to do CSC */ - if ((format_is_yuv(vout->v2f.fmt.pix.pixelformat) != - format_is_yuv(fb_fmt)) && - (vout->cur_disp_output == 5)) - vout->ic_bypass = 0; -#endif - - if (fbi->fbops->fb_ioctl) { - old_fs = get_fs(); - set_fs(KERNEL_DS); - fbi->fbops->fb_ioctl(fbi, MXCFB_GET_FB_IPU_CHAN, - (unsigned long)&ipu_ch); - set_fs(old_fs); - } - - if (ipu_ch == CHAN_NONE) { - dev_err(dev, "Can not get display ipu channel\n"); - return -EINVAL; - } - - vout->display_ch = ipu_ch; - - if (vout->ic_bypass) { - dev_info(dev, "Bypassing IC\n"); - vout->pp_split = 0; - switch (vout->v2f.fmt.pix.pixelformat) { - case V4L2_PIX_FMT_YUV420: - case V4L2_PIX_FMT_YVU420: - case V4L2_PIX_FMT_NV12: - fbvar.bits_per_pixel = 12; - break; - case V4L2_PIX_FMT_YUV422P: - fbvar.bits_per_pixel = 16; - break; - default: - fbvar.bits_per_pixel = 8* - bytes_per_pixel(vout->v2f.fmt.pix.pixelformat); - } - fbvar.nonstd = vout->v2f.fmt.pix.pixelformat; - } - - /* Init display channel through fb API */ - fbvar.yoffset = 0; - fbvar.accel_flags = FB_ACCEL_DOUBLE_FLAG; - fbvar.activate |= FB_ACTIVATE_FORCE; - console_lock(); - fbi->flags |= FBINFO_MISC_USEREVENT; - fb_set_var(fbi, &fbvar); - fbi->flags &= ~FBINFO_MISC_USEREVENT; - console_unlock(); - - if (fbi->fbops->fb_ioctl && vout->display_ch == MEM_FG_SYNC) { - fb_pos.x = vout->crop_current.left; - fb_pos.y = vout->crop_current.top; - old_fs = get_fs(); - set_fs(KERNEL_DS); - fbi->fbops->fb_ioctl(fbi, MXCFB_SET_OVERLAY_POS, - (unsigned long)&fb_pos); - set_fs(old_fs); - } - - vout->display_bufs[1] = fbi->fix.smem_start; - vout->display_bufs[0] = fbi->fix.smem_start + - (fbi->fix.line_length * vout->yres); - vout->display_buf_size = vout->xres * - vout->yres * fbi->var.bits_per_pixel / 8; - - /* fill black color for init fb, we assume fb has double buffer*/ - if (format_is_yuv(vout->v2f.fmt.pix.pixelformat)) { - int i; - - if ((vout->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY) || - (vout->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) || - (!vout->ic_bypass)) { - short * tmp = (short *) fbi->screen_base; - short color; - if (vout->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) - color = 0x8000; - else - color = 0x80; - for (i = 0; i < (fbi->fix.line_length * fbi->var.yres_virtual)/2; - i++, tmp++) - *tmp = color; - } else if ((vout->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) || - (vout->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YVU420) || - (vout->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_NV12)) { - char * base = (char *)fbi->screen_base; - int j, screen_size = fbi->var.xres * fbi->var.yres; - - for (j = 0; j < 2; j++) { - memset(base, 0, screen_size); - base += screen_size; - for (i = 0; i < screen_size/2; i++, base++) - *base = 0x80; - } - } else if (vout->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P) { - char * base = (char *)fbi->screen_base; - int j, screen_size = fbi->var.xres * fbi->var.yres; - - for (j = 0; j < 2; j++) { - memset(base, 0, screen_size); - base += screen_size; - for (i = 0; i < screen_size; i++, base++) - *base = 0x80; - } - } - } else - memset(fbi->screen_base, 0x0, - fbi->fix.line_length * fbi->var.yres_virtual); - - if (INTERLACED_CONTENT(vout)) - vout->post_proc_ch = MEM_VDI_PRP_VF_MEM; - else if (!vout->ic_bypass) - vout->post_proc_ch = MEM_PP_MEM; - - /* Init IC channel */ - if (!vout->ic_bypass) { - if (vout->rotate >= IPU_ROTATE_90_RIGHT) { - out_width = vout->crop_current.height; - out_height = vout->crop_current.width; - } - vout->display_input_ch = vout->post_proc_ch; - memset(¶ms, 0, sizeof(params)); - if (INTERLACED_CONTENT(vout)) { - if (vout->pp_split) { - dev_err(&vout->video_dev->dev, "VDI split has not supported yet.\n"); - return -1; - } else - rc = init_VDI(params, vout, dev, fbi, out_width, out_height); - } else { - rc = init_PP(¶ms, vout, dev, fbi, out_width, out_height); - } - if (rc < 0) - return rc; - } - - if (!vout->ic_bypass) { - switch (vout->display_input_ch) { - case MEM_PP_MEM: - vout->work_irq = IPU_IRQ_PP_OUT_EOF; - break; - case MEM_VDI_PRP_VF_MEM: - vout->work_irq = IPU_IRQ_PRP_VF_OUT_EOF; - break; - case MEM_ROT_VF_MEM: - vout->work_irq = IPU_IRQ_PRP_VF_ROT_OUT_EOF; - break; - case MEM_ROT_PP_MEM: - vout->work_irq = IPU_IRQ_PP_ROT_OUT_EOF; - break; - default: - dev_err(&vout->video_dev->dev, - "not support channel, should not be here\n"); - } - } else - vout->work_irq = -1; - - if (!vout->ic_bypass && (vout->work_irq > 0)) { - ipu_clear_irq(vout->ipu, vout->work_irq); - ipu_request_irq(vout->ipu, vout->work_irq, - mxc_v4l2out_work_irq_handler, - 0, vout->video_dev->name, vout); - } - - vout->state = STATE_STREAM_PAUSED; - - /* Enable display and IC channels */ - if (fbi) { - console_lock(); - fb_blank(fbi, FB_BLANK_UNBLANK); - console_unlock(); - vout->fb_blank = FB_BLANK_UNBLANK; - } else { - ipu_enable_channel(vout->ipu, vout->display_ch); - } - - /* correct display ch buffer address */ - ipu_update_channel_buffer(vout->ipu, vout->display_ch, IPU_INPUT_BUFFER, - 0, vout->display_bufs[0]); - ipu_update_channel_buffer(vout->ipu, vout->display_ch, IPU_INPUT_BUFFER, - 1, vout->display_bufs[1]); - - if (!vout->ic_bypass) { -#ifndef CONFIG_MXC_IPU_V1 - ipu_enable_channel(vout->ipu, vout->post_proc_ch); -#endif - if (LOAD_3FIELDS(vout)) { - ipu_enable_channel(vout->ipu, MEM_VDI_PRP_VF_MEM_P); - ipu_enable_channel(vout->ipu, MEM_VDI_PRP_VF_MEM_N); - ipu_select_multi_vdi_buffer(vout->ipu, 0); - } else - ipu_select_buffer(vout->ipu, vout->post_proc_ch, IPU_INPUT_BUFFER, 0); - ipu_select_buffer(vout->ipu, vout->post_proc_ch, IPU_OUTPUT_BUFFER, 0); - ipu_select_buffer(vout->ipu, vout->post_proc_ch, IPU_OUTPUT_BUFFER, 1); -#ifdef CONFIG_MXC_IPU_V1 - ipu_enable_channel(vout->ipu, vout->post_proc_ch); -#endif - } else { - ipu_update_channel_buffer(vout->ipu, vout->display_ch, - IPU_INPUT_BUFFER, - 0, vout->v4l2_bufs[vout->ipu_buf[0]].m.offset); - if (vout->offset.u_offset || vout->offset.v_offset) - /* only update u/v offset */ - ipu_update_channel_offset(vout->ipu, vout->display_ch, - IPU_INPUT_BUFFER, - vout->v2f.fmt.pix.pixelformat, - vout->v2f.fmt.pix.width, - vout->v2f.fmt.pix.height, - vout->bytesperline, - vout->offset.u_offset, - vout->offset.v_offset, - 0, - 0); - ipu_select_buffer(vout->ipu, vout->display_ch, IPU_INPUT_BUFFER, 0); - queue_work(vout->v4l_wq, &vout->icbypass_work); - } - - vout->start_jiffies = jiffies; - - msleep(1); - - dev_dbg(dev, - "streamon: start time = %lu jiffies\n", vout->start_jiffies); - - return 0; -} - -/*! - * Shut down the voutera - * - * @param vout structure vout_data * - * - * @return status 0 Success - */ -static int mxc_v4l2out_streamoff(vout_data *vout) -{ - struct fb_info *fbi = - registered_fb[vout->output_fb_num[vout->cur_disp_output]]; - int i, retval = 0; - unsigned long lockflag = 0; - - if (!vout) - return -EINVAL; - - if (vout->state == STATE_STREAM_OFF) { - return 0; - } - - if (!vout->ic_bypass) - ipu_free_irq(vout->ipu, vout->work_irq, vout); - - if (vout->ic_bypass) - cancel_work_sync(&vout->icbypass_work); - - /* fill black color for fb, we assume fb has double buffer*/ - if (format_is_yuv(vout->v2f.fmt.pix.pixelformat)) { - int i; - - if ((vout->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY) || - (vout->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) || - (!vout->ic_bypass)) { - short * tmp = (short *) fbi->screen_base; - short color; - if (vout->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) - color = 0x8000; - else - color = 0x80; - for (i = 0; i < (fbi->fix.line_length * fbi->var.yres_virtual)/2; - i++, tmp++) - *tmp = color; - } else if ((vout->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) || - (vout->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YVU420) || - (vout->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_NV12)) { - char * base = (char *)fbi->screen_base; - int j, screen_size = fbi->var.xres * fbi->var.yres; - - for (j = 0; j < 2; j++) { - memset(base, 0, screen_size); - base += screen_size; - for (i = 0; i < screen_size/2; i++, base++) - *base = 0x80; - } - } else if (vout->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV422P) { - char * base = (char *)fbi->screen_base; - int j, screen_size = fbi->var.xres * fbi->var.yres; - - for (j = 0; j < 2; j++) { - memset(base, 0, screen_size); - base += screen_size; - for (i = 0; i < screen_size; i++, base++) - *base = 0x80; - } - } - } else - memset(fbi->screen_base, 0x0, - fbi->fix.line_length * fbi->var.yres_virtual); - - spin_lock_irqsave(&g_lock, lockflag); - - del_timer(&vout->output_timer); - - if (vout->state == STATE_STREAM_ON) { - vout->state = STATE_STREAM_STOPPING; - } - - spin_unlock_irqrestore(&g_lock, lockflag); - - if (vout->display_ch == MEM_FG_SYNC) { - struct mxcfb_pos fb_pos; - mm_segment_t old_fs; - - fb_pos.x = 0; - fb_pos.y = 0; - if (fbi->fbops->fb_ioctl) { - old_fs = get_fs(); - set_fs(KERNEL_DS); - fbi->fbops->fb_ioctl(fbi, MXCFB_SET_OVERLAY_POS, - (unsigned long)&fb_pos); - set_fs(old_fs); - } - } - - if (vout->ic_bypass) { - fbi->var.accel_flags = FB_ACCEL_TRIPLE_FLAG; - fbi->var.activate |= FB_ACTIVATE_FORCE; - console_lock(); - fbi->flags |= FBINFO_MISC_USEREVENT; - fb_set_var(fbi, &fbi->var); - fbi->flags &= ~FBINFO_MISC_USEREVENT; - console_unlock(); - - if (vout->display_ch == MEM_FG_SYNC) { - console_lock(); - fb_blank(fbi, FB_BLANK_POWERDOWN); - console_unlock(); - } - - vout->display_bufs[0] = 0; - vout->display_bufs[1] = 0; - } else if (vout->post_proc_ch == MEM_PP_MEM || - vout->post_proc_ch == MEM_PRP_VF_MEM) { - /* SDC with Rotation */ - if (!ipu_can_rotate_in_place(vout->rotate)) { - ipu_unlink_channels(vout->ipu, MEM_PP_MEM, MEM_ROT_PP_MEM); - ipu_disable_channel(vout->ipu, MEM_ROT_PP_MEM, true); - - if (vout->rot_pp_bufs[0]) { - mxc_free_buffers(vout->rot_pp_bufs, - vout->rot_pp_bufs_vaddr, 2, - vout->display_buf_size); - } - } - ipu_disable_channel(vout->ipu, MEM_PP_MEM, true); - - fbi->var.accel_flags = FB_ACCEL_TRIPLE_FLAG; - fbi->var.activate |= FB_ACTIVATE_FORCE; - console_lock(); - fbi->flags |= FBINFO_MISC_USEREVENT; - fb_set_var(fbi, &fbi->var); - fbi->flags &= ~FBINFO_MISC_USEREVENT; - console_unlock(); - - if (vout->display_ch == MEM_FG_SYNC) { - console_lock(); - fb_blank(fbi, FB_BLANK_POWERDOWN); - console_unlock(); - } - - vout->display_bufs[0] = 0; - vout->display_bufs[1] = 0; - - ipu_uninit_channel(vout->ipu, MEM_PP_MEM); - if (!ipu_can_rotate_in_place(vout->rotate)) - ipu_uninit_channel(vout->ipu, MEM_ROT_PP_MEM); - } else if (INTERLACED_CONTENT(vout) && - (vout->post_proc_ch == MEM_VDI_PRP_VF_MEM)) { - if (!ipu_can_rotate_in_place(vout->rotate)) { - ipu_unlink_channels(vout->ipu, MEM_VDI_PRP_VF_MEM, - MEM_ROT_VF_MEM); - ipu_disable_channel(vout->ipu, MEM_ROT_VF_MEM, true); - - if (vout->rot_pp_bufs[0]) { - mxc_free_buffers(vout->rot_pp_bufs, - vout->rot_pp_bufs_vaddr, 2, - vout->display_buf_size); - } - } - - ipu_disable_channel(vout->ipu, MEM_VDI_PRP_VF_MEM, true); - if (LOAD_3FIELDS(vout)) { - ipu_disable_channel(vout->ipu, MEM_VDI_PRP_VF_MEM_P, true); - ipu_disable_channel(vout->ipu, MEM_VDI_PRP_VF_MEM_N, true); - } - - fbi->var.accel_flags = FB_ACCEL_TRIPLE_FLAG; - fbi->var.activate |= FB_ACTIVATE_FORCE; - console_lock(); - fbi->flags |= FBINFO_MISC_USEREVENT; - fb_set_var(fbi, &fbi->var); - fbi->flags &= ~FBINFO_MISC_USEREVENT; - console_unlock(); - - if (vout->display_ch == MEM_FG_SYNC) { - console_lock(); - fb_blank(fbi, FB_BLANK_POWERDOWN); - console_unlock(); - } - - vout->display_bufs[0] = 0; - vout->display_bufs[1] = 0; - - ipu_uninit_channel(vout->ipu, MEM_VDI_PRP_VF_MEM); - if (LOAD_3FIELDS(vout)) { - ipu_uninit_channel(vout->ipu, MEM_VDI_PRP_VF_MEM_P); - ipu_uninit_channel(vout->ipu, MEM_VDI_PRP_VF_MEM_N); - } - if (!ipu_can_rotate_in_place(vout->rotate)) - ipu_uninit_channel(vout->ipu, MEM_ROT_VF_MEM); - } - - vout->ready_q.head = vout->ready_q.tail = 0; - vout->done_q.head = vout->done_q.tail = 0; - for (i = 0; i < vout->buffer_cnt; i++) { - vout->v4l2_bufs[i].flags = 0; - vout->v4l2_bufs[i].timestamp.tv_sec = 0; - vout->v4l2_bufs[i].timestamp.tv_usec = 0; - } - - vout->post_proc_ch = CHAN_NONE; - vout->state = STATE_STREAM_OFF; - - return retval; -} - -/* - * Valid whether the palette is supported - * - * @param palette V4L2_PIX_FMT_RGB565, V4L2_PIX_FMT_BGR24 or V4L2_PIX_FMT_BGR32 - * - * @return 1 if supported, 0 if failed - */ -static inline int valid_mode(u32 palette) -{ - return ((palette == V4L2_PIX_FMT_RGB565) || - (palette == V4L2_PIX_FMT_BGR24) || - (palette == V4L2_PIX_FMT_RGB24) || - (palette == V4L2_PIX_FMT_BGR32) || - (palette == V4L2_PIX_FMT_RGB32) || - (palette == V4L2_PIX_FMT_NV12) || - (palette == V4L2_PIX_FMT_UYVY) || - (palette == V4L2_PIX_FMT_YUYV) || - (palette == V4L2_PIX_FMT_YUV422P) || - (palette == V4L2_PIX_FMT_YUV444) || - (palette == V4L2_PIX_FMT_YUV420)); -} - -/* - * V4L2 - Handles VIDIOC_G_FMT Ioctl - * - * @param vout structure vout_data * - * - * @param v4l2_format structure v4l2_format * - * - * @return status 0 success, EINVAL failed - */ -static int mxc_v4l2out_g_fmt(vout_data *vout, struct v4l2_format *f) -{ - if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { - return -EINVAL; - } - *f = vout->v2f; - return 0; -} - -/* - * V4L2 - Handles VIDIOC_S_FMT Ioctl - * - * @param vout structure vout_data * - * - * @param v4l2_format structure v4l2_format * - * - * @return status 0 success, EINVAL failed - */ -static int mxc_v4l2out_s_fmt(vout_data *vout, struct v4l2_format *f) -{ - int retval = 0; - u32 size = 0; - u32 bytesperline; - - if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { - retval = -EINVAL; - goto err0; - } - if (!valid_mode(f->fmt.pix.pixelformat)) { - dev_err(&vout->video_dev->dev, "pixel format not supported\n"); - retval = -EINVAL; - goto err0; - } - - bytesperline = (f->fmt.pix.width * fmt_to_bpp(f->fmt.pix.pixelformat)) / - 8; - if (f->fmt.pix.bytesperline < bytesperline) { - f->fmt.pix.bytesperline = bytesperline; - } else { - bytesperline = f->fmt.pix.bytesperline; - } - vout->bytesperline = bytesperline; - - /* Based on http://v4l2spec.bytesex.org/spec/x6386.htm#V4L2-FIELD */ - vout->field_fmt = f->fmt.pix.field; - switch (vout->field_fmt) { - /* Images are in progressive format, not interlaced */ - case V4L2_FIELD_NONE: - break; - /* The two fields of a frame are passed in separate buffers, - in temporal order, i. e. the older one first. */ - case V4L2_FIELD_ALTERNATE: - dev_err(&vout->video_dev->dev, - "V4L2_FIELD_ALTERNATE field format not supported yet!\n"); - break; - case V4L2_FIELD_INTERLACED_TB: - case V4L2_FIELD_INTERLACED_BT: - if (cpu_is_mx51() || cpu_is_mx53()) - break; - dev_err(&vout->video_dev->dev, - "De-interlacing not supported in this device!\n"); - vout->field_fmt = V4L2_FIELD_NONE; - break; - default: - vout->field_fmt = V4L2_FIELD_NONE; - break; - } - - switch (f->fmt.pix.pixelformat) { - case V4L2_PIX_FMT_YUV422P: - /* byteperline for YUV planar formats is for - Y plane only */ - size = bytesperline * f->fmt.pix.height * 2; - break; - case V4L2_PIX_FMT_YUV420: - case V4L2_PIX_FMT_NV12: - size = (bytesperline * f->fmt.pix.height * 3) / 2; - break; - default: - size = bytesperline * f->fmt.pix.height; - break; - } - - /* Return the actual size of the image to the app */ - if (f->fmt.pix.sizeimage < size) { - f->fmt.pix.sizeimage = size; - } else { - size = f->fmt.pix.sizeimage; - } - - vout->v2f.fmt.pix = f->fmt.pix; - if (vout->v2f.fmt.pix.priv != 0) { - if (copy_from_user(&vout->offset, - (void *)vout->v2f.fmt.pix.priv, - sizeof(vout->offset))) { - retval = -EFAULT; - goto err0; - } - } else { - vout->offset.u_offset = 0; - vout->offset.v_offset = 0; - } - - retval = 0; - err0: - return retval; -} - -/* - * V4L2 - Handles VIDIOC_G_CTRL Ioctl - * - * @param vout structure vout_data * - * - * @param c structure v4l2_control * - * - * @return status 0 success, EINVAL failed - */ -static int mxc_get_v42lout_control(vout_data *vout, struct v4l2_control *c) -{ - switch (c->id) { - case V4L2_CID_HFLIP: - return (vout->rotate & IPU_ROTATE_HORIZ_FLIP) ? 1 : 0; - case V4L2_CID_VFLIP: - return (vout->rotate & IPU_ROTATE_VERT_FLIP) ? 1 : 0; - case (V4L2_CID_PRIVATE_BASE + 1): - return vout->rotate; - default: - return -EINVAL; - } -} - -/* - * V4L2 - Handles VIDIOC_S_CTRL Ioctl - * - * @param vout structure vout_data * - * - * @param c structure v4l2_control * - * - * @return status 0 success, EINVAL failed - */ -static int mxc_set_v42lout_control(vout_data *vout, struct v4l2_control *c) -{ - switch (c->id) { - case V4L2_CID_HFLIP: - vout->rotate |= c->value ? IPU_ROTATE_HORIZ_FLIP : - IPU_ROTATE_NONE; - break; - case V4L2_CID_VFLIP: - vout->rotate |= c->value ? IPU_ROTATE_VERT_FLIP : - IPU_ROTATE_NONE; - break; - case V4L2_CID_MXC_ROT: - vout->rotate = c->value; - break; - case V4L2_CID_MXC_MOTION: - vout->motion_sel = c->value; - break; - default: - return -EINVAL; - } - return 0; -} - -/*! - * V4L2 interface - open function - * - * @param file structure file * - * - * @return status 0 success, ENODEV invalid device instance, - * ENODEV timeout, ERESTARTSYS interrupted by user - */ -static int mxc_v4l2out_open(struct file *file) -{ - struct video_device *dev = video_devdata(file); - vout_data *vout = video_get_drvdata(dev); - int err; - - if (!vout) { - return -ENODEV; - } - - down(&vout->busy_lock); - - err = -EINTR; - if (signal_pending(current)) - goto oops; - - - if (vout->open_count++ == 0) { - init_waitqueue_head(&vout->v4l_bufq); - - init_timer(&vout->output_timer); - vout->output_timer.function = mxc_v4l2out_timer_handler; - vout->output_timer.data = (unsigned long)vout; - - vout->state = STATE_STREAM_OFF; - vout->rotate = IPU_ROTATE_NONE; - - vout->v4l_wq = create_singlethread_workqueue("v4l2q"); - if (!vout->v4l_wq) { - dev_dbg(&dev->dev, - "Could not create work queue\n"); - err = -ENOMEM; - goto oops; - } - - INIT_WORK(&vout->icbypass_work, icbypass_work_func); - } - - file->private_data = dev; - - up(&vout->busy_lock); - - return 0; - - oops: - up(&vout->busy_lock); - return err; -} - -/*! - * V4L2 interface - close function - * - * @param file struct file * - * - * @return 0 success - */ -static int mxc_v4l2out_close(struct file *file) -{ - struct video_device *dev = video_devdata(file); - vout_data *vout = video_get_drvdata(dev); - - if (--vout->open_count == 0) { - if (vout->state != STATE_STREAM_OFF) - mxc_v4l2out_streamoff(vout); - - file->private_data = NULL; - - mxc_free_buffers(vout->queue_buf_paddr, vout->queue_buf_vaddr, - vout->buffer_cnt, vout->queue_buf_size); - vout->buffer_cnt = 0; - mxc_free_buffers(vout->rot_pp_bufs, vout->rot_pp_bufs_vaddr, 2, - vout->display_buf_size); - - /* capture off */ - wake_up_interruptible(&vout->v4l_bufq); - - flush_workqueue(vout->v4l_wq); - destroy_workqueue(vout->v4l_wq); - } - - return 0; -} - -/*! - * V4L2 interface - ioctl function - * - * @param file struct file * - * - * @param ioctlnr unsigned int - * - * @param arg void * - * - * @return 0 success, ENODEV for invalid device instance, - * -1 for other errors. - */ -static long -mxc_v4l2out_do_ioctl(struct file *file, - unsigned int ioctlnr, void *arg) -{ - struct video_device *vdev = file->private_data; - vout_data *vout = video_get_drvdata(vdev); - int retval = 0; - int i = 0; - - if (!vout) - return -EBADF; - - /* make this _really_ smp-safe */ - if (down_interruptible(&vout->busy_lock)) - return -EBUSY; - - switch (ioctlnr) { - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *cap = arg; - strcpy(cap->driver, "mxc_v4l2_output"); - cap->version = 0; - cap->capabilities = - V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; - cap->card[0] = '\0'; - cap->bus_info[0] = '\0'; - retval = 0; - break; - } - case VIDIOC_G_FMT: - { - struct v4l2_format *gf = arg; - retval = mxc_v4l2out_g_fmt(vout, gf); - break; - } - case VIDIOC_S_FMT: - { - struct v4l2_format *sf = arg; - if (vout->state != STATE_STREAM_OFF) { - retval = -EBUSY; - break; - } - retval = mxc_v4l2out_s_fmt(vout, sf); - break; - } - case VIDIOC_REQBUFS: - { - struct v4l2_requestbuffers *req = arg; - if ((req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) || - (req->memory != V4L2_MEMORY_MMAP)) { - dev_dbg(&vdev->dev, - "VIDIOC_REQBUFS: incorrect buffer type\n"); - retval = -EINVAL; - break; - } - - if (req->count == 0) - mxc_v4l2out_streamoff(vout); - - if (vout->state == STATE_STREAM_OFF) { - if (vout->queue_buf_paddr[0] != 0) { - mxc_free_buffers(vout->queue_buf_paddr, - vout->queue_buf_vaddr, - vout->buffer_cnt, - vout->queue_buf_size); - dev_dbg(&vdev->dev, - "VIDIOC_REQBUFS: freed buffers\n"); - } - vout->buffer_cnt = 0; - } else { - dev_dbg(&vdev->dev, - "VIDIOC_REQBUFS: Buffer is in use\n"); - retval = -EBUSY; - break; - } - - if (req->count == 0) - break; - - if (req->count < MIN_FRAME_NUM) { - req->count = MIN_FRAME_NUM; - } else if (req->count > MAX_FRAME_NUM) { - req->count = MAX_FRAME_NUM; - } - vout->buffer_cnt = req->count; - vout->queue_buf_size = - PAGE_ALIGN(vout->v2f.fmt.pix.sizeimage); - - retval = mxc_allocate_buffers(vout->queue_buf_paddr, - vout->queue_buf_vaddr, - vout->buffer_cnt, - vout->queue_buf_size); - if (retval < 0) - break; - - /* Init buffer queues */ - vout->done_q.head = 0; - vout->done_q.tail = 0; - vout->ready_q.head = 0; - vout->ready_q.tail = 0; - - for (i = 0; i < vout->buffer_cnt; i++) { - memset(&(vout->v4l2_bufs[i]), 0, - sizeof(vout->v4l2_bufs[i])); - vout->v4l2_bufs[i].flags = 0; - vout->v4l2_bufs[i].memory = V4L2_MEMORY_MMAP; - vout->v4l2_bufs[i].index = i; - vout->v4l2_bufs[i].type = - V4L2_BUF_TYPE_VIDEO_OUTPUT; - vout->v4l2_bufs[i].length = - PAGE_ALIGN(vout->v2f.fmt.pix.sizeimage); - vout->v4l2_bufs[i].m.offset = - (unsigned long)vout->queue_buf_paddr[i]; - vout->v4l2_bufs[i].timestamp.tv_sec = 0; - vout->v4l2_bufs[i].timestamp.tv_usec = 0; - } - break; - } - case VIDIOC_QUERYBUF: - { - struct v4l2_buffer *buf = arg; - u32 type = buf->type; - int index = buf->index; - - if ((type != V4L2_BUF_TYPE_VIDEO_OUTPUT) || - (index >= vout->buffer_cnt)) { - dev_dbg(&vdev->dev, - "VIDIOC_QUERYBUFS: incorrect buffer type\n"); - retval = -EINVAL; - break; - } - down(&vout->param_lock); - memcpy(buf, &(vout->v4l2_bufs[index]), sizeof(*buf)); - up(&vout->param_lock); - break; - } - case VIDIOC_QBUF: - { - struct v4l2_buffer *buf = arg; - int index = buf->index; - unsigned long lock_flags; - int param[5][3]; - - if ((buf->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) || - (index >= vout->buffer_cnt)) { - retval = -EINVAL; - break; - } - - dev_dbg(&vdev->dev, "VIDIOC_QBUF: %d field = %d\n", buf->index, buf->field); - - /* mmapped buffers are L1 WB cached, - * so we need to clean them */ - if (buf->memory & V4L2_MEMORY_MMAP) { - flush_cache_all(); - } - - spin_lock_irqsave(&g_lock, lock_flags); - - memcpy(&(vout->v4l2_bufs[index]), buf, sizeof(*buf)); - vout->v4l2_bufs[index].flags |= V4L2_BUF_FLAG_QUEUED; - - g_buf_q_cnt++; - if (vout->v4l2_bufs[index].reserved) - if (!copy_from_user(¶m[0][0], - (void *)vout-> - v4l2_bufs[index] - .reserved, sizeof(param))) - ipu_set_csc_coefficients(vout->ipu, vout-> - display_ch, - param); - queue_buf(&vout->ready_q, index); - if (vout->state == STATE_STREAM_PAUSED) { - index = peek_next_buf(&vout->ready_q); - setup_next_buf_timer(vout, index); - vout->state = STATE_STREAM_ON; - } - - spin_unlock_irqrestore(&g_lock, lock_flags); - break; - } - case VIDIOC_DQBUF: - { - struct v4l2_buffer *buf = arg; - int idx; - - if ((queue_size(&vout->done_q) == 0) && - (file->f_flags & O_NONBLOCK)) { - retval = -EAGAIN; - break; - } - - if (!wait_event_interruptible_timeout(vout->v4l_bufq, - queue_size(&vout-> - done_q) - != 0, 10 * HZ)) { - dev_dbg(&vdev->dev, "VIDIOC_DQBUF: timeout\n"); - retval = -ETIME; - break; - } else if (signal_pending(current)) { - dev_dbg(&vdev->dev, - "VIDIOC_DQBUF: interrupt received\n"); - retval = -ERESTARTSYS; - break; - } - idx = dequeue_buf(&vout->done_q); - if (idx == -1) { /* No frame free */ - dev_dbg(&vdev->dev, - "VIDIOC_DQBUF: no free buffers, returning\n"); - retval = -EAGAIN; - break; - } - if ((vout->v4l2_bufs[idx].flags & V4L2_BUF_FLAG_DONE) == - 0) - dev_dbg(&vdev->dev, - "VIDIOC_DQBUF: buffer in done q, but not " - "flagged as done\n"); - - vout->v4l2_bufs[idx].flags = 0; - memcpy(buf, &(vout->v4l2_bufs[idx]), sizeof(*buf)); - dev_dbg(&vdev->dev, "VIDIOC_DQBUF: %d\n", buf->index); - break; - } - case VIDIOC_STREAMON: - { - retval = mxc_v4l2out_streamon(vout); - break; - } - case VIDIOC_STREAMOFF: - { - retval = mxc_v4l2out_streamoff(vout); - break; - } - case VIDIOC_G_CTRL: - { - retval = mxc_get_v42lout_control(vout, arg); - break; - } - case VIDIOC_S_CTRL: - { - retval = mxc_set_v42lout_control(vout, arg); - break; - } - case VIDIOC_CROPCAP: - { - struct v4l2_cropcap *cap = arg; - - if (cap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { - retval = -EINVAL; - break; - } - - cap->bounds = vout->crop_bounds[vout->cur_disp_output]; - cap->defrect = vout->crop_bounds[vout->cur_disp_output]; - retval = 0; - break; - } - case VIDIOC_G_CROP: - { - struct v4l2_crop *crop = arg; - - if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { - retval = -EINVAL; - break; - } - crop->c = vout->crop_current; - break; - } - case VIDIOC_S_CROP: - { - struct v4l2_crop *crop = arg; - struct v4l2_rect *b = - &(vout->crop_bounds[vout->cur_disp_output]); - - if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { - retval = -EINVAL; - break; - } - if (crop->c.height < 0) { - retval = -EINVAL; - break; - } - if (crop->c.width < 0) { - retval = -EINVAL; - break; - } - - /* only full screen supported for SDC BG and SDC DC */ - if (vout->cur_disp_output == 4) { - crop->c = vout->crop_current; - break; - } - - if (crop->c.top < b->top) - crop->c.top = b->top; - if (crop->c.top >= b->top + b->height) - crop->c.top = b->top + b->height - 1; - if (crop->c.height > b->top - crop->c.top + b->height) - crop->c.height = - b->top - crop->c.top + b->height; - - if (crop->c.left < b->left) - crop->c.left = b->left; - if (crop->c.left >= b->left + b->width) - crop->c.left = b->left + b->width - 1; - if (crop->c.width > b->left - crop->c.left + b->width) - crop->c.width = - b->left - crop->c.left + b->width; - - /* stride line limitation */ - crop->c.height -= crop->c.height % 8; - crop->c.width -= crop->c.width % 8; - - vout->crop_current = crop->c; - break; - } - case VIDIOC_ENUMOUTPUT: - { - struct v4l2_output *output = arg; - - if ((output->index >= 5) || - (vout->output_enabled[output->index] == false)) { - retval = -EINVAL; - break; - } - - if (output->index >= 3) - *output = mxc_outputs[MXC_V4L2_OUT_2_SDC]; - break; - } - case VIDIOC_G_OUTPUT: - { - int *p_output_num = arg; - - *p_output_num = vout->cur_disp_output; - break; - } - case VIDIOC_S_OUTPUT: - { - int *p_output_num = arg; - int fbnum; - struct v4l2_rect *b; - - if ((*p_output_num >= MXC_V4L2_OUT_NUM_OUTPUTS) || - (vout->output_enabled[*p_output_num] == false)) { - retval = -EINVAL; - break; - } - - if (vout->state != STATE_STREAM_OFF) { - retval = -EBUSY; - break; - } - - vout->cur_disp_output = *p_output_num; - - /* Update bounds in case they have changed */ - b = &vout->crop_bounds[vout->cur_disp_output]; - - fbnum = vout->output_fb_num[vout->cur_disp_output]; - - /* - * For FG overlay, it uses BG window parameter as - * limitation reference; and BG must be enabled to - * support FG. - */ - if (vout->cur_disp_output == 3) { - unsigned int i, ipu_ch = CHAN_NONE; - struct fb_info *fbi; - mm_segment_t old_fs; - - for (i = 0; i < num_registered_fb; i++) { - fbi = registered_fb[i]; - if (fbi->fbops->fb_ioctl) { - old_fs = get_fs(); - set_fs(KERNEL_DS); - fbi->fbops->fb_ioctl(fbi, - MXCFB_GET_FB_IPU_CHAN, - (unsigned long)&ipu_ch); - set_fs(old_fs); - } - if (ipu_ch == CHAN_NONE) { - dev_err(&vdev->dev, - "Can't get disp ipu channel\n"); - retval = -EINVAL; - break; - } - - if ((ipu_ch == MEM_BG_SYNC) && - (!strncmp(fbi->fix.id, "DISP3", 5))) { - fbnum = i; - break; - } - } - } - - b->width = registered_fb[fbnum]->var.xres; - b->height = registered_fb[fbnum]->var.yres; - - vout->crop_current = *b; - break; - } - case VIDIOC_ENUM_FMT: - case VIDIOC_TRY_FMT: - case VIDIOC_QUERYCTRL: - case VIDIOC_G_PARM: - case VIDIOC_ENUMSTD: - case VIDIOC_G_STD: - case VIDIOC_S_STD: - case VIDIOC_G_TUNER: - case VIDIOC_S_TUNER: - case VIDIOC_G_FREQUENCY: - case VIDIOC_S_FREQUENCY: - default: - retval = -EINVAL; - break; - } - - up(&vout->busy_lock); - return retval; -} - -/* - * V4L2 interface - ioctl function - * - * @return None - */ -static long -mxc_v4l2out_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - return video_usercopy(file, cmd, arg, mxc_v4l2out_do_ioctl); -} - -/*! - * V4L2 interface - mmap function - * - * @param file structure file * - * - * @param vma structure vm_area_struct * - * - * @return status 0 Success, EINTR busy lock error, - * ENOBUFS remap_page error - */ -static int mxc_v4l2out_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct video_device *vdev = video_devdata(file); - unsigned long size = vma->vm_end - vma->vm_start; - int res = 0; - int i; - vout_data *vout = video_get_drvdata(vdev); - - dev_dbg(&vdev->dev, "pgoff=0x%lx, start=0x%lx, end=0x%lx\n", - vma->vm_pgoff, vma->vm_start, vma->vm_end); - - /* make this _really_ smp-safe */ - if (down_interruptible(&vout->busy_lock)) - return -EINTR; - - for (i = 0; i < vout->buffer_cnt; i++) { - if ((vout->v4l2_bufs[i].m.offset == - (vma->vm_pgoff << PAGE_SHIFT)) && - (vout->v4l2_bufs[i].length >= size)) { - vout->v4l2_bufs[i].flags |= V4L2_BUF_FLAG_MAPPED; - break; - } - } - if (i == vout->buffer_cnt) { - res = -ENOBUFS; - goto mxc_mmap_exit; - } - - /* make buffers inner write-back, outer write-thru cacheable */ - /* vma->vm_page_prot = pgprot_outer_wrthru(vma->vm_page_prot);*/ - - if (remap_pfn_range(vma, vma->vm_start, - vma->vm_pgoff, size, vma->vm_page_prot)) { - dev_dbg(&vdev->dev, "mmap remap_pfn_range failed\n"); - res = -ENOBUFS; - goto mxc_mmap_exit; - } - - vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */ - - mxc_mmap_exit: - up(&vout->busy_lock); - return res; -} - -/*! - * V4L2 interface - poll function - * - * @param file structure file * - * - * @param wait structure poll_table_struct * - * - * @return status POLLIN | POLLRDNORM - */ -static unsigned int mxc_v4l2out_poll(struct file *file, struct poll_table_struct * wait) -{ - struct video_device *dev = video_devdata(file); - vout_data *vout = video_get_drvdata(dev); - - wait_queue_head_t *queue = NULL; - int res = POLLIN | POLLRDNORM; - - if (down_interruptible(&vout->busy_lock)) - return -EINTR; - - queue = &vout->v4l_bufq; - poll_wait(file, queue, wait); - - up(&vout->busy_lock); - return res; -} - -static struct -v4l2_file_operations mxc_v4l2out_fops = { - .owner = THIS_MODULE, - .open = mxc_v4l2out_open, - .release = mxc_v4l2out_close, - .ioctl = mxc_v4l2out_ioctl, - .mmap = mxc_v4l2out_mmap, - .poll = mxc_v4l2out_poll, -}; - -static struct video_device mxc_v4l2out_template = { - .name = "MXC Video Output", - .vfl_type = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING, - .fops = &mxc_v4l2out_fops, - .release = video_device_release, -}; - -static ssize_t show_streaming(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct video_device *video_dev = container_of(dev, - struct video_device, dev); - vout_data *vout = video_get_drvdata(video_dev); - - if (vout->state == STATE_STREAM_OFF) - return sprintf(buf, "stream off\n"); - else if (vout->state == STATE_STREAM_PAUSED) - return sprintf(buf, "stream paused\n"); - else - return sprintf(buf, "stream on\n"); -} -static DEVICE_ATTR(fsl_v4l2_output_property, S_IRUGO, show_streaming, NULL); - -/*! - * Probe routine for the framebuffer driver. It is called during the - * driver binding process. The following functions are performed in - * this routine: Framebuffer initialization, Memory allocation and - * mapping, Framebuffer registration, IPU initialization. - * - * @return Appropriate error code to the kernel common code - */ -static int mxc_v4l2out_probe(struct platform_device *pdev) -{ - int i; - vout_data *vout; - - /* - * Allocate sufficient memory for the fb structure - */ - vout = kmalloc(sizeof(vout_data), GFP_KERNEL); - - if (!vout) - return 0; - - memset(vout, 0, sizeof(vout_data)); - - vout->video_dev = video_device_alloc(); - if (vout->video_dev == NULL) - return -1; - vout->video_dev->minor = -1; - - *(vout->video_dev) = mxc_v4l2out_template; - - /* register v4l device */ - if (video_register_device(vout->video_dev, - VFL_TYPE_GRABBER, video_nr) == -1) { - dev_dbg(&pdev->dev, "video_register_device failed\n"); - return 0; - } - dev_info(&pdev->dev, "Registered device video%d\n", - vout->video_dev->minor & 0x1f); - /*vout->video_dev->dev = &pdev->dev;*/ - - video_set_drvdata(vout->video_dev, vout); - - init_MUTEX(&vout->param_lock); - init_MUTEX(&vout->busy_lock); - - /* setup outputs and cropping */ - vout->cur_disp_output = -1; - for (i = 0; i < num_registered_fb; i++) { - char *idstr = registered_fb[i]->fix.id; - if (strncmp(idstr, "DISP3", 5) == 0) { - int disp_num = idstr[4] - '0'; - if (disp_num == 3) { - if (strcmp(idstr, "DISP3 BG - DI1") == 0) - disp_num = 5; - else if (strncmp(idstr, "DISP3 BG", 8) == 0) - disp_num = 4; - } - vout->crop_bounds[disp_num].left = 0; - vout->crop_bounds[disp_num].top = 0; - vout->crop_bounds[disp_num].width = - registered_fb[i]->var.xres; - vout->crop_bounds[disp_num].height = - registered_fb[i]->var.yres; - vout->output_enabled[disp_num] = true; - vout->output_fb_num[disp_num] = i; - if (vout->cur_disp_output == -1) { - vout->cur_disp_output = disp_num; - } - } - - } - vout->crop_current = vout->crop_bounds[vout->cur_disp_output]; - - vout->ipu = ipu_get_soc(0); - - platform_set_drvdata(pdev, vout); - - if (device_create_file(&vout->video_dev->dev, - &dev_attr_fsl_v4l2_output_property)) - dev_err(&pdev->dev, "Error on creating file\n"); - - return 0; -} - -static int mxc_v4l2out_remove(struct platform_device *pdev) -{ - vout_data *vout = platform_get_drvdata(pdev); - - if (vout->video_dev) { - device_remove_file(&vout->video_dev->dev, - &dev_attr_fsl_v4l2_output_property); - video_unregister_device(vout->video_dev); - vout->video_dev = NULL; - } - - platform_set_drvdata(pdev, NULL); - - kfree(vout); - - return 0; -} - -/*! - * This structure contains pointers to the power management callback functions. - */ -static struct platform_driver mxc_v4l2out_driver = { - .driver = { - .name = "mxc_v4l2_output", - }, - .probe = mxc_v4l2out_probe, - .remove = mxc_v4l2out_remove, -}; - -/*! - * mxc v4l2 init function - * - */ -static int mxc_v4l2out_init(void) -{ - return platform_driver_register(&mxc_v4l2out_driver); -} - -/*! - * mxc v4l2 cleanup function - * - */ -static void mxc_v4l2out_clean(void) -{ - platform_driver_unregister(&mxc_v4l2out_driver); -} - -module_init(mxc_v4l2out_init); -module_exit(mxc_v4l2out_clean); - -module_param(video_nr, int, 0444); -MODULE_AUTHOR("Freescale Semiconductor, Inc."); -MODULE_DESCRIPTION("V4L2-driver for MXC video output"); -MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE("video"); diff --git a/drivers/media/video/mxc/output/mxc_v4l2_output.h b/drivers/media/video/mxc/output/mxc_v4l2_output.h deleted file mode 100644 index 6ab0a4a990b0..000000000000 --- a/drivers/media/video/mxc/output/mxc_v4l2_output.h +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 2005-2011 Freescale Semiconductor, Inc. All Rights Reserved. - */ - -/* - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html - */ - -/*! - * @defgroup MXC_V4L2_OUTPUT MXC V4L2 Video Output Driver - */ -/*! - * @file mxc_v4l2_output.h - * - * @brief MXC V4L2 Video Output Driver Header file - * - * Video4Linux2 Output Device using MXC IPU Post-processing functionality. - * - * @ingroup MXC_V4L2_OUTPUT - */ -#ifndef __MXC_V4L2_OUTPUT_H__ -#define __MXC_V4L2_OUTPUT_H__ - -#include <media/v4l2-dev.h> - -#ifdef __KERNEL__ - -#include <linux/mxc_v4l2.h> -#include <linux/videodev2.h> -#include <mach/ipu-v3.h> - -#define MIN_FRAME_NUM 2 -#define MAX_FRAME_NUM 30 - -#define MXC_V4L2_OUT_NUM_OUTPUTS 6 -#define MXC_V4L2_OUT_2_SDC 0 - - -typedef struct { - int list[MAX_FRAME_NUM + 1]; - int head; - int tail; -} v4l_queue; - -/*! - * States for the video stream - */ -typedef enum { - STATE_STREAM_OFF, - STATE_STREAM_ON, - STATE_STREAM_PAUSED, - STATE_STREAM_STOPPING, -} v4lout_state; - -/*! - * common v4l2 driver structure. - */ -typedef struct _vout_data { - struct video_device *video_dev; - /*! - * semaphore guard against SMP multithreading - */ - struct semaphore busy_lock; - - /*! - * number of process that have device open - */ - int open_count; - - /*! - * params lock for this camera - */ - struct semaphore param_lock; - - struct timer_list output_timer; - struct workqueue_struct *v4l_wq; - struct work_struct icbypass_work; - int disp_buf_num; - int fb_blank; - unsigned long start_jiffies; - u32 frame_count; - - v4l_queue ready_q; - v4l_queue done_q; - - s8 next_rdy_ipu_buf; - s8 next_done_ipu_buf; - s8 next_disp_ipu_buf; - s8 ipu_buf[2]; - s8 ipu_buf_p[2]; - s8 ipu_buf_n[2]; - volatile v4lout_state state; - - int cur_disp_output; - int output_fb_num[MXC_V4L2_OUT_NUM_OUTPUTS]; - int output_enabled[MXC_V4L2_OUT_NUM_OUTPUTS]; - struct v4l2_framebuffer v4l2_fb; - int ic_bypass; - u32 work_irq; - ipu_channel_t display_ch; - ipu_channel_t post_proc_ch; - ipu_channel_t display_input_ch; - - /*! - * FRAME_NUM-buffering, so we need a array - */ - int buffer_cnt; - dma_addr_t queue_buf_paddr[MAX_FRAME_NUM]; - void *queue_buf_vaddr[MAX_FRAME_NUM]; - u32 queue_buf_size; - struct v4l2_buffer v4l2_bufs[MAX_FRAME_NUM]; - u32 display_buf_size; - dma_addr_t display_bufs[2]; - void *display_bufs_vaddr[2]; - dma_addr_t rot_pp_bufs[2]; - void *rot_pp_bufs_vaddr[2]; - - /*! - * Poll wait queue - */ - wait_queue_head_t v4l_bufq; - - /*! - * v4l2 format - */ - struct v4l2_format v2f; - struct v4l2_mxc_offset offset; - ipu_rotate_mode_t rotate; - - /* crop */ - struct v4l2_rect crop_bounds[MXC_V4L2_OUT_NUM_OUTPUTS]; - struct v4l2_rect crop_current; - u32 bytesperline; - enum v4l2_field field_fmt; - ipu_motion_sel motion_sel; - - /* PP split fot two stripes*/ - int pp_split; /* 0,1 */ - struct stripe_param pp_left_stripe; - struct stripe_param pp_right_stripe; /* struct for split parameters */ - struct stripe_param pp_up_stripe; - struct stripe_param pp_down_stripe; - /* IC ouput buffer number. Counting from 0 to 7 */ - int pp_split_buf_num; /* 0..7 */ - u16 bpp ; /* bit per pixel */ - u16 xres; /* width of physical frame (BGs) */ - u16 yres; /* heigth of physical frame (BGs)*/ - - void *ipu; -} vout_data; - -#endif -#endif /* __MXC_V4L2_OUTPUT_H__ */ diff --git a/drivers/media/video/mxc/output/mxc_vout.c b/drivers/media/video/mxc/output/mxc_vout.c new file mode 100644 index 000000000000..26ab55af4cbd --- /dev/null +++ b/drivers/media/video/mxc/output/mxc_vout.c @@ -0,0 +1,1488 @@ +/* + * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/vmalloc.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/videodev2.h> +#include <linux/mxcfb.h> +#include <linux/console.h> +#include <linux/mxc_v4l2.h> +#include <mach/ipu-v3.h> + +#include <media/videobuf-dma-contig.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> + +#define MAX_FB_NUM 6 +#define FB_BUFS 3 + +struct mxc_vout_fb { + char *name; + int ipu_id; + struct v4l2_rect crop_bounds; + unsigned int disp_fmt; + bool disp_support_csc; + bool disp_support_windows; +}; + +struct mxc_vout_output { + int open_cnt; + struct fb_info *fbi; + struct video_device *vfd; + struct mutex mutex; + struct mutex task_lock; + enum v4l2_buf_type type; + + struct videobuf_queue vbq; + spinlock_t vbq_lock; + + struct list_head queue_list; + struct list_head active_list; + + struct v4l2_rect crop_bounds; + unsigned int disp_fmt; + struct mxcfb_pos win_pos; + bool disp_support_windows; + bool disp_support_csc; + + struct ipu_task task; + + bool timer_stop; + struct timer_list timer; + struct workqueue_struct *v4l_wq; + struct work_struct disp_work; + unsigned long frame_count; + unsigned long start_jiffies; + + int ctrl_rotate; + int ctrl_vflip; + int ctrl_hflip; + + dma_addr_t disp_bufs[FB_BUFS]; +}; + +struct mxc_vout_dev { + struct device *dev; + struct v4l2_device v4l2_dev; + struct mxc_vout_output *out[MAX_FB_NUM]; + int out_num; +}; + +/* Driver Configuration macros */ +#define VOUT_NAME "mxc_vout" + +/* Variables configurable through module params*/ +static int debug; +static int video_nr = 16; + +/* Module parameters */ +module_param(video_nr, int, S_IRUGO); +MODULE_PARM_DESC(video_nr, "video device numbers"); +module_param(debug, bool, S_IRUGO); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +const static struct v4l2_fmtdesc mxc_formats[] = { + { + .description = "RGB565", + .pixelformat = V4L2_PIX_FMT_RGB565, + }, + { + .description = "BGR24", + .pixelformat = V4L2_PIX_FMT_BGR24, + }, + { + .description = "RGB24", + .pixelformat = V4L2_PIX_FMT_RGB24, + }, + { + .description = "RGB32", + .pixelformat = V4L2_PIX_FMT_RGB32, + }, + { + .description = "BGR32", + .pixelformat = V4L2_PIX_FMT_BGR32, + }, + { + .description = "NV12", + .pixelformat = V4L2_PIX_FMT_NV12, + }, + { + .description = "UYVY", + .pixelformat = V4L2_PIX_FMT_UYVY, + }, + { + .description = "YUYV", + .pixelformat = V4L2_PIX_FMT_YUYV, + }, + { + .description = "YUV422 planar", + .pixelformat = V4L2_PIX_FMT_YUV422P, + }, + { + .description = "YUV444", + .pixelformat = V4L2_PIX_FMT_YUV444, + }, + { + .description = "YUV420", + .pixelformat = V4L2_PIX_FMT_YUV420, + }, +}; + +#define NUM_MXC_VOUT_FORMATS (ARRAY_SIZE(mxc_formats)) + +#define DEF_INPUT_WIDTH 320 +#define DEF_INPUT_HEIGHT 240 + +static int mxc_vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i); + +static struct mxc_vout_fb g_fb_setting[MAX_FB_NUM]; + +static ipu_channel_t get_ipu_channel(struct fb_info *fbi) +{ + ipu_channel_t ipu_ch = CHAN_NONE; + mm_segment_t old_fs; + + if (fbi->fbops->fb_ioctl) { + old_fs = get_fs(); + set_fs(KERNEL_DS); + fbi->fbops->fb_ioctl(fbi, MXCFB_GET_FB_IPU_CHAN, + (unsigned long)&ipu_ch); + set_fs(old_fs); + } + + return ipu_ch; +} + +static unsigned int get_ipu_fmt(struct fb_info *fbi) +{ + mm_segment_t old_fs; + unsigned int fb_fmt; + + if (fbi->fbops->fb_ioctl) { + old_fs = get_fs(); + set_fs(KERNEL_DS); + fbi->fbops->fb_ioctl(fbi, MXCFB_GET_DIFMT, + (unsigned long)&fb_fmt); + set_fs(old_fs); + } + + return fb_fmt; +} + +static void update_display_setting(void) +{ + int i; + struct fb_info *fbi; + struct v4l2_rect bg_crop_bounds[2]; + + for (i = 0; i < num_registered_fb; i++) { + fbi = registered_fb[i]; + + memset(&g_fb_setting[i], 0, sizeof(struct mxc_vout_fb)); + + if (!strncmp(fbi->fix.id, "DISP3", 5)) + g_fb_setting[i].ipu_id = 0; + else + g_fb_setting[i].ipu_id = 1; + + g_fb_setting[i].name = fbi->fix.id; + g_fb_setting[i].crop_bounds.left = 0; + g_fb_setting[i].crop_bounds.top = 0; + g_fb_setting[i].crop_bounds.width = fbi->var.xres; + g_fb_setting[i].crop_bounds.height = fbi->var.yres; + g_fb_setting[i].disp_fmt = get_ipu_fmt(fbi); + + if (get_ipu_channel(fbi) == MEM_BG_SYNC) { + bg_crop_bounds[g_fb_setting[i].ipu_id] = + g_fb_setting[i].crop_bounds; + g_fb_setting[i].disp_support_csc = true; + } else if (get_ipu_channel(fbi) == MEM_FG_SYNC) { + g_fb_setting[i].disp_support_csc = true; + g_fb_setting[i].disp_support_windows = true; + } + } + + for (i = 0; i < num_registered_fb; i++) { + fbi = registered_fb[i]; + + if (get_ipu_channel(fbi) == MEM_FG_SYNC) + g_fb_setting[i].crop_bounds = + bg_crop_bounds[g_fb_setting[i].ipu_id]; + } +} + +/* called after g_fb_setting filled by update_display_setting */ +static int update_setting_from_fbi(struct mxc_vout_output *vout, + struct fb_info *fbi) +{ + int i; + bool found = false; + + for (i = 0; i < MAX_FB_NUM; i++) { + if (g_fb_setting[i].name) { + if (!strcmp(fbi->fix.id, g_fb_setting[i].name)) { + vout->crop_bounds = g_fb_setting[i].crop_bounds; + vout->disp_fmt = g_fb_setting[i].disp_fmt; + vout->disp_support_csc = g_fb_setting[i].disp_support_csc; + vout->disp_support_windows = + g_fb_setting[i].disp_support_windows; + found = true; + break; + } + } + } + + if (!found) { + v4l2_err(vout->vfd->v4l2_dev, "can not find output\n"); + return -EINVAL; + } + strlcpy(vout->vfd->name, fbi->fix.id, sizeof(vout->vfd->name)); + + memset(&vout->task, 0, sizeof(struct ipu_task)); + + vout->task.input.width = DEF_INPUT_WIDTH; + vout->task.input.height = DEF_INPUT_HEIGHT; + vout->task.input.crop.pos.x = 0; + vout->task.input.crop.pos.y = 0; + vout->task.input.crop.w = DEF_INPUT_WIDTH; + vout->task.input.crop.h = DEF_INPUT_HEIGHT; + + vout->task.output.width = vout->crop_bounds.width; + vout->task.output.height = vout->crop_bounds.height; + vout->task.output.crop.pos.x = 0; + vout->task.output.crop.pos.y = 0; + vout->task.output.crop.w = vout->crop_bounds.width; + vout->task.output.crop.h = vout->crop_bounds.height; + if (colorspaceofpixel(vout->disp_fmt) == YUV_CS) + vout->task.output.format = IPU_PIX_FMT_UYVY; + else + vout->task.output.format = IPU_PIX_FMT_RGB565; + + return 0; +} + +static inline unsigned long get_jiffies(struct timeval *t) +{ + struct timeval cur; + + if (t->tv_usec >= 1000000) { + t->tv_sec += t->tv_usec / 1000000; + t->tv_usec = t->tv_usec % 1000000; + } + + do_gettimeofday(&cur); + if ((t->tv_sec < cur.tv_sec) + || ((t->tv_sec == cur.tv_sec) && (t->tv_usec < cur.tv_usec))) + return jiffies; + + if (t->tv_usec < cur.tv_usec) { + cur.tv_sec = t->tv_sec - cur.tv_sec - 1; + cur.tv_usec = t->tv_usec + 1000000 - cur.tv_usec; + } else { + cur.tv_sec = t->tv_sec - cur.tv_sec; + cur.tv_usec = t->tv_usec - cur.tv_usec; + } + + return jiffies + timeval_to_jiffies(&cur); +} + +static bool deinterlace_3_field(struct mxc_vout_output *vout) +{ + return (vout->task.input.deinterlace.enable && + (vout->task.input.deinterlace.motion != HIGH_MOTION)); +} + +static bool is_pp_bypass(struct mxc_vout_output *vout) +{ + if ((vout->task.input.width == vout->task.output.width) && + (vout->task.input.height == vout->task.output.height) && + (vout->task.input.crop.w == vout->task.output.crop.w) && + (vout->task.input.crop.h == vout->task.output.crop.h) && + (vout->task.output.rotate < IPU_ROTATE_90_RIGHT) && + !vout->task.input.deinterlace.enable) { + if (vout->disp_support_csc) + return true; + else if (!need_csc(vout->task.input.format, vout->disp_fmt)) + return true; + /* input crop show to full output which can show based on xres_virtual/yres_virtual */ + } else if ((vout->task.input.crop.w == vout->task.output.crop.w) && + (vout->task.output.crop.w == vout->task.output.width) && + (vout->task.input.crop.h == vout->task.output.crop.h) && + (vout->task.output.crop.h == vout->task.output.height) && + (vout->task.output.rotate < IPU_ROTATE_90_RIGHT) && + !vout->task.input.deinterlace.enable) { + if (vout->disp_support_csc) + return true; + else if (!need_csc(vout->task.input.format, vout->disp_fmt)) + return true; + } + return false; +} + +static void setup_buf_timer(struct mxc_vout_output *vout, + struct videobuf_buffer *vb) +{ + unsigned long timeout; + + /* if timestamp is 0, then default to 30fps */ + if ((vb->ts.tv_sec == 0) + && (vb->ts.tv_usec == 0) + && vout->start_jiffies) + timeout = + vout->start_jiffies + vout->frame_count * HZ / 30; + else + timeout = get_jiffies(&vb->ts); + + if (jiffies >= timeout) { + v4l2_dbg(1, debug, vout->vfd->v4l2_dev, + "warning: timer timeout already expired.\n"); + } + + if (mod_timer(&vout->timer, timeout)) { + v4l2_warn(vout->vfd->v4l2_dev, + "warning: timer was already set\n"); + } + + v4l2_dbg(1, debug, vout->vfd->v4l2_dev, + "timer handler next schedule: %lu\n", timeout); +} + +static int show_buf(struct mxc_vout_output *vout, int idx) +{ + struct fb_info *fbi = vout->fbi; + struct fb_var_screeninfo var; + int ret; + + memcpy(&var, &fbi->var, sizeof(var)); + + if (is_pp_bypass(vout)) { + /* + * crack fb base + * NOTE: should not do other fb operation during v4l2 + */ + console_lock(); + fbi->fix.smem_start = vout->task.output.paddr; + fbi->var.yoffset = vout->task.input.crop.pos.y + 1; + var.xoffset = vout->task.input.crop.pos.x; + var.yoffset = vout->task.input.crop.pos.y; + ret = fb_pan_display(fbi, &var); + console_unlock(); + } else { + var.yoffset = idx * fbi->var.yres; + console_lock(); + ret = fb_pan_display(fbi, &var); + console_unlock(); + } + + return ret; +} + +static void disp_work_func(struct work_struct *work) +{ + struct mxc_vout_output *vout = + container_of(work, struct mxc_vout_output, disp_work); + struct videobuf_queue *q = &vout->vbq; + struct videobuf_buffer *vb, *vb_next = NULL; + unsigned long flags = 0; + int ret; + + spin_lock_irqsave(q->irqlock, flags); + + if (deinterlace_3_field(vout)) { + if (list_is_singular(&vout->active_list)) { + v4l2_warn(vout->vfd->v4l2_dev, + "deinterlacing: no enough entry in active_list\n"); + spin_unlock_irqrestore(q->irqlock, flags); + return; + } + } else { + if (list_empty(&vout->active_list)) { + v4l2_warn(vout->vfd->v4l2_dev, + "no entry in active_list, should not be here\n"); + spin_unlock_irqrestore(q->irqlock, flags); + return; + } + } + vb = list_first_entry(&vout->active_list, + struct videobuf_buffer, queue); + + if (deinterlace_3_field(vout)) + vb_next = list_first_entry(vout->active_list.next, + struct videobuf_buffer, queue); + + spin_unlock_irqrestore(q->irqlock, flags); + + if (vb->memory == V4L2_MEMORY_USERPTR) + vout->task.input.paddr = vb->baddr; + else + vout->task.input.paddr = videobuf_to_dma_contig(vb); + + if (is_pp_bypass(vout)) + vout->task.output.paddr = vout->task.input.paddr; + else { + if (deinterlace_3_field(vout)) { + if (vb->memory == V4L2_MEMORY_USERPTR) + vout->task.input.paddr_n = vb_next->baddr; + else + vout->task.input.paddr_n = + videobuf_to_dma_contig(vb_next); + } + vout->task.output.paddr = + vout->disp_bufs[vout->frame_count % FB_BUFS]; + mutex_lock(&vout->task_lock); + ret = ipu_queue_task(&vout->task); + mutex_unlock(&vout->task_lock); + if (ret < 0) + goto err; + } + + show_buf(vout, vout->frame_count % FB_BUFS); + + spin_lock_irqsave(q->irqlock, flags); + + list_del(&vb->queue); + + vb->state = VIDEOBUF_DONE; + + wake_up_interruptible(&vb->done); + + vout->frame_count++; + + /* pick next queue buf to setup timer */ + if (list_empty(&vout->queue_list)) + vout->timer_stop = true; + else { + vb = list_first_entry(&vout->queue_list, + struct videobuf_buffer, queue); + setup_buf_timer(vout, vb); + } + + spin_unlock_irqrestore(q->irqlock, flags); + + v4l2_dbg(1, debug, vout->vfd->v4l2_dev, "disp work finish one frame\n"); + + return; +err: + v4l2_err(vout->vfd->v4l2_dev, "display work fail\n"); + vout->timer_stop = true; + vb->state = VIDEOBUF_ERROR; + return; +} + +static void mxc_vout_timer_handler(unsigned long arg) +{ + struct mxc_vout_output *vout = + (struct mxc_vout_output *) arg; + struct videobuf_queue *q = &vout->vbq; + struct videobuf_buffer *vb; + unsigned long flags = 0; + + spin_lock_irqsave(q->irqlock, flags); + + /* + * put first queued entry into active, if previous entry did not + * finish, setup current entry's timer again. + */ + if (list_empty(&vout->queue_list)) { + spin_unlock_irqrestore(q->irqlock, flags); + return; + } + + /* move videobuf from queued list to active list */ + vb = list_first_entry(&vout->queue_list, + struct videobuf_buffer, queue); + list_del(&vb->queue); + list_add_tail(&vb->queue, &vout->active_list); + + if (queue_work(vout->v4l_wq, &vout->disp_work) == 0) { + v4l2_warn(vout->vfd->v4l2_dev, + "disp work was in queue already, queue buf again next time\n"); + list_del(&vb->queue); + list_add(&vb->queue, &vout->queue_list); + spin_unlock_irqrestore(q->irqlock, flags); + return; + } + + vb->state = VIDEOBUF_ACTIVE; + + spin_unlock_irqrestore(q->irqlock, flags); +} + +/* Video buffer call backs */ + +/* + * Buffer setup function is called by videobuf layer when REQBUF ioctl is + * called. This is used to setup buffers and return size and count of + * buffers allocated. After the call to this buffer, videobuf layer will + * setup buffer queue depending on the size and count of buffers + */ +static int mxc_vout_buffer_setup(struct videobuf_queue *q, unsigned int *count, + unsigned int *size) +{ + struct mxc_vout_output *vout = q->priv_data; + + if (!vout) + return -EINVAL; + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != q->type) + return -EINVAL; + + *size = PAGE_ALIGN(vout->task.input.width * vout->task.input.height * + fmt_to_bpp(vout->task.input.format)/8); + + return 0; +} + +/* + * This function will be called when VIDIOC_QBUF ioctl is called. + * It prepare buffers before give out for the display. This function + * converts user space virtual address into physical address if userptr memory + * exchange mechanism is used. + */ +static int mxc_vout_buffer_prepare(struct videobuf_queue *q, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + vb->state = VIDEOBUF_PREPARED; + return 0; +} + +/* + * Buffer queue funtion will be called from the videobuf layer when _QBUF + * ioctl is called. It is used to enqueue buffer, which is ready to be + * displayed. + * This function is protected by q->irqlock. + */ +static void mxc_vout_buffer_queue(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + struct mxc_vout_output *vout = q->priv_data; + + list_add_tail(&vb->queue, &vout->queue_list); + vb->state = VIDEOBUF_QUEUED; + + if (vout->timer_stop) { + if (deinterlace_3_field(vout) && + list_empty(&vout->active_list)) { + vb = list_first_entry(&vout->queue_list, + struct videobuf_buffer, queue); + list_del(&vb->queue); + list_add_tail(&vb->queue, &vout->active_list); + } else { + setup_buf_timer(vout, vb); + vout->timer_stop = false; + } + } +} + +/* + * Buffer release function is called from videobuf layer to release buffer + * which are already allocated + */ +static void mxc_vout_buffer_release(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + vb->state = VIDEOBUF_NEEDS_INIT; +} + +static int mxc_vout_mmap(struct file *file, struct vm_area_struct *vma) +{ + int ret; + struct mxc_vout_output *vout = file->private_data; + + if (!vout) + return -ENODEV; + + ret = videobuf_mmap_mapper(&vout->vbq, vma); + if (ret < 0) + v4l2_err(vout->vfd->v4l2_dev, + "offset invalid [offset=0x%lx]\n", + (vma->vm_pgoff << PAGE_SHIFT)); + + return ret; +} + +static int mxc_vout_release(struct file *file) +{ + unsigned int ret = 0; + struct videobuf_queue *q; + struct mxc_vout_output *vout = file->private_data; + + if (!vout) + return 0; + + if (--vout->open_cnt == 0) { + destroy_workqueue(vout->v4l_wq); + q = &vout->vbq; + if (q->streaming) + ret = mxc_vidioc_streamoff(file, vout, vout->type); + } + + return ret; +} + +static int mxc_vout_open(struct file *file) +{ + struct mxc_vout_output *vout = NULL; + int ret; + + vout = video_drvdata(file); + + if (vout == NULL) + return -ENODEV; + + if (vout->open_cnt++ == 0) { + vout->ctrl_rotate = 0; + vout->ctrl_vflip = 0; + vout->ctrl_hflip = 0; + update_display_setting(); + ret = update_setting_from_fbi(vout, vout->fbi); + if (ret < 0) + goto err; + + vout->v4l_wq = create_singlethread_workqueue("v4l2q"); + if (!vout->v4l_wq) { + v4l2_err(vout->vfd->v4l2_dev, + "Could not create work queue\n"); + ret = -ENOMEM; + goto err; + } + + INIT_WORK(&vout->disp_work, disp_work_func); + + INIT_LIST_HEAD(&vout->queue_list); + INIT_LIST_HEAD(&vout->active_list); + + vout->frame_count = 0; + + vout->win_pos.x = 0; + vout->win_pos.y = 0; + } + + file->private_data = vout; + +err: + return ret; +} + +/* + * V4L2 ioctls + */ +static int mxc_vidioc_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct mxc_vout_output *vout = fh; + + strlcpy(cap->driver, VOUT_NAME, sizeof(cap->driver)); + strlcpy(cap->card, vout->vfd->name, sizeof(cap->card)); + cap->bus_info[0] = '\0'; + cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OUTPUT; + + return 0; +} + +static int mxc_vidioc_enum_fmt_vid_out(struct file *file, void *fh, + struct v4l2_fmtdesc *fmt) +{ + if (fmt->index >= NUM_MXC_VOUT_FORMATS) + return -EINVAL; + + strlcpy(fmt->description, mxc_formats[fmt->index].description, + sizeof(fmt->description)); + fmt->pixelformat = mxc_formats[fmt->index].pixelformat; + + return 0; +} + +static int mxc_vidioc_g_fmt_vid_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct mxc_vout_output *vout = fh; + struct v4l2_rect *rect = NULL; + + f->fmt.pix.width = vout->task.input.width; + f->fmt.pix.height = vout->task.input.height; + f->fmt.pix.pixelformat = vout->task.input.format; + f->fmt.pix.sizeimage = vout->task.input.width * vout->task.input.height * + fmt_to_bpp(vout->task.input.format)/8; + + if (f->fmt.pix.priv) { + rect = (struct v4l2_rect *)f->fmt.pix.priv; + rect->left = vout->task.input.crop.pos.x; + rect->top = vout->task.input.crop.pos.y; + rect->width = vout->task.input.crop.w; + rect->height = vout->task.input.crop.h; + } + + return 0; +} + +static inline int ipu_try_task(struct ipu_task *task) +{ + int ret; + +again: + ret = ipu_check_task(task); + if (ret != IPU_CHECK_OK) { + if (ret > IPU_CHECK_ERR_MIN) { + if (ret == IPU_CHECK_ERR_SPLIT_INPUTW_OVER) { + task->input.crop.w -= 8; + goto again; + } + if (ret == IPU_CHECK_ERR_SPLIT_INPUTH_OVER) { + task->input.crop.h -= 8; + goto again; + } + if (ret == IPU_CHECK_ERR_SPLIT_OUTPUTW_OVER) { + task->output.crop.w -= 8; + goto again; + } + if (ret == IPU_CHECK_ERR_SPLIT_OUTPUTH_OVER) { + task->output.crop.h -= 8; + goto again; + } + ret = -EINVAL; + } + } else + ret = 0; + + return ret; +} + +static int mxc_vout_try_format(struct mxc_vout_output *vout, struct v4l2_format *f) +{ + int ret = 0; + struct v4l2_rect *rect = NULL; + + vout->task.input.width = f->fmt.pix.width; + vout->task.input.height = f->fmt.pix.height; + vout->task.input.format = f->fmt.pix.pixelformat; + + switch (f->fmt.pix.field) { + /* Images are in progressive format, not interlaced */ + case V4L2_FIELD_NONE: + break; + /* The two fields of a frame are passed in separate buffers, + in temporal order, i. e. the older one first. */ + case V4L2_FIELD_ALTERNATE: + v4l2_err(vout->vfd->v4l2_dev, + "V4L2_FIELD_ALTERNATE field format not supported yet!\n"); + break; + case V4L2_FIELD_INTERLACED_TB: + v4l2_info(vout->vfd->v4l2_dev, "Enable deinterlace.\n"); + vout->task.input.deinterlace.enable = true; + vout->task.input.deinterlace.field_fmt = + IPU_DEINTERLACE_FIELD_TOP; + break; + case V4L2_FIELD_INTERLACED_BT: + v4l2_info(vout->vfd->v4l2_dev, "Enable deinterlace.\n"); + vout->task.input.deinterlace.enable = true; + vout->task.input.deinterlace.field_fmt = + IPU_DEINTERLACE_FIELD_BOTTOM; + break; + default: + break; + } + + if (f->fmt.pix.priv) { + rect = (struct v4l2_rect *)f->fmt.pix.priv; + vout->task.input.crop.pos.x = rect->left; + vout->task.input.crop.pos.y = rect->top; + vout->task.input.crop.w = rect->width; + vout->task.input.crop.h = rect->height; + } else { + vout->task.input.crop.pos.x = 0; + vout->task.input.crop.pos.y = 0; + vout->task.input.crop.w = f->fmt.pix.width; + vout->task.input.crop.h = f->fmt.pix.height; + } + + /* assume task.output already set by S_CROP */ + if (is_pp_bypass(vout)) { + v4l2_info(vout->vfd->v4l2_dev, "Bypass IC.\n"); + vout->task.output.format = vout->task.input.format; + } else { + /* if need CSC, choose IPU-DP or IPU_IC do it */ + if (vout->disp_support_csc) { + if (colorspaceofpixel(vout->task.input.format) == YUV_CS) + vout->task.output.format = IPU_PIX_FMT_UYVY; + else + vout->task.output.format = IPU_PIX_FMT_RGB565; + } else { + if (colorspaceofpixel(vout->disp_fmt) == YUV_CS) + vout->task.output.format = IPU_PIX_FMT_UYVY; + else + vout->task.output.format = IPU_PIX_FMT_RGB565; + } + ret = ipu_try_task(&vout->task); + if (!ret) { + if (rect) { + rect->width = vout->task.input.crop.w; + rect->height = vout->task.input.crop.h; + } else { + f->fmt.pix.width = vout->task.input.crop.w; + f->fmt.pix.height = vout->task.input.crop.h; + } + } + } + + return ret; +} + +static int mxc_vidioc_s_fmt_vid_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct mxc_vout_output *vout = fh; + int ret = 0; + + if (vout->vbq.streaming) + return -EBUSY; + + mutex_lock(&vout->task_lock); + ret = mxc_vout_try_format(vout, f); + mutex_unlock(&vout->task_lock); + + return ret; +} + +static int mxc_vidioc_cropcap(struct file *file, void *fh, + struct v4l2_cropcap *cropcap) +{ + struct mxc_vout_output *vout = fh; + + if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + cropcap->bounds = vout->crop_bounds; + cropcap->defrect = vout->crop_bounds; + + return 0; +} + +static int mxc_vidioc_g_crop(struct file *file, void *fh, struct v4l2_crop *crop) +{ + struct mxc_vout_output *vout = fh; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + if (vout->task.output.crop.w && vout->task.output.crop.h) { + crop->c.left = vout->task.output.crop.pos.x; + crop->c.top = vout->task.output.crop.pos.y; + crop->c.width = vout->task.output.crop.w; + crop->c.height = vout->task.output.crop.h; + } else { + crop->c.left = 0; + crop->c.top = 0; + crop->c.width = vout->task.output.width; + crop->c.height = vout->task.output.height; + } + + return 0; +} + +static int mxc_vidioc_s_crop(struct file *file, void *fh, struct v4l2_crop *crop) +{ + struct mxc_vout_output *vout = fh; + struct v4l2_rect *b = &vout->crop_bounds; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + if (crop->c.width < 0 || crop->c.height < 0) + return -EINVAL; + + if (vout->vbq.streaming) + return -EBUSY; + + if (crop->c.top < b->top) + crop->c.top = b->top; + if (crop->c.top >= b->top + b->height) + crop->c.top = b->top + b->height - 1; + if (crop->c.height > b->top - crop->c.top + b->height) + crop->c.height = + b->top - crop->c.top + b->height; + + if (crop->c.left < b->left) + crop->c.left = b->left; + if (crop->c.left >= b->left + b->width) + crop->c.left = b->left + b->width - 1; + if (crop->c.width > b->left - crop->c.left + b->width) + crop->c.width = + b->left - crop->c.left + b->width; + + /* stride line limitation */ + crop->c.height -= crop->c.height % 8; + crop->c.width -= crop->c.width % 8; + + mutex_lock(&vout->task_lock); + + if (vout->disp_support_windows) { + vout->task.output.crop.pos.x = 0; + vout->task.output.crop.pos.y = 0; + vout->win_pos.x = crop->c.left; + vout->win_pos.y = crop->c.top; + vout->task.output.width = crop->c.width; + vout->task.output.height = crop->c.height; + } else { + vout->task.output.crop.pos.x = crop->c.left; + vout->task.output.crop.pos.y = crop->c.top; + } + + vout->task.output.crop.w = crop->c.width; + vout->task.output.crop.h = crop->c.height; + + mutex_unlock(&vout->task_lock); + + return 0; +} + +static int mxc_vidioc_queryctrl(struct file *file, void *fh, + struct v4l2_queryctrl *ctrl) +{ + int ret = 0; + + switch (ctrl->id) { + case V4L2_CID_ROTATE: + ret = v4l2_ctrl_query_fill(ctrl, 0, 270, 90, 0); + break; + case V4L2_CID_VFLIP: + ret = v4l2_ctrl_query_fill(ctrl, 0, 1, 1, 0); + break; + case V4L2_CID_HFLIP: + ret = v4l2_ctrl_query_fill(ctrl, 0, 1, 1, 0); + break; + case V4L2_CID_MXC_MOTION: + ret = v4l2_ctrl_query_fill(ctrl, 0, 2, 1, 0); + break; + default: + ctrl->name[0] = '\0'; + ret = -EINVAL; + } + return ret; +} + +static int mxc_vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *ctrl) +{ + int ret = 0; + struct mxc_vout_output *vout = fh; + + switch (ctrl->id) { + case V4L2_CID_ROTATE: + ctrl->value = vout->ctrl_rotate; + break; + case V4L2_CID_VFLIP: + ctrl->value = vout->ctrl_vflip; + break; + case V4L2_CID_HFLIP: + ctrl->value = vout->ctrl_hflip; + break; + case V4L2_CID_MXC_MOTION: + if (vout->task.input.deinterlace.enable) + ctrl->value = vout->task.input.deinterlace.motion; + else + ctrl->value = 0; + break; + default: + ret = -EINVAL; + } + return ret; +} + +static void setup_task_rotation(struct mxc_vout_output *vout) +{ + if (vout->ctrl_rotate == 0) { + if (vout->ctrl_vflip && vout->ctrl_hflip) + vout->task.output.rotate = IPU_ROTATE_180; + else if (vout->ctrl_vflip) + vout->task.output.rotate = IPU_ROTATE_VERT_FLIP; + else if (vout->ctrl_hflip) + vout->task.output.rotate = IPU_ROTATE_HORIZ_FLIP; + else + vout->task.output.rotate = IPU_ROTATE_NONE; + } else if (vout->ctrl_rotate == 90) { + if (vout->ctrl_vflip && vout->ctrl_hflip) + vout->task.output.rotate = IPU_ROTATE_90_LEFT; + else if (vout->ctrl_vflip) + vout->task.output.rotate = IPU_ROTATE_90_RIGHT_VFLIP; + else if (vout->ctrl_hflip) + vout->task.output.rotate = IPU_ROTATE_90_RIGHT_HFLIP; + else + vout->task.output.rotate = IPU_ROTATE_90_RIGHT; + } else if (vout->ctrl_rotate == 180) { + if (vout->ctrl_vflip && vout->ctrl_hflip) + vout->task.output.rotate = IPU_ROTATE_NONE; + else if (vout->ctrl_vflip) + vout->task.output.rotate = IPU_ROTATE_HORIZ_FLIP; + else if (vout->ctrl_hflip) + vout->task.output.rotate = IPU_ROTATE_VERT_FLIP; + else + vout->task.output.rotate = IPU_ROTATE_180; + } else if (vout->ctrl_rotate == 270) { + if (vout->ctrl_vflip && vout->ctrl_hflip) + vout->task.output.rotate = IPU_ROTATE_90_RIGHT; + else if (vout->ctrl_vflip) + vout->task.output.rotate = IPU_ROTATE_90_RIGHT_HFLIP; + else if (vout->ctrl_hflip) + vout->task.output.rotate = IPU_ROTATE_90_RIGHT_VFLIP; + else + vout->task.output.rotate = IPU_ROTATE_90_LEFT; + } +} + +static int mxc_vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *ctrl) +{ + int ret = 0; + struct mxc_vout_output *vout = fh; + + mutex_lock(&vout->task_lock); + switch (ctrl->id) { + case V4L2_CID_ROTATE: + { + vout->ctrl_rotate = (ctrl->value/90) * 90; + if (vout->ctrl_rotate > 270) + vout->ctrl_rotate = 270; + setup_task_rotation(vout); + break; + } + case V4L2_CID_VFLIP: + { + vout->ctrl_vflip = ctrl->value; + setup_task_rotation(vout); + break; + } + case V4L2_CID_HFLIP: + { + vout->ctrl_hflip = ctrl->value; + setup_task_rotation(vout); + break; + } + case V4L2_CID_MXC_MOTION: + { + vout->task.input.deinterlace.motion = ctrl->value; + break; + } + default: + ret = -EINVAL; + } + mutex_unlock(&vout->task_lock); + return ret; +} + +static int mxc_vidioc_reqbufs(struct file *file, void *fh, + struct v4l2_requestbuffers *req) +{ + int ret = 0; + struct mxc_vout_output *vout = fh; + struct videobuf_queue *q = &vout->vbq; + + if (req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + /* should not be here after streaming, videobuf_reqbufs will control */ + mutex_lock(&vout->task_lock); + + ret = videobuf_reqbufs(q, req); + + mutex_unlock(&vout->task_lock); + return ret; +} + +static int mxc_vidioc_querybuf(struct file *file, void *fh, + struct v4l2_buffer *b) +{ + struct mxc_vout_output *vout = fh; + + return videobuf_querybuf(&vout->vbq, b); +} + +static int mxc_vidioc_qbuf(struct file *file, void *fh, + struct v4l2_buffer *buffer) +{ + struct mxc_vout_output *vout = fh; + + return videobuf_qbuf(&vout->vbq, buffer); +} + +static int mxc_vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) +{ + struct mxc_vout_output *vout = fh; + + if (!vout->vbq.streaming) + return -EINVAL; + + if (file->f_flags & O_NONBLOCK) + return videobuf_dqbuf(&vout->vbq, (struct v4l2_buffer *)b, 1); + else + return videobuf_dqbuf(&vout->vbq, (struct v4l2_buffer *)b, 0); +} + +static int set_window_position(struct mxc_vout_output *vout) +{ + struct fb_info *fbi = vout->fbi; + mm_segment_t old_fs; + int ret; + + if (vout->disp_support_windows) { + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = fbi->fbops->fb_ioctl(fbi, MXCFB_SET_OVERLAY_POS, + (unsigned long)&vout->win_pos); + set_fs(old_fs); + } + + return ret; +} + +static int config_disp_output(struct mxc_vout_output *vout) +{ + struct fb_info *fbi = vout->fbi; + struct fb_var_screeninfo var; + int i, display_buf_size, fb_num, ret; + + memcpy(&var, &fbi->var, sizeof(var)); + + var.xres = vout->task.output.width; + var.yres = vout->task.output.height; + if (is_pp_bypass(vout)) { + fb_num = 1; + /* input crop */ + if (vout->task.input.width > vout->task.output.width) + var.xres_virtual = vout->task.input.width; + else + var.xres_virtual = var.xres; + if (vout->task.input.height > vout->task.output.height) + var.yres_virtual = vout->task.input.height; + else + var.yres_virtual = var.yres; + } else { + fb_num = FB_BUFS; + var.xres_virtual = var.xres; + var.yres_virtual = fb_num * var.yres; + } + var.bits_per_pixel = fmt_to_bpp(vout->task.output.format); + var.nonstd = vout->task.output.format; + + v4l2_dbg(1, debug, vout->vfd->v4l2_dev, + "set display fb to %d %d\n", + var.xres, var.yres); + + /* Init display channel through fb API */ + var.yoffset = 0; + var.activate |= FB_ACTIVATE_FORCE; + console_lock(); + fbi->flags |= FBINFO_MISC_USEREVENT; + ret = fb_set_var(fbi, &var); + fbi->flags &= ~FBINFO_MISC_USEREVENT; + console_unlock(); + if (ret < 0) + return ret; + + display_buf_size = fbi->fix.line_length * fbi->var.yres; + for (i = 0; i < fb_num; i++) + vout->disp_bufs[i] = fbi->fix.smem_start + i * display_buf_size; + + ret = set_window_position(vout); + if (ret < 0) + return ret; + + console_lock(); + ret = fb_blank(fbi, FB_BLANK_UNBLANK); + console_unlock(); + + return ret; +} + +static void release_disp_output(struct mxc_vout_output *vout) +{ + struct fb_info *fbi = vout->fbi; + + console_lock(); + fb_blank(fbi, FB_BLANK_POWERDOWN); + console_unlock(); + + /* fix if ic bypass crack smem_start */ + if (is_pp_bypass(vout)) + fbi->fix.smem_start = vout->disp_bufs[0]; + + if (get_ipu_channel(fbi) == MEM_BG_SYNC) { + console_lock(); + fb_blank(fbi, FB_BLANK_UNBLANK); + console_unlock(); + } +} + +static int mxc_vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i) +{ + struct mxc_vout_output *vout = fh; + struct videobuf_queue *q = &vout->vbq; + int ret; + + if (q->streaming) { + v4l2_err(vout->vfd->v4l2_dev, + "video output already run\n"); + ret = -EBUSY; + goto done; + } + + if (deinterlace_3_field(vout) && list_is_singular(&q->stream)) { + v4l2_err(vout->vfd->v4l2_dev, + "deinterlacing: need queue 2 frame before streamon\n"); + ret = -EINVAL; + goto done; + } + + ret = config_disp_output(vout); + if (ret < 0) { + v4l2_err(vout->vfd->v4l2_dev, + "Config display output failed\n"); + goto done; + } + + init_timer(&vout->timer); + vout->timer.function = mxc_vout_timer_handler; + vout->timer.data = (unsigned long)vout; + vout->timer_stop = true; + + vout->start_jiffies = jiffies; + + ret = videobuf_streamon(q); +done: + return ret; +} + +static int mxc_vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i) +{ + struct mxc_vout_output *vout = fh; + struct videobuf_queue *q = &vout->vbq; + int ret; + + del_timer(&vout->timer); + + cancel_work_sync(&vout->disp_work); + flush_workqueue(vout->v4l_wq); + + release_disp_output(vout); + + ret = videobuf_streamoff(&vout->vbq); + if (ret < 0) + goto err; + + ret = videobuf_mmap_free(q); + +err: + return ret; +} + +static const struct v4l2_ioctl_ops mxc_vout_ioctl_ops = { + .vidioc_querycap = mxc_vidioc_querycap, + .vidioc_enum_fmt_vid_out = mxc_vidioc_enum_fmt_vid_out, + .vidioc_g_fmt_vid_out = mxc_vidioc_g_fmt_vid_out, + .vidioc_s_fmt_vid_out = mxc_vidioc_s_fmt_vid_out, + .vidioc_cropcap = mxc_vidioc_cropcap, + .vidioc_g_crop = mxc_vidioc_g_crop, + .vidioc_s_crop = mxc_vidioc_s_crop, + .vidioc_queryctrl = mxc_vidioc_queryctrl, + .vidioc_g_ctrl = mxc_vidioc_g_ctrl, + .vidioc_s_ctrl = mxc_vidioc_s_ctrl, + .vidioc_reqbufs = mxc_vidioc_reqbufs, + .vidioc_querybuf = mxc_vidioc_querybuf, + .vidioc_qbuf = mxc_vidioc_qbuf, + .vidioc_dqbuf = mxc_vidioc_dqbuf, + .vidioc_streamon = mxc_vidioc_streamon, + .vidioc_streamoff = mxc_vidioc_streamoff, +}; + +static const struct v4l2_file_operations mxc_vout_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = video_ioctl2, + .mmap = mxc_vout_mmap, + .open = mxc_vout_open, + .release = mxc_vout_release, +}; + +static struct video_device mxc_vout_template = { + .name = "MXC Video Output", + .fops = &mxc_vout_fops, + .ioctl_ops = &mxc_vout_ioctl_ops, + .release = video_device_release, +}; + +static struct videobuf_queue_ops mxc_vout_vbq_ops = { + .buf_setup = mxc_vout_buffer_setup, + .buf_prepare = mxc_vout_buffer_prepare, + .buf_release = mxc_vout_buffer_release, + .buf_queue = mxc_vout_buffer_queue, +}; + +static void mxc_vout_free_output(struct mxc_vout_dev *dev) +{ + int i; + struct mxc_vout_output *vout; + struct video_device *vfd; + + for (i = 0; i < dev->out_num; i++) { + vout = dev->out[i]; + vfd = vout->vfd; + if (vfd) { + if (!video_is_registered(vfd)) + video_device_release(vfd); + else + video_unregister_device(vfd); + } + kfree(vout); + } +} + +static int __init mxc_vout_setup_output(struct mxc_vout_dev *dev) +{ + struct videobuf_queue *q; + struct fb_info *fbi; + struct mxc_vout_output *vout; + int i, ret = 0; + + update_display_setting(); + + /* all output/overlay based on fb */ + for (i = 0; i < num_registered_fb; i++) { + fbi = registered_fb[i]; + + vout = kzalloc(sizeof(struct mxc_vout_output), GFP_KERNEL); + if (!vout) { + ret = -ENOMEM; + break; + } + + dev->out[dev->out_num] = vout; + dev->out_num++; + + vout->fbi = fbi; + vout->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + vout->vfd = video_device_alloc(); + if (!vout->vfd) { + ret = -ENOMEM; + break; + } + + *vout->vfd = mxc_vout_template; + vout->vfd->debug = debug; + vout->vfd->v4l2_dev = &dev->v4l2_dev; + vout->vfd->lock = &vout->mutex; + + mutex_init(&vout->mutex); + mutex_init(&vout->task_lock); + + strlcpy(vout->vfd->name, fbi->fix.id, sizeof(vout->vfd->name)); + + video_set_drvdata(vout->vfd, vout); + + if (video_register_device(vout->vfd, + VFL_TYPE_GRABBER, video_nr + i) < 0) { + ret = -ENODEV; + break; + } + + q = &vout->vbq; + q->dev = dev->dev; + spin_lock_init(&vout->vbq_lock); + videobuf_queue_dma_contig_init(q, &mxc_vout_vbq_ops, q->dev, + &vout->vbq_lock, vout->type, V4L2_FIELD_NONE, + sizeof(struct videobuf_buffer), vout, NULL); + + v4l2_info(vout->vfd->v4l2_dev, "V4L2 device registered as %s\n", + video_device_node_name(vout->vfd)); + + } + + return ret; +} + +static int __init mxc_vout_probe(struct platform_device *pdev) +{ + int ret; + struct mxc_vout_dev *dev; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->dev = &pdev->dev; + dev->dev->dma_mask = kmalloc(sizeof(*dev->dev->dma_mask), GFP_KERNEL); + *dev->dev->dma_mask = DMA_BIT_MASK(32); + dev->dev->coherent_dma_mask = DMA_BIT_MASK(32); + + ret = v4l2_device_register(dev->dev, &dev->v4l2_dev); + if (ret) { + dev_err(dev->dev, "v4l2_device_register failed\n"); + goto free_dev; + } + + ret = mxc_vout_setup_output(dev); + if (ret < 0) + goto rel_vdev; + + return 0; + +rel_vdev: + mxc_vout_free_output(dev); + v4l2_device_unregister(&dev->v4l2_dev); +free_dev: + kfree(dev); + return ret; +} + +static int mxc_vout_remove(struct platform_device *pdev) +{ + struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); + struct mxc_vout_dev *dev = container_of(v4l2_dev, struct + mxc_vout_dev, v4l2_dev); + + mxc_vout_free_output(dev); + v4l2_device_unregister(v4l2_dev); + kfree(dev); + return 0; +} + +static struct platform_driver mxc_vout_driver = { + .driver = { + .name = "mxc_v4l2_output", + }, + .probe = mxc_vout_probe, + .remove = mxc_vout_remove, +}; + +static int __init mxc_vout_init(void) +{ + if (platform_driver_register(&mxc_vout_driver) != 0) { + printk(KERN_ERR VOUT_NAME ":Could not register Video driver\n"); + return -EINVAL; + } + return 0; +} + +static void mxc_vout_cleanup(void) +{ + platform_driver_unregister(&mxc_vout_driver); +} + +module_init(mxc_vout_init); +module_exit(mxc_vout_cleanup); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("V4L2-driver for MXC video output"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/mxc/mxc_ipuv3_fb.c b/drivers/video/mxc/mxc_ipuv3_fb.c index d1252bae883c..11e67449fea9 100644 --- a/drivers/video/mxc/mxc_ipuv3_fb.c +++ b/drivers/video/mxc/mxc_ipuv3_fb.c @@ -244,8 +244,7 @@ static int _setup_disp_channel2(struct fb_info *fbi) IPU_ROTATE_NONE, base, base, - (fbi->var.accel_flags == - FB_ACCEL_TRIPLE_FLAG) ? base : 0, + base, 0, 0); if (retval) { dev_err(fbi->device, @@ -577,8 +576,7 @@ static int mxcfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) if (var->xres_virtual < var->xres) var->xres_virtual = var->xres; - /* Default Y virtual size is 3*yres */ - if (var->yres_virtual < var->yres * 3) + if (var->yres_virtual < var->yres) var->yres_virtual = var->yres * 3; if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) && @@ -1235,6 +1233,17 @@ mxcfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) mxc_fbi->cur_ipu_alpha_buf); } + /* update u/v offset */ + ipu_update_channel_offset(mxc_fbi->ipu, mxc_fbi->ipu_ch, + IPU_INPUT_BUFFER, + bpp_to_pixfmt(info), + info->var.xres_virtual, + info->var.yres_virtual, + info->var.xres_virtual, + 0, 0, + var->yoffset, + var->xoffset); + ipu_select_buffer(mxc_fbi->ipu, mxc_fbi->ipu_ch, IPU_INPUT_BUFFER, mxc_fbi->cur_ipu_buf); ipu_clear_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_irq); |