From 0eb553bf96e2c990d3bfccaa07da0863624c89ab Mon Sep 17 00:00:00 2001 From: Ian Wisbon Date: Thu, 10 Feb 2011 17:15:15 -0500 Subject: Linux 2.6.31 Release for Digi ConnectCore Wi-i.MX boards Digi 01262011 Release --- drivers/media/video/mxc/output/mxc_v4l2_output.c | 1241 +++++++++++----------- 1 file changed, 627 insertions(+), 614 deletions(-) (limited to 'drivers/media/video/mxc/output/mxc_v4l2_output.c') diff --git a/drivers/media/video/mxc/output/mxc_v4l2_output.c b/drivers/media/video/mxc/output/mxc_v4l2_output.c index 408f9b7871a8..b7bbfed73019 100644 --- a/drivers/media/video/mxc/output/mxc_v4l2_output.c +++ b/drivers/media/video/mxc/output/mxc_v4l2_output.c @@ -44,9 +44,7 @@ vout_data *g_vout; #define LOAD_3FIELDS(vout) ((INTERLACED_CONTENT(vout)) && \ ((vout)->motion_sel != HIGH_MOTION)) -#define SDC_FG_FB_FORMAT IPU_PIX_FMT_RGB565 - -struct v4l2_output mxc_outputs[2] = { +struct v4l2_output mxc_outputs[1] = { { .index = MXC_V4L2_OUT_2_SDC, .name = "DISP3 Video Out", @@ -54,23 +52,14 @@ struct v4l2_output mxc_outputs[2] = { but no other choice */ .audioset = 0, .modulator = 0, - .std = V4L2_STD_UNKNOWN}, - { - .index = MXC_V4L2_OUT_2_ADC, - .name = "DISPx 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 spinlock_t g_lock = SPIN_LOCK_UNLOCKED; static int last_index_n; -static int last_index_c; 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; @@ -256,10 +245,14 @@ 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->display_ch, IPU_INPUT_BUFFER) != next_buf) ret = ipu_select_buffer(vout->display_ch, IPU_INPUT_BUFFER, next_buf); + else + dev_dbg(&vout->video_dev->dev, + "display buffer not ready for select\n"); return ret; } @@ -290,110 +283,138 @@ static void setup_next_buf_timer(vout_data *vout, int index) "timer handler next schedule: %lu\n", timeout); } -static int wait_for_disp_vsync(vout_data *vout) +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; - /* 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); + /* make sure buf[vout->disp_buf_num] in showing */ + while (ipu_check_buffer_busy(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) { + /* ic_bypass need clear display buffer ready for next update*/ + ipu_clear_buffer_ready(vout->display_ch, IPU_INPUT_BUFFER, + vout->disp_buf_num); + } + } + } + + 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->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 timer_work_func(struct work_struct *work) +static void icbypass_work_func(struct work_struct *work) { vout_data *vout = - container_of(work, vout_data, timer_work); + container_of(work, vout_data, icbypass_work); int index, ret; int last_buf; unsigned long lock_flags = 0; - /* wait 2 first frame finish for ic bypass mode*/ - if ((g_buf_output_cnt == 0) && vout->ic_bypass) { - wait_for_disp_vsync(vout); - wait_for_disp_vsync(vout); - spin_lock_irqsave(&g_lock, lock_flags); - last_buf = vout->ipu_buf[0]; - vout->v4l2_bufs[last_buf].flags = V4L2_BUF_FLAG_DONE; - queue_buf(&vout->done_q, last_buf); - vout->ipu_buf[0] = -1; - last_buf = vout->ipu_buf[1]; - vout->v4l2_bufs[last_buf].flags = V4L2_BUF_FLAG_DONE; - queue_buf(&vout->done_q, last_buf); - vout->ipu_buf[1] = -1; - g_buf_output_cnt = 2; - wake_up_interruptible(&vout->v4l_bufq); - if (vout->state == STATE_STREAM_PAUSED) { - index = peek_next_buf(&vout->ready_q); - if (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; - } - - if (wait_for_disp_vsync(vout) < 0) { - /* ic_bypass need clear display buffer ready for next update*/ - ipu_clear_buffer_ready(vout->display_ch, IPU_INPUT_BUFFER, - !vout->next_done_ipu_buf); - } + finish_previous_frame(vout); spin_lock_irqsave(&g_lock, lock_flags); - if (vout->ic_bypass) { - 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; - } + 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++; - if (vout->ic_bypass) - ret = select_display_buffer(vout, vout->next_rdy_ipu_buf); - else if (LOAD_3FIELDS(vout)) - ret = ipu_select_multi_vdi_buffer(vout->next_rdy_ipu_buf); - else - ret = ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, - vout->next_rdy_ipu_buf); + vout->ipu_buf[vout->next_rdy_ipu_buf] = index; + ret = ipu_update_channel_buffer(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 set IPU buffer ready\n"); + "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; - /* Non IC split action */ - if (!vout->pp_split) - 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; + } - /* 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 (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); +} - if (vout->state == STATE_STREAM_STOPPING) { - if ((vout->ipu_buf[0] == -1) && (vout->ipu_buf[1] == -1)) { - vout->state = STATE_STREAM_OFF; - } +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; + + 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) @@ -401,13 +422,13 @@ static void mxc_v4l2out_timer_handler(unsigned long arg) int index, ret; unsigned long lock_flags = 0; vout_data *vout = (vout_data *) arg; - unsigned int aid_field_offset = 0, current_field_offset = 0; 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 @@ -419,98 +440,92 @@ static void mxc_v4l2out_timer_handler(unsigned long arg) goto exit0; } - /* Dequeue buffer and pass to IPU */ - if (INTERLACED_CONTENT(vout)) { - if (((LOAD_3FIELDS(vout)) && (vout->next_rdy_ipu_buf)) || - ((!LOAD_3FIELDS(vout)) && !(vout->next_rdy_ipu_buf))) { - aid_field_offset = vout->bytesperline; - current_field_offset = 0; - index = last_index_n; - } else { - aid_field_offset = 0; - current_field_offset = vout->bytesperline; - 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++; - last_index_n = index; - } - } else { - current_field_offset = 0; - index = dequeue_buf(&vout->ready_q); - if (index == -1) { /* no buffers ready, should never occur */ + /* 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, - "mxc_v4l2out: timer - no queued buffers ready\n"); - goto exit0; + "ic bypass work was in queue already!\n "); + vout->state = STATE_STREAM_PAUSED; } - g_buf_dq_cnt++; - vout->frame_count++; + goto exit0; + } else if (!vout->fb_blank && + (ipu_get_cur_buffer_idx(vout->display_ch, IPU_INPUT_BUFFER) + == vout->next_disp_ipu_buf)) { + dev_dbg(&vout->video_dev->dev, "IPU disp busy\n"); + get_cur_fb_blank(vout); + index = peek_next_buf(&vout->ready_q); + setup_next_buf_timer(vout, index); + goto exit0; } + vout->fb_blank = 0; + + /* 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 (vout->ic_bypass) { + 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; - ret = ipu_update_channel_buffer(vout->display_ch, IPU_INPUT_BUFFER, - vout->next_rdy_ipu_buf, - vout->v4l2_bufs[index].m.offset); + vout->ipu_buf_n[vout->next_rdy_ipu_buf] = index; + ret = ipu_update_channel_buffer(vout->post_proc_ch, + IPU_INPUT_BUFFER, + vout->next_rdy_ipu_buf, + vout->v4l2_bufs[index].m.offset); + ret += ipu_update_channel_buffer(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(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 { - if (LOAD_3FIELDS(vout)) { - int index_n = index; - int index_p = last_index_c; - index = last_index_n; - vout->ipu_buf_p[vout->next_rdy_ipu_buf] = index_p; - vout->ipu_buf[vout->next_rdy_ipu_buf] = last_index_c = index; - vout->ipu_buf_n[vout->next_rdy_ipu_buf] = last_index_n = index_n; - last_index_n = vout->ipu_buf_n[vout->next_rdy_ipu_buf]; - last_index_c = vout->ipu_buf[vout->next_rdy_ipu_buf]; + 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->post_proc_ch, - IPU_INPUT_BUFFER, - vout->next_rdy_ipu_buf, - vout->v4l2_bufs[index].m.offset+current_field_offset); - ret += ipu_update_channel_buffer(MEM_VDI_PRP_VF_MEM_P, - IPU_INPUT_BUFFER, - vout->next_rdy_ipu_buf, - vout->v4l2_bufs[index_p].m.offset+aid_field_offset); - ret += ipu_update_channel_buffer(MEM_VDI_PRP_VF_MEM_N, - IPU_INPUT_BUFFER, - vout->next_rdy_ipu_buf, - vout->v4l2_bufs[index_n].m.offset+aid_field_offset); - } 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->post_proc_ch, - IPU_INPUT_BUFFER, - 0,/* vout->next_rdy_ipu_buf,*/ - (vout->v4l2_bufs[index].m.offset) + - vout->pp_left_stripe.input_column + - current_field_offset); - - /* the U/V offset has to be updated inside of IDMAC */ - /* according to stripe offset */ - ret += ipu_update_channel_offset(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, - 0, - vout->pp_left_stripe.input_column + current_field_offset); - - } else - ret = ipu_update_channel_buffer(vout->post_proc_ch, - IPU_INPUT_BUFFER, - vout->next_rdy_ipu_buf, - vout->v4l2_bufs[index].m.offset + - current_field_offset); - } + 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->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->post_proc_ch, + IPU_INPUT_BUFFER, + vout->next_rdy_ipu_buf, + vout->v4l2_bufs[index].m.offset); } if (ret < 0) { @@ -521,9 +536,35 @@ static void mxc_v4l2out_timer_handler(unsigned long arg) } /* set next buffer ready */ - if (queue_work(vout->v4l_wq, &vout->timer_work) == 0) { - dev_err(&vout->video_dev->dev, "work was in queue already!\n "); + if (LOAD_3FIELDS(vout)) + ret = ipu_select_multi_vdi_buffer(vout->next_rdy_ipu_buf); + else + ret = ipu_select_buffer(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); @@ -540,12 +581,15 @@ static irqreturn_t mxc_v4l2out_work_irq_handler(int irq, void *dev_id) int index; unsigned long lock_flags = 0; vout_data *vout = dev_id; - int pp_out_buf_num = 0; + 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; + u32 eba_offset = 0; + u32 vertical_offset = 0; u16 x_pos; u16 y_pos; int ret = -1; @@ -563,17 +607,37 @@ static irqreturn_t mxc_v4l2out_work_irq_handler(int irq, void *dev_id) if (last_buf != -1) { /* If IC split mode on, update output buffer number */ if (vout->pp_split) { - pp_out_buf_num = vout->pp_split_buf_num & 1;/* left/right stripe */ - disp_buf_num = vout->pp_split_buf_num >> 1; - disp_buf_num_next = ((vout->pp_split_buf_num+2) & 3) >> 1; - if (!pp_out_buf_num) {/* next buffer is right stripe*/ - eba_offset = vout->pp_right_stripe.input_column;/*always right stripe*/ + 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->post_proc_ch, IPU_INPUT_BUFFER, - 1, /* right stripe */ + (1 - local_buffer), (vout->v4l2_bufs[vout->ipu_buf[disp_buf_num]].m.offset) - + eba_offset); - + + eba_offset + vertical_offset * vout->bytesperline); ret += ipu_update_channel_offset(vout->post_proc_ch, IPU_INPUT_BUFFER, vout->v2f.fmt.pix.pixelformat, @@ -582,68 +646,78 @@ static irqreturn_t mxc_v4l2out_work_irq_handler(int irq, void *dev_id) vout->bytesperline, vout->offset.u_offset, vout->offset.v_offset, - 0, - vout->pp_right_stripe.input_column); + vertical_offset, + eba_offset); /* select right stripe */ - ret += ipu_select_buffer(vout->post_proc_ch, - IPU_INPUT_BUFFER, 1); + ret += ipu_select_buffer(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"); + } - vout->ipu_buf[vout->next_done_ipu_buf] = -1; - vout->next_done_ipu_buf = !vout->next_done_ipu_buf; + /* 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; - } else /* right stripe is done, run display refresh */ - select_display_buffer(vout, disp_buf_num); + eba_offset = ((vout->pp_split_buf_num & 1) ? + vout->pp_down_stripe.output_column : + vout->pp_up_stripe.output_column); - vout->next_rdy_ipu_buf = !vout->next_rdy_ipu_buf; + } 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; + } - /* offset for next buffer's EBA */ - pp_out_buf_offset = pp_out_buf_num ? vout->pp_right_stripe.output_column : - vout->pp_left_stripe.output_column; - eba_offset = 0; 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; + 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; + pp_out_buf_offset + eba_offset; ipu_update_channel_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER, - pp_out_buf_num, eba_offset); + local_buffer, eba_offset); /* next buffer ready */ - ret = ipu_select_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER, pp_out_buf_num); + ret = ipu_select_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER, local_buffer); - /* next stripe_buffer index 0..3 */ - vout->pp_split_buf_num = (vout->pp_split_buf_num + 1) & 3; + /* next stripe_buffer index 0..7 */ + vout->pp_split_buf_num = (vout->pp_split_buf_num + vout->pp_split) & 0x7; } else { - /* show to display */ - select_display_buffer(vout, vout->next_done_ipu_buf); + disp_buf_num = vout->next_done_ipu_buf; ret += ipu_select_buffer(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 ? pp_out_buf_num : 1; + release_buffer = vout->pp_split ? (!(vout->pp_split_buf_num & 0x3)) : 1; if (release_buffer) { - if ((!INTERLACED_CONTENT(vout)) || (vout->next_done_ipu_buf)) { - 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); - } + 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; } - vout->next_done_ipu_buf = !vout->next_done_ipu_buf; + /* 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 */ @@ -702,7 +776,6 @@ static int init_VDI_channel(vout_data *vout, ipu_channel_params_t params) 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, - dma_addr_t phyaddr_0, dma_addr_t phyaddr_1, uint32_t u_offset, uint32_t v_offset) { struct device *dev = &vout->video_dev->dev; @@ -710,7 +783,7 @@ static int init_VDI_in_channel_buffer(vout_data *vout, uint32_t in_pixel_fmt, if (ipu_init_channel_buffer(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->bytesperline, + vout->v4l2_bufs[vout->ipu_buf[0]].m.offset, vout->v4l2_bufs[vout->ipu_buf[0]].m.offset, u_offset, v_offset) != 0) { dev_err(dev, "Error initializing VDI current input buffer\n"); @@ -721,7 +794,7 @@ static int init_VDI_in_channel_buffer(vout_data *vout, uint32_t in_pixel_fmt, 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->v4l2_bufs[vout->ipu_buf_p[0]].m.offset+vout->bytesperline, vout->v4l2_bufs[vout->ipu_buf_p[0]].m.offset+vout->bytesperline, u_offset, v_offset) != 0) { dev_err(dev, "Error initializing VDI previous input buffer\n"); @@ -731,7 +804,7 @@ static int init_VDI_in_channel_buffer(vout_data *vout, uint32_t in_pixel_fmt, 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->v4l2_bufs[vout->ipu_buf_n[0]].m.offset+vout->bytesperline, vout->v4l2_bufs[vout->ipu_buf_n[0]].m.offset+vout->bytesperline, u_offset, v_offset) != 0) { dev_err(dev, "Error initializing VDI next input buffer\n"); @@ -759,10 +832,7 @@ static int init_VDI(ipu_channel_params_t params, vout_data *vout, 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; - if (vout->display_ch == ADC_SYS2) - params.mem_prp_vf_mem.out_pixel_fmt = SDC_FG_FB_FORMAT; - else - params.mem_prp_vf_mem.out_pixel_fmt = bpp_to_fmt(fbi); + 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"); @@ -775,8 +845,6 @@ static int init_VDI(ipu_channel_params_t params, vout_data *vout, params.mem_prp_vf_mem.in_height, bytes_per_pixel(params.mem_prp_vf_mem. in_pixel_fmt), - vout->v4l2_bufs[vout->ipu_buf[0]].m.offset, - vout->v4l2_bufs[vout->ipu_buf[1]].m.offset, vout->offset.u_offset, vout->offset.v_offset) != 0) { return -EINVAL; @@ -805,7 +873,6 @@ static int init_VDI(ipu_channel_params_t params, vout_data *vout, dev_err(dev, "Error initializing PRP output buffer\n"); return -EINVAL; } - if (ipu_init_channel(MEM_ROT_VF_MEM, NULL) != 0) { dev_err(dev, "Error initializing PP ROT channel\n"); return -EINVAL; @@ -883,14 +950,14 @@ static int init_PP(ipu_channel_params_t *params, vout_data *vout, 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; - if (vout->display_ch == ADC_SYS2) - params->mem_pp_mem.out_pixel_fmt = SDC_FG_FB_FORMAT; - else - params->mem_pp_mem.out_pixel_fmt = bpp_to_fmt(fbi); + 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; @@ -907,37 +974,66 @@ static int init_PP(ipu_channel_params_t *params, vout_data *vout, 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.out_resize_ratio = 0; /* 0 means unused */ - + 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) { - 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 * + 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 * + 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 * + 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 * + 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.out_resize_ratio = vout->pp_left_stripe.irr; - + 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; } @@ -946,6 +1042,12 @@ static int init_PP(ipu_channel_params_t *params, vout_data *vout, 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->post_proc_ch, IPU_INPUT_BUFFER, params->mem_pp_mem.in_pixel_fmt, @@ -955,8 +1057,8 @@ static int init_PP(ipu_channel_params_t *params, vout_data *vout, bytes_per_pixel(params->mem_pp_mem. in_pixel_fmt), IPU_ROTATE_NONE, - vout->v4l2_bufs[vout->ipu_buf[0]].m.offset, - vout->v4l2_bufs[vout->ipu_buf[1]].m.offset, + phy_addr0, + phy_addr1, vout->offset.u_offset, vout->offset.v_offset) != 0) { dev_err(dev, "Error initializing PP input buffer\n"); @@ -1048,20 +1150,27 @@ static int init_PP(ipu_channel_params_t *params, vout_data *vout, ipu_update_channel_buffer(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_left_stripe.input_column + + vout->pp_up_stripe.input_column * vout->bytesperline); + + ipu_update_channel_buffer(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_right_stripe.input_column + + vout->pp_up_stripe.input_column * vout->bytesperline); + ipu_update_channel_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER, 0, vout->display_bufs[0] + eba_offset + - vout->pp_left_stripe.output_column); + vout->pp_left_stripe.output_column + + vout->pp_up_stripe.output_column); ipu_update_channel_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER, 1, vout->display_bufs[0] + eba_offset + - vout->pp_right_stripe.output_column); + vout->pp_right_stripe.output_column + + vout->pp_up_stripe.output_column); } return 0; @@ -1084,8 +1193,8 @@ static int mxc_v4l2out_streamon(vout_data * vout) registered_fb[vout->output_fb_num[vout->cur_disp_output]]; u16 out_width; u16 out_height; - bool use_direct_adc = false; mm_segment_t old_fs; + unsigned int ipu_ch = CHAN_NONE; int rc = 0; dev_dbg(dev, "mxc_v4l2out_streamon: field format=%d\n", @@ -1107,241 +1216,132 @@ static int mxc_v4l2out_streamon(vout_data * vout) 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 = 1; + vout->next_rdy_ipu_buf = vout->next_disp_ipu_buf = 1; + vout->fb_blank = 0; vout->pp_split = 0; - - if (!INTERLACED_CONTENT(vout)) { - vout->next_done_ipu_buf = vout->next_rdy_ipu_buf = 0; - vout->ipu_buf[0] = dequeue_buf(&vout->ready_q); + 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; + 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) && - out_width > ipu_ic_out_max_width_size) { - vout->pp_split = 1; + 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] = dequeue_buf(&vout->ready_q); - vout->frame_count = 2; + 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; - last_index_n = vout->ipu_buf[0]; } else { vout->ipu_buf_p[0] = dequeue_buf(&vout->ready_q); - vout->ipu_buf[0] = vout->ipu_buf_p[0]; - vout->ipu_buf_n[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_c = vout->ipu_buf[0]; last_index_n = vout->ipu_buf_n[0]; vout->frame_count = 2; } - /* Init Display Channel */ -#ifdef CONFIG_FB_MXC_ASYNC_PANEL - if (vout->cur_disp_output < DISP3) { - vout->work_irq = IPU_IRQ_PP_IN_EOF; - ipu_clear_irq(vout->work_irq); - ipu_request_irq(vout->work_irq, - mxc_v4l2out_work_irq_handler, - 0, vout->video_dev->name, vout); - mxcfb_set_refresh_mode(fbi, MXCFB_REFRESH_OFF, 0); - fbi = NULL; - if (ipu_can_rotate_in_place(vout->rotate)) { - dev_dbg(dev, "Using PP direct to ADC channel\n"); - use_direct_adc = true; - vout->display_ch = MEM_PP_ADC; - vout->post_proc_ch = MEM_PP_ADC; - - memset(¶ms, 0, sizeof(params)); - params.mem_pp_adc.in_width = vout->v2f.fmt.pix.width; - params.mem_pp_adc.in_height = vout->v2f.fmt.pix.height; - params.mem_pp_adc.in_pixel_fmt = - vout->v2f.fmt.pix.pixelformat; - params.mem_pp_adc.out_width = out_width; - params.mem_pp_adc.out_height = out_height; - params.mem_pp_adc.out_pixel_fmt = SDC_FG_FB_FORMAT; -#ifdef CONFIG_FB_MXC_EPSON_PANEL - params.mem_pp_adc.out_left = - 2 + vout->crop_current.left; -#else - params.mem_pp_adc.out_left = - 12 + vout->crop_current.left; -#endif - params.mem_pp_adc.out_top = vout->crop_current.top; - if (ipu_init_channel(vout->post_proc_ch, ¶ms) != 0) { - dev_err(dev, "Error initializing PP chan\n"); - return -EINVAL; - } - if (ipu_init_channel_buffer(vout->post_proc_ch, - IPU_INPUT_BUFFER, - params.mem_pp_adc. - in_pixel_fmt, - params.mem_pp_adc.in_width, - params.mem_pp_adc.in_height, - vout->v2f.fmt.pix. - bytesperline / - bytes_per_pixel(params. - mem_pp_adc. - in_pixel_fmt), - vout->rotate, - vout->v4l2_bufs[vout->ipu_buf[0]].m.offset, - vout->v4l2_bufs[vout->ipu_buf[1]].m.offset, - vout->offset.u_offset, - vout->offset.v_offset) != - 0) { - dev_err(dev, "Error initializing PP in buf\n"); - return -EINVAL; - } - - if (ipu_init_channel_buffer(vout->post_proc_ch, - IPU_OUTPUT_BUFFER, - params.mem_pp_adc. - out_pixel_fmt, out_width, - out_height, out_width, - vout->rotate, 0, 0, 0, - 0) != 0) { - dev_err(dev, - "Error initializing PP output buffer\n"); - return -EINVAL; - } - - } else { - dev_dbg(dev, "Using ADC SYS2 channel\n"); - vout->display_ch = ADC_SYS2; - vout->post_proc_ch = MEM_PP_MEM; - - if (vout->display_bufs[0]) { - mxc_free_buffers(vout->display_bufs, - vout->display_bufs_vaddr, - 2, vout->display_buf_size); - } - - vout->display_buf_size = vout->crop_current.width * - vout->crop_current.height * - fmt_to_bpp(SDC_FG_FB_FORMAT) / 8; - mxc_allocate_buffers(vout->display_bufs, - vout->display_bufs_vaddr, - 2, vout->display_buf_size); - - memset(¶ms, 0, sizeof(params)); - params.adc_sys2.disp = vout->cur_disp_output; - params.adc_sys2.ch_mode = WriteTemplateNonSeq; -#ifdef CONFIG_FB_MXC_EPSON_PANEL - params.adc_sys2.out_left = 2 + vout->crop_current.left; -#else - params.adc_sys2.out_left = 12 + vout->crop_current.left; -#endif - params.adc_sys2.out_top = vout->crop_current.top; - if (ipu_init_channel(ADC_SYS2, ¶ms) < 0) - return -EINVAL; - - if (ipu_init_channel_buffer(vout->display_ch, - IPU_INPUT_BUFFER, - SDC_FG_FB_FORMAT, - out_width, out_height, - out_width, IPU_ROTATE_NONE, - vout->display_bufs[0], - vout->display_bufs[1], 0, - 0) != 0) { - dev_err(dev, - "Error initializing SDC FG buffer\n"); - return -EINVAL; - } - } - } else -#endif - { /* Use SDC */ - unsigned int ipu_ch = CHAN_NONE; - - dev_dbg(dev, "Using SDC channel\n"); - - if (INTERLACED_CONTENT(vout)) - vout->work_irq = IPU_IRQ_PRP_VF_OUT_EOF; - else - vout->work_irq = IPU_IRQ_PP_IN_EOF; - - /* - * 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) { - fbvar.bits_per_pixel = 16; + /* + * 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) { + fbvar.bits_per_pixel = 16; + if (vout->cur_disp_output == 3) { + /* Only set YUV for the first display. The second display can + * only work in RGB */ if (format_is_yuv(vout->v2f.fmt.pix.pixelformat)) 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; + fbvar.xres = out_width; + fbvar.yres = out_height; + vout->xres = fbvar.xres; + vout->yres = fbvar.yres; } - 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->ic_bypass = 1; - } else { - vout->ic_bypass = 0; - } + 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))) - vout->ic_bypass = 0; + /* IPUv1 needs IC to do CSC */ + if (format_is_yuv(vout->v2f.fmt.pix.pixelformat) != + format_is_yuv(bpp_to_fmt(fbi))) + vout->ic_bypass = 0; #endif - /* We are using IC to do input cropping */ - if (vout->queue_buf_paddr[vout->ipu_buf[0]] != - vout->v4l2_bufs[vout->ipu_buf[0]].m.offset || - vout->queue_buf_paddr[vout->ipu_buf[1]] != - vout->v4l2_bufs[vout->ipu_buf[1]].m.offset) - vout->ic_bypass = 0; - - 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 (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; - } + if (ipu_ch == CHAN_NONE) { + dev_err(dev, "Can not get display ipu channel\n"); + return -EINVAL; + } - vout->display_ch = ipu_ch; + vout->display_ch = ipu_ch; - if (vout->ic_bypass) { - pr_debug("Bypassing IC\n"); - vout->work_irq = -1; - switch (vout->v2f.fmt.pix.pixelformat) { + if (vout->ic_bypass) { + pr_debug("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: @@ -1353,87 +1353,80 @@ static int mxc_v4l2out_streamon(vout_data * vout) default: fbvar.bits_per_pixel = 8* bytes_per_pixel(vout->v2f.fmt.pix.pixelformat); - } - fbvar.nonstd = vout->v2f.fmt.pix.pixelformat; } + fbvar.nonstd = vout->v2f.fmt.pix.pixelformat; + } - fbvar.activate |= FB_ACTIVATE_FORCE; - fb_set_var(fbi, &fbvar); + /* Init display channel through fb API */ + fbvar.activate |= FB_ACTIVATE_FORCE; + fb_set_var(fbi, &fbvar); - 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); - } + 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; - 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; + 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) || + *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; - } + 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 - vout->post_proc_ch = MEM_PP_MEM; - - if (!vout->ic_bypass) { - ipu_clear_irq(vout->work_irq); - ipu_request_irq(vout->work_irq, - mxc_v4l2out_work_irq_handler, - 0, vout->video_dev->name, vout); } - } + } 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 PP */ - if (use_direct_adc == false && !vout->ic_bypass) { + /* 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; @@ -1441,7 +1434,11 @@ static int mxc_v4l2out_streamon(vout_data * vout) vout->display_input_ch = vout->post_proc_ch; memset(¶ms, 0, sizeof(params)); if (INTERLACED_CONTENT(vout)) { - rc = init_VDI(params, vout, dev, fbi, out_width, out_height); + 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); } @@ -1449,52 +1446,79 @@ static int mxc_v4l2out_streamon(vout_data * vout) 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->work_irq); + ipu_request_irq(vout->work_irq, + mxc_v4l2out_work_irq_handler, + 0, vout->video_dev->name, vout); + } + vout->state = STATE_STREAM_PAUSED; - if (use_direct_adc == false) { - if (fbi) { - acquire_console_sem(); - fb_blank(fbi, FB_BLANK_UNBLANK); - release_console_sem(); - } else { - ipu_enable_channel(vout->display_ch); - } - if (!vout->ic_bypass) { + /* Enable display and IC channels */ + if (fbi) { + acquire_console_sem(); + fb_blank(fbi, FB_BLANK_UNBLANK); + release_console_sem(); + } else { + ipu_enable_channel(vout->display_ch); + } + if (!vout->ic_bypass) { #ifndef CONFIG_MXC_IPU_V1 - ipu_enable_channel(vout->post_proc_ch); + ipu_enable_channel(vout->post_proc_ch); #endif - if (LOAD_3FIELDS(vout)) { - ipu_enable_channel(MEM_VDI_PRP_VF_MEM_P); - ipu_enable_channel(MEM_VDI_PRP_VF_MEM_N); - ipu_select_multi_vdi_buffer(0); - } else if (INTERLACED_CONTENT(vout)) { - ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 0); - } else { - ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 0); - if (!vout->pp_split) - ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 1); - } - ipu_select_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER, 0); - ipu_select_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER, 1); + if (LOAD_3FIELDS(vout)) { + ipu_enable_channel(MEM_VDI_PRP_VF_MEM_P); + ipu_enable_channel(MEM_VDI_PRP_VF_MEM_N); + ipu_select_multi_vdi_buffer(0); + } else + ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 0); + ipu_select_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER, 0); + ipu_select_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER, 1); #ifdef CONFIG_MXC_IPU_V1 - ipu_enable_channel(vout->post_proc_ch); + ipu_enable_channel(vout->post_proc_ch); #endif - } else { - ipu_update_channel_buffer(vout->display_ch, + } else { + ipu_update_channel_buffer(vout->display_ch, IPU_INPUT_BUFFER, 0, vout->v4l2_bufs[vout->ipu_buf[0]].m.offset); - ipu_update_channel_buffer(vout->display_ch, - IPU_INPUT_BUFFER, - 1, vout->v4l2_bufs[vout->ipu_buf[1]].m.offset); - ipu_select_buffer(vout->display_ch, IPU_INPUT_BUFFER, 0); - ipu_select_buffer(vout->display_ch, IPU_INPUT_BUFFER, 1); - queue_work(vout->v4l_wq, &vout->timer_work); - } - } else { - ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 0); - ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 1); - ipu_enable_channel(vout->post_proc_ch); + if (vout->offset.u_offset || vout->offset.v_offset) + /* only update u/v offset */ + ipu_update_channel_offset(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->display_ch, IPU_INPUT_BUFFER, 0); + queue_work(vout->v4l_wq, &vout->icbypass_work); } + vout->start_jiffies = jiffies; msleep(1); @@ -1529,7 +1553,8 @@ static int mxc_v4l2out_streamoff(vout_data * vout) if (!vout->ic_bypass) ipu_free_irq(vout->work_irq, vout); - cancel_work_sync(&vout->timer_work); + if (vout->ic_bypass) + cancel_work_sync(&vout->icbypass_work); spin_lock_irqsave(&g_lock, lockflag); @@ -1556,9 +1581,21 @@ static int mxc_v4l2out_streamoff(vout_data * vout) } } - if (vout->post_proc_ch == MEM_PP_MEM || + if (vout->ic_bypass) { + fbi->var.activate |= FB_ACTIVATE_FORCE; + fb_set_var(fbi, &fbi->var); + + if (vout->display_ch == MEM_FG_SYNC) { + acquire_console_sem(); + fb_blank(fbi, FB_BLANK_POWERDOWN); + release_console_sem(); + } + + 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 or ADC with Rotation */ + /* SDC with Rotation */ if (!ipu_can_rotate_in_place(vout->rotate)) { ipu_unlink_channels(MEM_PP_MEM, MEM_ROT_PP_MEM); ipu_disable_channel(MEM_ROT_PP_MEM, true); @@ -1571,28 +1608,23 @@ static int mxc_v4l2out_streamoff(vout_data * vout) } ipu_disable_channel(MEM_PP_MEM, true); - if (vout->display_ch == ADC_SYS2 || - vout->display_ch == MEM_FG_SYNC) { - ipu_disable_channel(vout->display_ch, true); - ipu_uninit_channel(vout->display_ch); - } else { - fbi->var.activate |= FB_ACTIVATE_FORCE; - fb_set_var(fbi, &fbi->var); + fbi->var.activate |= FB_ACTIVATE_FORCE; + fb_set_var(fbi, &fbi->var); - if (vout->display_ch == MEM_FG_SYNC) { - acquire_console_sem(); - fb_blank(fbi, FB_BLANK_POWERDOWN); - release_console_sem(); - } - - vout->display_bufs[0] = 0; - vout->display_bufs[1] = 0; + if (vout->display_ch == MEM_FG_SYNC) { + acquire_console_sem(); + fb_blank(fbi, FB_BLANK_POWERDOWN); + release_console_sem(); } + vout->display_bufs[0] = 0; + vout->display_bufs[1] = 0; + ipu_uninit_channel(MEM_PP_MEM); if (!ipu_can_rotate_in_place(vout->rotate)) ipu_uninit_channel(MEM_ROT_PP_MEM); - } else if (INTERLACED_CONTENT(vout) && (vout->post_proc_ch == MEM_VDI_PRP_VF_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(MEM_VDI_PRP_VF_MEM, MEM_ROT_VF_MEM); @@ -1606,32 +1638,32 @@ static int mxc_v4l2out_streamoff(vout_data * vout) } ipu_disable_channel(MEM_VDI_PRP_VF_MEM, true); + if (LOAD_3FIELDS(vout)) { + ipu_disable_channel(MEM_VDI_PRP_VF_MEM_P, true); + ipu_disable_channel(MEM_VDI_PRP_VF_MEM_N, true); + } - if (vout->display_ch == ADC_SYS2 || - vout->display_ch == MEM_FG_SYNC) { - ipu_disable_channel(vout->display_ch, true); - ipu_uninit_channel(vout->display_ch); - } else { - fbi->var.activate |= FB_ACTIVATE_FORCE; - fb_set_var(fbi, &fbi->var); + fbi->var.activate |= FB_ACTIVATE_FORCE; + fb_set_var(fbi, &fbi->var); - if (vout->display_ch == MEM_FG_SYNC) { - acquire_console_sem(); - fb_blank(fbi, FB_BLANK_POWERDOWN); - release_console_sem(); - } - - vout->display_bufs[0] = 0; - vout->display_bufs[1] = 0; + if (vout->display_ch == MEM_FG_SYNC) { + acquire_console_sem(); + fb_blank(fbi, FB_BLANK_POWERDOWN); + release_console_sem(); } + vout->display_bufs[0] = 0; + vout->display_bufs[1] = 0; + ipu_uninit_channel(MEM_VDI_PRP_VF_MEM); + if (LOAD_3FIELDS(vout)) { + ipu_uninit_channel(MEM_VDI_PRP_VF_MEM_P); + ipu_uninit_channel(MEM_VDI_PRP_VF_MEM_N); + } if (!ipu_can_rotate_in_place(vout->rotate)) ipu_uninit_channel(MEM_ROT_VF_MEM); - } else { /* ADC Direct */ - ipu_disable_channel(MEM_PP_ADC, true); - ipu_uninit_channel(MEM_PP_ADC); } + 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++) { @@ -1640,23 +1672,9 @@ static int mxc_v4l2out_streamoff(vout_data * vout) vout->v4l2_bufs[i].timestamp.tv_usec = 0; } + vout->post_proc_ch = CHAN_NONE; vout->state = STATE_STREAM_OFF; -#ifdef CONFIG_FB_MXC_ASYNC_PANEL - if (vout->cur_disp_output < DISP3) { - if (vout->display_bufs[0] != 0) { - mxc_free_buffers(vout->display_bufs, - vout->display_bufs_vaddr, 2, - vout->display_buf_size); - } - - mxcfb_set_refresh_mode(registered_fb - [vout-> - output_fb_num[vout->cur_disp_output]], - MXCFB_REFRESH_PARTIAL, 0); - } -#endif - return retval; } @@ -1751,7 +1769,6 @@ static int mxc_v4l2out_s_fmt(vout_data * vout, struct v4l2_format *f) dev_err(&vout->video_dev->dev, "De-interlacing not supported in this device!\n"); vout->field_fmt = V4L2_FIELD_NONE; - break; case V4L2_FIELD_INTERLACED_BT: dev_err(&vout->video_dev->dev, "V4L2_FIELD_INTERLACED_BT field format not supported yet!\n"); @@ -1898,7 +1915,7 @@ static int mxc_v4l2out_open(struct file *file) goto oops; } - INIT_WORK(&vout->timer_work, timer_work_func); + INIT_WORK(&vout->icbypass_work, icbypass_work_func); } file->private_data = dev; @@ -2284,12 +2301,8 @@ mxc_v4l2out_do_ioctl(struct file *file, break; } - if (output->index < 3) { - *output = mxc_outputs[MXC_V4L2_OUT_2_ADC]; - output->name[4] = '0' + output->index; - } else { + if (output->index >= 3) *output = mxc_outputs[MXC_V4L2_OUT_2_SDC]; - } break; } case VIDIOC_G_OUTPUT: -- cgit v1.2.3