diff options
author | guoyin.chen <guoyin.chen@freescale.com> | 2015-05-08 17:24:09 +0800 |
---|---|---|
committer | guoyin.chen <guoyin.chen@freescale.com> | 2015-05-08 17:24:09 +0800 |
commit | 29029534d9824bf6241b9f14989254c02757f677 (patch) | |
tree | e1e81c28bf0be9acd1024611e57025526c7c6054 /drivers | |
parent | f653937894e89a9f74d281bd1b6874860d44577d (diff) |
MLK-10475-6 video: mxc_ipuv3_fb: Add PRE/PRG support
This patch adds PRE/PRG support for IPUv3 fb.
Signed-off-by: Liu Ying <Ying.Liu@freescale.com>
Conflicts:
drivers/video/mxc/mxc_ipuv3_fb.c
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/video/mxc/mxc_ipuv3_fb.c | 1177 |
1 files changed, 1085 insertions, 92 deletions
diff --git a/drivers/video/mxc/mxc_ipuv3_fb.c b/drivers/video/mxc/mxc_ipuv3_fb.c index 23a43669729c..e8207be24fac 100644 --- a/drivers/video/mxc/mxc_ipuv3_fb.c +++ b/drivers/video/mxc/mxc_ipuv3_fb.c @@ -39,6 +39,8 @@ #include <linux/ioport.h> #include <linux/ipu.h> #include <linux/ipu-v3.h> +#include <linux/ipu-v3-pre.h> +#include <linux/ipu-v3-prg.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/mxcfb.h> @@ -47,6 +49,7 @@ #include <linux/sched.h> #include <linux/slab.h> #include <linux/string.h> +#include <linux/time.h> #include <linux/uaccess.h> #include "mxc_dispdrv.h" @@ -68,12 +71,19 @@ struct mxcfb_info { ipu_channel_t ipu_ch; int ipu_id; int ipu_di; + int pre_num; u32 ipu_di_pix_fmt; bool ipu_int_clk; bool overlay; bool alpha_chan_en; bool late_init; bool first_set_par; + bool resolve; + bool prefetch; + bool on_the_fly; + uint32_t final_pfmt; + unsigned long gpu_sec_buf_off; + dma_addr_t store_addr; dma_addr_t alpha_phy_addr0; dma_addr_t alpha_phy_addr1; void *alpha_virt_addr0; @@ -99,6 +109,9 @@ struct mxcfb_info { struct fb_var_screeninfo cur_var; ktime_t vsync_nf_timestamp; + uint32_t cur_ipu_pfmt; + uint32_t cur_fb_pfmt; + bool cur_prefetch; }; struct mxcfb_pfmt { @@ -110,14 +123,72 @@ struct mxcfb_pfmt { struct fb_bitfield transp; }; +struct mxcfb_tile_block { + u32 fb_pix_fmt; + int bw; /* in pixel */ + int bh; /* in pixel */ +}; + +#define NA (~0x0UL) static const struct mxcfb_pfmt mxcfb_pfmts[] = { /* pixel bpp red green blue transp */ - {IPU_PIX_FMT_RGB565, 16, {11, 5, 0}, { 5, 6, 0}, { 0, 5, 0}, { 0, 0, 0} }, + {IPU_PIX_FMT_RGB565, 16, {11, 5, 0}, { 5, 6, 0}, { 0, 5, 0}, { 0, 0, 0} }, + {IPU_PIX_FMT_BGRA4444, 16, { 8, 4, 0}, { 4, 4, 0}, { 0, 4, 0}, { 12, 4, 0} }, + {IPU_PIX_FMT_BGRA5551, 16, {10, 5, 0}, { 5, 5, 0}, { 0, 5, 0}, { 15, 1, 0} }, {IPU_PIX_FMT_RGB24, 24, { 0, 8, 0}, { 8, 8, 0}, {16, 8, 0}, { 0, 0, 0} }, {IPU_PIX_FMT_BGR24, 24, {16, 8, 0}, { 8, 8, 0}, { 0, 8, 0}, { 0, 0, 0} }, {IPU_PIX_FMT_RGB32, 32, { 0, 8, 0}, { 8, 8, 0}, {16, 8, 0}, {24, 8, 0} }, {IPU_PIX_FMT_BGR32, 32, {16, 8, 0}, { 8, 8, 0}, { 0, 8, 0}, {24, 8, 0} }, {IPU_PIX_FMT_ABGR32, 32, {24, 8, 0}, {16, 8, 0}, { 8, 8, 0}, { 0, 8, 0} }, + /* pixel bpp red green blue transp */ + {IPU_PIX_FMT_YUV420P2, 12, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} }, + {IPU_PIX_FMT_YUV420P, 12, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} }, + {IPU_PIX_FMT_YVU420P, 12, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} }, + {IPU_PIX_FMT_NV12, 12, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} }, + {PRE_PIX_FMT_NV21, 12, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} }, + {IPU_PIX_FMT_NV16, 16, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} }, + {PRE_PIX_FMT_NV61, 16, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} }, + {IPU_PIX_FMT_YUV422P, 16, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} }, + {IPU_PIX_FMT_YVU422P, 16, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} }, + {IPU_PIX_FMT_UYVY, 16, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} }, + {IPU_PIX_FMT_YUYV, 16, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} }, + {IPU_PIX_FMT_YUV444, 24, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} }, + {IPU_PIX_FMT_YUV444P, 24, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} }, + {IPU_PIX_FMT_AYUV, 32, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} }, + /* pixel bpp red green blue transp */ + {IPU_PIX_FMT_GPU32_SB_ST, 32, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} }, + {IPU_PIX_FMT_GPU32_SB_SRT, 32, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} }, + {IPU_PIX_FMT_GPU32_ST, 32, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} }, + {IPU_PIX_FMT_GPU32_SRT, 32, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} }, + {IPU_PIX_FMT_GPU16_SB_ST, 16, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} }, + {IPU_PIX_FMT_GPU16_SB_SRT, 16, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} }, + {IPU_PIX_FMT_GPU16_ST, 16, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} }, + {IPU_PIX_FMT_GPU16_SRT, 16, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA}, {NA, NA, NA} }, +}; + +/* Tile fb alignment */ +static const struct mxcfb_tile_block tas[] = { + {IPU_PIX_FMT_GPU32_SB_ST, 16, 8}, + {IPU_PIX_FMT_GPU32_SB_SRT, 64, 128}, + {IPU_PIX_FMT_GPU32_ST, 16, 4}, + {IPU_PIX_FMT_GPU32_SRT, 64, 64}, + {IPU_PIX_FMT_GPU16_SB_ST, 16, 8}, + {IPU_PIX_FMT_GPU16_SB_SRT, 64, 128}, + {IPU_PIX_FMT_GPU16_ST, 16, 4}, + {IPU_PIX_FMT_GPU16_SRT, 64, 64}, +}; + +/* The block can be resolved */ +static const struct mxcfb_tile_block trs[] = { + /* pixel w h */ + {IPU_PIX_FMT_GPU32_SB_ST, 16, 4}, + {IPU_PIX_FMT_GPU32_SB_SRT, 16, 4}, + {IPU_PIX_FMT_GPU32_ST, 16, 4}, + {IPU_PIX_FMT_GPU32_SRT, 16, 4}, + {IPU_PIX_FMT_GPU16_SB_ST, 16, 4}, + {IPU_PIX_FMT_GPU16_SB_SRT, 16, 4}, + {IPU_PIX_FMT_GPU16_ST, 16, 4}, + {IPU_PIX_FMT_GPU16_SRT, 16, 4}, }; struct mxcfb_alloc_list { @@ -184,6 +255,9 @@ static int bpp_to_var(int bpp, struct fb_var_screeninfo *var) { uint32_t pixfmt = 0; + if (var->nonstd) + return -1; + pixfmt = bpp_to_pixfmt(bpp); if (pixfmt) return pixfmt_to_var(pixfmt, var); @@ -195,6 +269,17 @@ static int check_var_pixfmt(struct fb_var_screeninfo *var) { int i, ret = -1; + if (var->nonstd) { + for (i = 0; i < ARRAY_SIZE(mxcfb_pfmts); i++) { + if (mxcfb_pfmts[i].fb_pix_fmt == var->nonstd) { + var->bits_per_pixel = mxcfb_pfmts[i].bpp; + ret = 0; + break; + } + } + return ret; + } + for (i = 0; i < ARRAY_SIZE(mxcfb_pfmts); i++) { if (bitfield_is_equal(var->red, mxcfb_pfmts[i].red) && bitfield_is_equal(var->green, mxcfb_pfmts[i].green) && @@ -208,14 +293,73 @@ static int check_var_pixfmt(struct fb_var_screeninfo *var) return ret; } -static uint32_t fbi_to_pixfmt(struct fb_info *fbi) +static uint32_t fb_to_store_pixfmt(uint32_t fb_pixfmt) { + switch (fb_pixfmt) { + case IPU_PIX_FMT_GPU32_SB_ST: + case IPU_PIX_FMT_GPU32_SB_SRT: + case IPU_PIX_FMT_GPU32_ST: + case IPU_PIX_FMT_GPU32_SRT: + case IPU_PIX_FMT_RGB32: + case IPU_PIX_FMT_BGR32: + case IPU_PIX_FMT_ABGR32: + return IPU_PIX_FMT_BGR32; + case IPU_PIX_FMT_RGB24: + return IPU_PIX_FMT_RGB24; + case IPU_PIX_FMT_BGR24: + return IPU_PIX_FMT_BGR24; + case IPU_PIX_FMT_GPU16_SB_ST: + case IPU_PIX_FMT_GPU16_SB_SRT: + case IPU_PIX_FMT_GPU16_ST: + case IPU_PIX_FMT_GPU16_SRT: + case IPU_PIX_FMT_RGB565: + return IPU_PIX_FMT_RGB565; + case IPU_PIX_FMT_BGRA4444: + return IPU_PIX_FMT_BGRA4444; + case IPU_PIX_FMT_BGRA5551: + return IPU_PIX_FMT_BGRA5551; + case IPU_PIX_FMT_YUV422P: + case IPU_PIX_FMT_YVU422P: + case IPU_PIX_FMT_YUV420P2: + case IPU_PIX_FMT_YVU420P: + case IPU_PIX_FMT_NV12: + case PRE_PIX_FMT_NV21: + case IPU_PIX_FMT_NV16: + case PRE_PIX_FMT_NV61: + case IPU_PIX_FMT_YUV420P: + case IPU_PIX_FMT_UYVY: + return IPU_PIX_FMT_UYVY; + case IPU_PIX_FMT_YUYV: + return IPU_PIX_FMT_YUYV; + case IPU_PIX_FMT_YUV444: + return IPU_PIX_FMT_YUV444; + case IPU_PIX_FMT_YUV444P: + case IPU_PIX_FMT_AYUV: + return IPU_PIX_FMT_AYUV; + default: + return 0; + } +} + +static uint32_t fbi_to_pixfmt(struct fb_info *fbi, bool original_fb) +{ + struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par; int i; uint32_t pixfmt = 0; - if (fbi->var.nonstd) - return fbi->var.nonstd; + if (fbi->var.nonstd) { + if (mxc_fbi->prefetch && !original_fb) { + if (ipu_pixel_format_is_gpu_tile(fbi->var.nonstd) && + bytes_per_pixel(fbi->var.nonstd) == 2) + goto next; + + return fb_to_store_pixfmt(fbi->var.nonstd); + } else { + return fbi->var.nonstd; + } + } +next: for (i = 0; i < ARRAY_SIZE(mxcfb_pfmts); i++) { if (bitfield_is_equal(fbi->var.red, mxcfb_pfmts[i].red) && bitfield_is_equal(fbi->var.green, mxcfb_pfmts[i].green) && @@ -226,12 +370,43 @@ static uint32_t fbi_to_pixfmt(struct fb_info *fbi) } } + if (mxc_fbi->prefetch && !original_fb) + pixfmt = fb_to_store_pixfmt(pixfmt); + if (pixfmt == 0) dev_err(fbi->device, "cannot get pixel format\n"); return pixfmt; } +static void fmt_to_tile_alignment(uint32_t fmt, int *bw, int *bh) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(tas); i++) { + if (tas[i].fb_pix_fmt == fmt) { + *bw = tas[i].bw; + *bh = tas[i].bh; + } + } + + BUG_ON(!(*bw) || !(*bh)); +} + +static void fmt_to_tile_block(uint32_t fmt, int *bw, int *bh) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(trs); i++) { + if (trs[i].fb_pix_fmt == fmt) { + *bw = trs[i].bw; + *bh = trs[i].bh; + } + } + + BUG_ON(!(*bw) || !(*bh)); +} + static struct fb_info *found_registered_fb(ipu_channel_t ipu_ch, int ipu_id) { int i; @@ -291,13 +466,17 @@ static int _setup_disp_channel1(struct fb_info *fbi) if (fbi->var.vmode & FB_VMODE_INTERLACED) params.mem_dc_sync.interlaced = true; params.mem_dc_sync.out_pixel_fmt = mxc_fbi->ipu_di_pix_fmt; - params.mem_dc_sync.in_pixel_fmt = fbi_to_pixfmt(fbi); + params.mem_dc_sync.in_pixel_fmt = mxc_fbi->on_the_fly ? + mxc_fbi->final_pfmt : + fbi_to_pixfmt(fbi, false); } else { params.mem_dp_bg_sync.di = mxc_fbi->ipu_di; if (fbi->var.vmode & FB_VMODE_INTERLACED) params.mem_dp_bg_sync.interlaced = true; params.mem_dp_bg_sync.out_pixel_fmt = mxc_fbi->ipu_di_pix_fmt; - params.mem_dp_bg_sync.in_pixel_fmt = fbi_to_pixfmt(fbi); + params.mem_dp_bg_sync.in_pixel_fmt = mxc_fbi->on_the_fly ? + mxc_fbi->final_pfmt : + fbi_to_pixfmt(fbi, false); if (mxc_fbi->alpha_chan_en) params.mem_dp_bg_sync.alpha_chan_en = true; } @@ -310,14 +489,20 @@ static int _setup_disp_channel2(struct fb_info *fbi) { int retval = 0; struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par; - int fb_stride; - unsigned long base; + int fb_stride, ipu_stride, bw = 0, bh = 0; + unsigned long base, ipu_base; unsigned int fr_xoff, fr_yoff, fr_w, fr_h; + unsigned int prg_width; + struct ipu_pre_context pre; + bool post_pre_disable = false; - switch (fbi_to_pixfmt(fbi)) { + switch (fbi_to_pixfmt(fbi, true)) { case IPU_PIX_FMT_YUV420P2: case IPU_PIX_FMT_YVU420P: case IPU_PIX_FMT_NV12: + case PRE_PIX_FMT_NV21: + case IPU_PIX_FMT_NV16: + case PRE_PIX_FMT_NV61: case IPU_PIX_FMT_YUV422P: case IPU_PIX_FMT_YVU422P: case IPU_PIX_FMT_YUV420P: @@ -337,14 +522,26 @@ static int _setup_disp_channel2(struct fb_info *fbi) fr_h = fbi->var.yres; base += fbi->fix.line_length * fbi->var.yres * (fbi->var.yoffset / fbi->var.yres); + if (ipu_pixel_format_is_split_gpu_tile(fbi->var.nonstd)) + base += (mxc_fbi->gpu_sec_buf_off - + fbi->fix.line_length * fbi->var.yres / 2) * + (fbi->var.yoffset / fbi->var.yres); } else { dev_dbg(fbi->device, "Y wrap enabled\n"); fr_yoff = fbi->var.yoffset; fr_h = fbi->var.yres_virtual; } - base += fr_yoff * fb_stride + fr_xoff; - mxc_fbi->cur_ipu_buf = 2; + /* pixel block alignment for resolving cases */ + if (mxc_fbi->resolve) { + fmt_to_tile_block(fbi->var.nonstd, &bw, &bh); + } else { + base += fr_yoff * fb_stride + fr_xoff * + bytes_per_pixel(fbi_to_pixfmt(fbi, true)); + } + + if (!mxc_fbi->on_the_fly) + mxc_fbi->cur_ipu_buf = 2; init_completion(&mxc_fbi->flip_complete); /* * We don't need to wait for vsync at the first time @@ -360,33 +557,353 @@ static int _setup_disp_channel2(struct fb_info *fbi) complete(&mxc_fbi->alpha_flip_complete); } - retval = ipu_init_channel_buffer(mxc_fbi->ipu, - mxc_fbi->ipu_ch, IPU_INPUT_BUFFER, - fbi_to_pixfmt(fbi), - fbi->var.xres, fbi->var.yres, - fb_stride, - fbi->var.rotate, - base, - base, - fbi->var.accel_flags & - FB_ACCEL_DOUBLE_FLAG ? 0 : base, - 0, 0); - if (retval) { - dev_err(fbi->device, - "ipu_init_channel_buffer error %d\n", retval); - return retval; + if (mxc_fbi->prefetch) { + struct ipu_prg_config prg; + struct fb_var_screeninfo from_var, to_var; + + if (mxc_fbi->pre_num < 0) { + mxc_fbi->pre_num = ipu_pre_alloc(mxc_fbi->ipu_id, + mxc_fbi->ipu_ch); + if (mxc_fbi->pre_num < 0) { + dev_err(fbi->device, + "failed to alloc PRE\n"); + return mxc_fbi->pre_num; + } + } + pre.repeat = true; + pre.vflip = fbi->var.rotate ? true : false; + pre.handshake_en = true; + pre.hsk_abort_en = true; + pre.hsk_line_num = 0; + pre.sdw_update = false; + pre.cur_buf = base; + pre.next_buf = pre.cur_buf; + if (fbi->var.vmode & FB_VMODE_INTERLACED) { + pre.interlaced = 2; + if (mxc_fbi->resolve) { + pre.field_inverse = fbi->var.rotate; + pre.interlace_offset = 0; + } else { + pre.field_inverse = 0; + if (fbi->var.rotate) { + pre.interlace_offset = ~(fbi->var.xres_virtual * + bytes_per_pixel(fbi_to_pixfmt(fbi, true))) + 1; + pre.cur_buf += fbi->var.xres_virtual * bytes_per_pixel(fbi_to_pixfmt(fbi, true)); + pre.next_buf = pre.cur_buf; + } else { + pre.interlace_offset = fbi->var.xres_virtual * + bytes_per_pixel(fbi_to_pixfmt(fbi, true)); + } + } + } else { + pre.interlaced = 0; + pre.interlace_offset = 0; + } + pre.prefetch_mode = mxc_fbi->resolve ? 1 : 0; + pre.tile_fmt = mxc_fbi->resolve ? fbi->var.nonstd : 0; + pre.read_burst = mxc_fbi->resolve ? 0x4 : 0x3; + if (fbi_to_pixfmt(fbi, true) == IPU_PIX_FMT_RGB24 || + fbi_to_pixfmt(fbi, true) == IPU_PIX_FMT_BGR24 || + fbi_to_pixfmt(fbi, true) == IPU_PIX_FMT_YUV444) { + if ((fbi->var.xres * 3) % 8 == 0 && + (fbi->var.xres_virtual * 3) % 8 == 0) + pre.prefetch_input_bpp = 64; + else if ((fbi->var.xres * 3) % 4 == 0 && + (fbi->var.xres_virtual * 3) % 4 == 0) + pre.prefetch_input_bpp = 32; + else if ((fbi->var.xres * 3) % 2 == 0 && + (fbi->var.xres_virtual * 3) % 2 == 0) + pre.prefetch_input_bpp = 16; + else + pre.prefetch_input_bpp = 8; + } else { + pre.prefetch_input_bpp = + 8 * bytes_per_pixel(fbi_to_pixfmt(fbi, true)); + } + pre.prefetch_input_pixel_fmt = mxc_fbi->resolve ? + 0x1 : (fbi->var.nonstd ? fbi->var.nonstd : 0); + pre.shift_bypass = (mxc_fbi->on_the_fly && + mxc_fbi->final_pfmt != fbi_to_pixfmt(fbi, false)) ? + false : true; + pixfmt_to_var(fbi_to_pixfmt(fbi, false), &from_var); + pixfmt_to_var(mxc_fbi->final_pfmt, &to_var); + if (mxc_fbi->on_the_fly && + (format_to_colorspace(fbi_to_pixfmt(fbi, true)) == RGB) && + (bytes_per_pixel(fbi_to_pixfmt(fbi, true)) == 4)) { + pre.prefetch_shift_offset = (from_var.red.offset << to_var.red.offset) | + (from_var.green.offset << to_var.green.offset) | + (from_var.blue.offset << to_var.blue.offset) | + (from_var.transp.offset << to_var.transp.offset); + pre.prefetch_shift_width = (to_var.red.length << (to_var.red.offset/2)) | + (to_var.green.length << (to_var.green.offset/2)) | + (to_var.blue.length << (to_var.blue.offset/2)) | + (to_var.transp.length << (to_var.transp.offset/2)); + } else { + pre.prefetch_shift_offset = 0; + pre.prefetch_shift_width = 0; + } + pre.tpr_coor_offset_en = mxc_fbi->resolve ? true : false; + pre.prefetch_output_size.left = mxc_fbi->resolve ? (fr_xoff & ~(bw - 1)) : 0; + pre.prefetch_output_size.top = mxc_fbi->resolve ? (fr_yoff & ~(bh - 1)) : 0; + if (fbi_to_pixfmt(fbi, true) == IPU_PIX_FMT_RGB24 || + fbi_to_pixfmt(fbi, true) == IPU_PIX_FMT_BGR24 || + fbi_to_pixfmt(fbi, true) == IPU_PIX_FMT_YUV444) { + pre.prefetch_output_size.width = (fbi->var.xres * 3) / + (pre.prefetch_input_bpp / 8); + pre.store_output_bpp = pre.prefetch_input_bpp; + } else { + pre.prefetch_output_size.width = fbi->var.xres; + pre.store_output_bpp = 8 * + bytes_per_pixel(fbi_to_pixfmt(fbi, false)); + } + pre.prefetch_output_size.height = fbi->var.yres; + pre.prefetch_input_active_width = pre.prefetch_output_size.width; + if (fbi_to_pixfmt(fbi, true) == IPU_PIX_FMT_RGB24 || + fbi_to_pixfmt(fbi, true) == IPU_PIX_FMT_BGR24 || + fbi_to_pixfmt(fbi, true) == IPU_PIX_FMT_YUV444) + pre.prefetch_input_width = (fbi->var.xres_virtual * 3) / + (pre.prefetch_input_bpp / 8); + else + pre.prefetch_input_width = fbi->var.xres_virtual; + pre.prefetch_input_height = fbi->var.yres; + prg_width = pre.prefetch_output_size.width; + if (!(pre.prefetch_input_active_width % 32)) + pre.block_size = 0; + else if (!(pre.prefetch_input_active_width % 16)) + pre.block_size = 1; + else + pre.block_size = 0; + if (mxc_fbi->resolve) { + int bs = pre.block_size ? 16 : 32; + pre.prefetch_input_active_width += fr_xoff % bw; + if (((fr_xoff % bw) + pre.prefetch_input_active_width) % bs) + pre.prefetch_input_active_width = + ALIGN(pre.prefetch_input_active_width, bs); + pre.prefetch_output_size.width = + pre.prefetch_input_active_width; + prg_width = pre.prefetch_output_size.width; + pre.prefetch_input_height += fr_yoff % bh; + if (((fr_yoff % bh) + fbi->var.yres) % 4) { + pre.prefetch_input_height = + (fbi->var.vmode & FB_VMODE_INTERLACED) ? + ALIGN(pre.prefetch_input_height, 8) : + ALIGN(pre.prefetch_input_height, 4); + } else { + if (fbi->var.vmode & FB_VMODE_INTERLACED) + pre.prefetch_input_height = + ALIGN(pre.prefetch_input_height, 8); + } + pre.prefetch_output_size.height = pre.prefetch_input_height; + } + + /* store output pitch 8-byte aligned */ + while ((pre.store_output_bpp * prg_width) % 64) + prg_width++; + + pre.store_pitch = (pre.store_output_bpp * prg_width) / 8; + + pre.store_en = true; + pre.write_burst = 0x3; + ipu_get_channel_offset(fbi_to_pixfmt(fbi, true), + fbi->var.xres, + fr_h, + fr_w, + 0, 0, + fr_yoff, + fr_xoff, + &pre.sec_buf_off, + &pre.trd_buf_off); + if (mxc_fbi->resolve) + pre.sec_buf_off = mxc_fbi->gpu_sec_buf_off; + + ipu_pre_config(mxc_fbi->pre_num, &pre); + ipu_stride = pre.store_pitch; + ipu_base = pre.store_addr; + mxc_fbi->store_addr = ipu_base; + + retval = ipu_pre_sdw_update(mxc_fbi->pre_num); + if (retval < 0) { + dev_err(fbi->device, + "failed to update PRE shadow reg %d\n", retval); + return retval; + } + + if (!mxc_fbi->on_the_fly || !mxc_fbi->cur_prefetch) { + prg.id = mxc_fbi->ipu_id; + prg.pre_num = mxc_fbi->pre_num; + prg.ipu_ch = mxc_fbi->ipu_ch; + prg.so = (fbi->var.vmode & FB_VMODE_INTERLACED) ? + PRG_SO_INTERLACE : PRG_SO_PROGRESSIVE; + prg.vflip = fbi->var.rotate ? true : false; + prg.block_mode = mxc_fbi->resolve ? PRG_BLOCK_MODE : PRG_SCAN_MODE; + prg.stride = (fbi->var.vmode & FB_VMODE_INTERLACED) ? + ipu_stride * 2 : ipu_stride; + prg.ilo = (fbi->var.vmode & FB_VMODE_INTERLACED) ? + ipu_stride : 0; + prg.height = mxc_fbi->resolve ? + pre.prefetch_output_size.height : fbi->var.yres; + prg.ipu_height = fbi->var.yres; + prg.crop_line = mxc_fbi->resolve ? + ((fbi->var.vmode & FB_VMODE_INTERLACED) ? (fr_yoff % bh) / 2 : fr_yoff % bh) : 0; + prg.baddr = pre.store_addr; + prg.offset = mxc_fbi->resolve ? (prg.crop_line * prg.stride + + (fr_xoff % bw) * + bytes_per_pixel(fbi_to_pixfmt(fbi, false))) : 0; + ipu_base += prg.offset; + if (ipu_base % 8) { + dev_err(fbi->device, + "IPU base address is not 8byte aligned\n"); + return -EINVAL; + } + mxc_fbi->store_addr = ipu_base; + + if (!mxc_fbi->on_the_fly) { + retval = ipu_init_channel_buffer(mxc_fbi->ipu, + mxc_fbi->ipu_ch, IPU_INPUT_BUFFER, + mxc_fbi->on_the_fly ? mxc_fbi->final_pfmt : + fbi_to_pixfmt(fbi, false), + fbi->var.xres, fbi->var.yres, + ipu_stride, + fbi->var.rotate, + ipu_base, + ipu_base, + fbi->var.accel_flags & + FB_ACCEL_DOUBLE_FLAG ? 0 : ipu_base, + 0, 0); + if (retval) { + dev_err(fbi->device, + "ipu_init_channel_buffer error %d\n", retval); + return retval; + } + } + + retval = ipu_prg_config(&prg); + if (retval < 0) { + dev_err(fbi->device, + "failed to configure PRG %d\n", retval); + return retval; + } + + retval = ipu_pre_enable(mxc_fbi->pre_num); + if (retval < 0) { + ipu_prg_disable(mxc_fbi->ipu_id, mxc_fbi->pre_num); + dev_err(fbi->device, + "failed to enable PRE %d\n", retval); + return retval; + } + + retval = ipu_prg_wait_buf_ready(mxc_fbi->ipu_id, + mxc_fbi->pre_num, + pre.hsk_line_num, + pre.prefetch_output_size.height); + if (retval < 0) { + ipu_prg_disable(mxc_fbi->ipu_id, mxc_fbi->pre_num); + ipu_pre_disable(mxc_fbi->pre_num); + ipu_pre_free(&mxc_fbi->pre_num); + dev_err(fbi->device, "failed to wait PRG ready %d\n", retval); + return retval; + } + } + } else { + ipu_stride = fb_stride; + ipu_base = base; + if (mxc_fbi->on_the_fly) + post_pre_disable = true; } - /* update u/v offset */ - ipu_update_channel_offset(mxc_fbi->ipu, mxc_fbi->ipu_ch, - IPU_INPUT_BUFFER, - fbi_to_pixfmt(fbi), - fr_w, - fr_h, - fr_w, - 0, 0, - fr_yoff, - fr_xoff); + if (mxc_fbi->on_the_fly && ((mxc_fbi->cur_prefetch && !mxc_fbi->prefetch) || + (!mxc_fbi->cur_prefetch && mxc_fbi->prefetch))) { + int htotal = fbi->var.xres + fbi->var.right_margin + + fbi->var.hsync_len + fbi->var.left_margin; + int vtotal = fbi->var.yres + fbi->var.lower_margin + + fbi->var.vsync_len + fbi->var.upper_margin; + int timeout = ((htotal * vtotal) / PICOS2KHZ(fbi->var.pixclock)) * 2 ; + int cur_buf = 0; + + BUG_ON(timeout <= 0); + + ++mxc_fbi->cur_ipu_buf; + mxc_fbi->cur_ipu_buf %= 3; + if (ipu_update_channel_buffer(mxc_fbi->ipu, mxc_fbi->ipu_ch, + IPU_INPUT_BUFFER, + mxc_fbi->cur_ipu_buf, + ipu_base) == 0) { + if (!mxc_fbi->prefetch) + ipu_update_channel_offset(mxc_fbi->ipu, + mxc_fbi->ipu_ch, + IPU_INPUT_BUFFER, + fbi_to_pixfmt(fbi, true), + fr_w, + fr_h, + fr_w, + 0, 0, + fr_yoff, + fr_xoff); + ipu_select_buffer(mxc_fbi->ipu, mxc_fbi->ipu_ch, + IPU_INPUT_BUFFER, mxc_fbi->cur_ipu_buf); + for (; timeout > 0; timeout--) { + cur_buf = ipu_get_cur_buffer_idx(mxc_fbi->ipu, + mxc_fbi->ipu_ch, + IPU_INPUT_BUFFER); + if (cur_buf == mxc_fbi->cur_ipu_buf) + break; + + udelay(1000); + } + if (!timeout) + dev_err(fbi->device, "Timeout for switch to buf %d " + "to address=0x%08lX, current buf %d, " + "buf0 ready %d, buf1 ready %d, buf2 ready " + "%d\n", mxc_fbi->cur_ipu_buf, ipu_base, + ipu_get_cur_buffer_idx(mxc_fbi->ipu, + mxc_fbi->ipu_ch, + IPU_INPUT_BUFFER), + ipu_check_buffer_ready(mxc_fbi->ipu, + mxc_fbi->ipu_ch, + IPU_INPUT_BUFFER, 0), + ipu_check_buffer_ready(mxc_fbi->ipu, + mxc_fbi->ipu_ch, + IPU_INPUT_BUFFER, 1), + ipu_check_buffer_ready(mxc_fbi->ipu, + mxc_fbi->ipu_ch, + IPU_INPUT_BUFFER, 2)); + } + } else if (!mxc_fbi->on_the_fly && !mxc_fbi->prefetch) { + retval = ipu_init_channel_buffer(mxc_fbi->ipu, + mxc_fbi->ipu_ch, IPU_INPUT_BUFFER, + mxc_fbi->on_the_fly ? mxc_fbi->final_pfmt : + fbi_to_pixfmt(fbi, false), + fbi->var.xres, fbi->var.yres, + ipu_stride, + fbi->var.rotate, + ipu_base, + ipu_base, + fbi->var.accel_flags & + FB_ACCEL_DOUBLE_FLAG ? 0 : ipu_base, + 0, 0); + if (retval) { + dev_err(fbi->device, + "ipu_init_channel_buffer error %d\n", retval); + return retval; + } + /* update u/v offset */ + if (!mxc_fbi->prefetch) + ipu_update_channel_offset(mxc_fbi->ipu, mxc_fbi->ipu_ch, + IPU_INPUT_BUFFER, + fbi_to_pixfmt(fbi, true), + fr_w, + fr_h, + fr_w, + 0, 0, + fr_yoff, + fr_xoff); + } + + if (post_pre_disable) { + ipu_prg_disable(mxc_fbi->ipu_id, mxc_fbi->pre_num); + ipu_pre_disable(mxc_fbi->pre_num); + ipu_pre_free(&mxc_fbi->pre_num); + } if (mxc_fbi->alpha_chan_en) { retval = ipu_init_channel_buffer(mxc_fbi->ipu, @@ -429,6 +946,137 @@ static bool mxcfb_need_to_set_par(struct fb_info *fbi) sizeof(struct fb_var_screeninfo)); } +static bool mxcfb_can_set_par_on_the_fly(struct fb_info *fbi, + uint32_t *final_pfmt) +{ + struct mxcfb_info *mxc_fbi = fbi->par; + struct fb_var_screeninfo cur_var = mxc_fbi->cur_var; + uint32_t cur_pfmt = mxc_fbi->cur_ipu_pfmt; + uint32_t new_pfmt = fbi_to_pixfmt(fbi, false); + uint32_t new_fb_pfmt = fbi_to_pixfmt(fbi, true); + int cur_bpp, new_bpp, cur_bw, cur_bh, new_bw, new_bh; + unsigned int mem_len; + ipu_color_space_t cur_space, new_space; + + cur_space = format_to_colorspace(cur_pfmt); + new_space = format_to_colorspace(new_pfmt); + + cur_bpp = bytes_per_pixel(cur_pfmt); + new_bpp = bytes_per_pixel(new_pfmt); + + if (mxc_fbi->first_set_par || mxc_fbi->cur_blank != FB_BLANK_UNBLANK) + return false; + + if (!mxc_fbi->prefetch && !mxc_fbi->cur_prefetch) + return false; + + if (!mxc_fbi->prefetch && cur_pfmt != new_pfmt) + return false; + + if (cur_space == RGB && (cur_bpp == 2 || cur_bpp == 3) && + cur_pfmt != new_pfmt) + return false; + + if (!(mxc_fbi->cur_prefetch && mxc_fbi->prefetch) && + ((cur_var.xres_virtual != fbi->var.xres_virtual) || + (cur_var.xres != cur_var.xres_virtual) || + (fbi->var.xres != fbi->var.xres_virtual))) + return false; + + if (cur_space != new_space || + (new_space == RGB && cur_bpp != new_bpp)) + return false; + + if (new_space == YCbCr) + return false; + + mem_len = fbi->var.yres_virtual * fbi->fix.line_length; + if (mxc_fbi->resolve && mxc_fbi->gpu_sec_buf_off) { + if (fbi->var.vmode & FB_VMODE_YWRAP) + mem_len = mxc_fbi->gpu_sec_buf_off + mem_len / 2; + else + mem_len = mxc_fbi->gpu_sec_buf_off * + (fbi->var.yres_virtual / fbi->var.yres) + mem_len / 2; + } + if (mem_len > fbi->fix.smem_len) + return false; + + if (mxc_fbi->resolve && ipu_pixel_format_is_gpu_tile(mxc_fbi->cur_fb_pfmt)) { + fmt_to_tile_block(mxc_fbi->cur_fb_pfmt, &cur_bw, &cur_bh); + fmt_to_tile_block(new_fb_pfmt, &new_bw, &new_bh); + + if (cur_bw != new_bw || cur_bh != new_bh || + cur_var.xoffset % cur_bw != fbi->var.xoffset % new_bw || + cur_var.yoffset % cur_bh != fbi->var.yoffset % new_bh) + return false; + } else if (mxc_fbi->resolve && mxc_fbi->cur_prefetch) { + fmt_to_tile_block(new_fb_pfmt, &new_bw, &new_bh); + if (fbi->var.xoffset % new_bw || fbi->var.yoffset % new_bh || + fbi->var.xres % 16 || fbi->var.yres % + (fbi->var.vmode & FB_VMODE_INTERLACED ? 8 : 4)) + return false; + } else if (mxc_fbi->prefetch && ipu_pixel_format_is_gpu_tile(mxc_fbi->cur_fb_pfmt)) { + fmt_to_tile_block(mxc_fbi->cur_fb_pfmt, &cur_bw, &cur_bh); + if (cur_var.xoffset % cur_bw || cur_var.yoffset % cur_bh || + cur_var.xres % 16 || cur_var.yres % + (cur_var.vmode & FB_VMODE_INTERLACED ? 8 : 4)) + return false; + } + + cur_var.xres_virtual = fbi->var.xres_virtual; + cur_var.yres_virtual = fbi->var.yres_virtual; + cur_var.xoffset = fbi->var.xoffset; + cur_var.yoffset = fbi->var.yoffset; + cur_var.red = fbi->var.red; + cur_var.green = fbi->var.green; + cur_var.blue = fbi->var.blue; + cur_var.transp = fbi->var.transp; + cur_var.nonstd = fbi->var.nonstd; + if (memcmp(&cur_var, &fbi->var, + sizeof(struct fb_var_screeninfo))) + return false; + + *final_pfmt = cur_pfmt; + + return true; +} + +static void mxcfb_check_resolve(struct fb_info *fbi) +{ + struct mxcfb_info *mxc_fbi = fbi->par; + + switch (fbi->var.nonstd) { + case IPU_PIX_FMT_GPU32_ST: + case IPU_PIX_FMT_GPU32_SRT: + case IPU_PIX_FMT_GPU16_ST: + case IPU_PIX_FMT_GPU16_SRT: + mxc_fbi->gpu_sec_buf_off = 0; + case IPU_PIX_FMT_GPU32_SB_ST: + case IPU_PIX_FMT_GPU32_SB_SRT: + case IPU_PIX_FMT_GPU16_SB_ST: + case IPU_PIX_FMT_GPU16_SB_SRT: + mxc_fbi->prefetch = true; + mxc_fbi->resolve = true; + break; + default: + mxc_fbi->resolve = false; + } +} + +static void mxcfb_check_yuv(struct fb_info *fbi) +{ + struct mxcfb_info *mxc_fbi = fbi->par; + + if (fbi->var.vmode & FB_VMODE_INTERLACED) { + if (ipu_pixel_format_is_multiplanar_yuv(fbi_to_pixfmt(fbi, true))) + mxc_fbi->prefetch = false; + } else { + if (fbi->var.nonstd == PRE_PIX_FMT_NV21 || + fbi->var.nonstd == PRE_PIX_FMT_NV61) + mxc_fbi->prefetch = true; + } +} + /* * Set framebuffer parameters and change the operating mode. * @@ -440,13 +1088,13 @@ static int mxcfb_set_par(struct fb_info *fbi) u32 mem_len, alpha_mem_len; ipu_di_signal_cfg_t sig_cfg; struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par; - + uint32_t final_pfmt = 0; int16_t ov_pos_x = 0, ov_pos_y = 0; int ov_pos_ret = 0; struct mxcfb_info *mxc_fbi_fg = NULL; - bool ovfbi_enable = false; + bool ovfbi_enable = false, on_the_fly; - if (ipu_ch_param_bad_alpha_pos(fbi_to_pixfmt(fbi)) && + if (ipu_ch_param_bad_alpha_pos(fbi_to_pixfmt(fbi, true)) && mxc_fbi->alpha_chan_en) { dev_err(fbi->device, "Bad pixel format for " "graphics plane fb\n"); @@ -468,6 +1116,19 @@ static int mxcfb_set_par(struct fb_info *fbi) if (fbi->var.xres == 0 || fbi->var.yres == 0) return 0; + mxcfb_set_fix(fbi); + + mxcfb_check_resolve(fbi); + + mxcfb_check_yuv(fbi); + + on_the_fly = mxcfb_can_set_par_on_the_fly(fbi, &final_pfmt); + mxc_fbi->on_the_fly = on_the_fly; + mxc_fbi->final_pfmt = final_pfmt; + + if (on_the_fly) + dev_dbg(fbi->device, "Reconfiguring framebuffer on the fly\n"); + if (ovfbi_enable) { ov_pos_ret = ipu_disp_get_window_pos( mxc_fbi_fg->ipu, mxc_fbi_fg->ipu_ch, @@ -476,20 +1137,34 @@ static int mxcfb_set_par(struct fb_info *fbi) dev_err(fbi->device, "Get overlay pos failed, dispdrv:%s.\n", mxc_fbi->dispdrv->drv->name); - ipu_clear_irq(mxc_fbi_fg->ipu, mxc_fbi_fg->ipu_ch_irq); - ipu_disable_irq(mxc_fbi_fg->ipu, mxc_fbi_fg->ipu_ch_irq); - ipu_clear_irq(mxc_fbi_fg->ipu, mxc_fbi_fg->ipu_ch_nf_irq); - ipu_disable_irq(mxc_fbi_fg->ipu, mxc_fbi_fg->ipu_ch_nf_irq); - ipu_disable_channel(mxc_fbi_fg->ipu, mxc_fbi_fg->ipu_ch, true); - ipu_uninit_channel(mxc_fbi_fg->ipu, mxc_fbi_fg->ipu_ch); + if (!on_the_fly) { + ipu_clear_irq(mxc_fbi_fg->ipu, mxc_fbi_fg->ipu_ch_irq); + ipu_disable_irq(mxc_fbi_fg->ipu, mxc_fbi_fg->ipu_ch_irq); + ipu_clear_irq(mxc_fbi_fg->ipu, mxc_fbi_fg->ipu_ch_nf_irq); + ipu_disable_irq(mxc_fbi_fg->ipu, mxc_fbi_fg->ipu_ch_nf_irq); + ipu_disable_channel(mxc_fbi_fg->ipu, mxc_fbi_fg->ipu_ch, true); + ipu_uninit_channel(mxc_fbi_fg->ipu, mxc_fbi_fg->ipu_ch); + if (mxc_fbi_fg->cur_prefetch) { + ipu_prg_disable(mxc_fbi_fg->ipu_id, mxc_fbi_fg->pre_num); + ipu_pre_disable(mxc_fbi_fg->pre_num); + ipu_pre_free(&mxc_fbi_fg->pre_num); + } + } } - ipu_clear_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_irq); - ipu_disable_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_irq); - ipu_clear_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_nf_irq); - ipu_disable_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_nf_irq); - ipu_disable_channel(mxc_fbi->ipu, mxc_fbi->ipu_ch, true); - ipu_uninit_channel(mxc_fbi->ipu, mxc_fbi->ipu_ch); + if (!on_the_fly) { + ipu_clear_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_irq); + ipu_disable_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_irq); + ipu_clear_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_nf_irq); + ipu_disable_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_nf_irq); + ipu_disable_channel(mxc_fbi->ipu, mxc_fbi->ipu_ch, true); + ipu_uninit_channel(mxc_fbi->ipu, mxc_fbi->ipu_ch); + if (mxc_fbi->cur_prefetch) { + ipu_prg_disable(mxc_fbi->ipu_id, mxc_fbi->pre_num); + ipu_pre_disable(mxc_fbi->pre_num); + ipu_pre_free(&mxc_fbi->pre_num); + } + } /* * Disable IPU hsp clock if it is enabled for an @@ -498,9 +1173,14 @@ static int mxcfb_set_par(struct fb_info *fbi) if (mxc_fbi->first_set_par && mxc_fbi->late_init) ipu_disable_hsp_clk(mxc_fbi->ipu); - mxcfb_set_fix(fbi); - mem_len = fbi->var.yres_virtual * fbi->fix.line_length; + if (mxc_fbi->resolve && mxc_fbi->gpu_sec_buf_off) { + if (fbi->var.vmode & FB_VMODE_YWRAP) + mem_len = mxc_fbi->gpu_sec_buf_off + mem_len / 2; + else + mem_len = mxc_fbi->gpu_sec_buf_off * + (fbi->var.yres_virtual / fbi->var.yres) + mem_len / 2; + } if (!fbi->fix.smem_start || (mem_len > fbi->fix.smem_len)) { if (fbi->fix.smem_start) mxcfb_unmap_video_memory(fbi); @@ -569,7 +1249,7 @@ static int mxcfb_set_par(struct fb_info *fbi) if (mxc_fbi->next_blank != FB_BLANK_UNBLANK) return retval; - if (mxc_fbi->dispdrv && mxc_fbi->dispdrv->drv->setup) { + if (!on_the_fly && mxc_fbi->dispdrv && mxc_fbi->dispdrv->drv->setup) { retval = mxc_fbi->dispdrv->drv->setup(mxc_fbi->dispdrv, fbi); if (retval < 0) { dev_err(fbi->device, "setup error, dispdrv:%s.\n", @@ -578,11 +1258,13 @@ static int mxcfb_set_par(struct fb_info *fbi) } } - _setup_disp_channel1(fbi); - if (ovfbi_enable) - _setup_disp_channel1(mxc_fbi->ovfbi); + if (!on_the_fly) { + _setup_disp_channel1(fbi); + if (ovfbi_enable) + _setup_disp_channel1(mxc_fbi->ovfbi); + } - if (!mxc_fbi->overlay) { + if (!mxc_fbi->overlay && !on_the_fly) { uint32_t out_pixel_fmt; memset(&sig_cfg, 0, sizeof(sig_cfg)); @@ -638,7 +1320,7 @@ static int mxcfb_set_par(struct fb_info *fbi) return retval; } - if (ovfbi_enable) { + if (ovfbi_enable && !on_the_fly) { if (ov_pos_ret >= 0) ipu_disp_set_window_pos( mxc_fbi_fg->ipu, mxc_fbi_fg->ipu_ch, @@ -651,16 +1333,18 @@ static int mxcfb_set_par(struct fb_info *fbi) } } - ipu_enable_channel(mxc_fbi->ipu, mxc_fbi->ipu_ch); - if (ovfbi_enable) - ipu_enable_channel(mxc_fbi_fg->ipu, mxc_fbi_fg->ipu_ch); + if (!on_the_fly) { + ipu_enable_channel(mxc_fbi->ipu, mxc_fbi->ipu_ch); + if (ovfbi_enable) + ipu_enable_channel(mxc_fbi_fg->ipu, mxc_fbi_fg->ipu_ch); + } /* Clear activate as not Reconfiguring framebuffer again */ if ((fbi->var.activate & FB_ACTIVATE_FORCE) && (fbi->var.activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) fbi->var.activate = FB_ACTIVATE_NOW; - if (mxc_fbi->dispdrv && mxc_fbi->dispdrv->drv->enable) { + if (!on_the_fly && mxc_fbi->dispdrv && mxc_fbi->dispdrv->drv->enable) { retval = mxc_fbi->dispdrv->drv->enable(mxc_fbi->dispdrv, fbi); if (retval < 0) { dev_err(fbi->device, "enable error, dispdrv:%s.\n", @@ -670,6 +1354,10 @@ static int mxcfb_set_par(struct fb_info *fbi) } mxc_fbi->cur_var = fbi->var; + mxc_fbi->cur_ipu_pfmt = on_the_fly ? mxc_fbi->final_pfmt : + fbi_to_pixfmt(fbi, false); + mxc_fbi->cur_fb_pfmt = fbi_to_pixfmt(fbi, true); + mxc_fbi->cur_prefetch = mxc_fbi->prefetch; return retval; } @@ -839,7 +1527,11 @@ static int mxcfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) u32 vtotal; u32 htotal; struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)info->par; - + struct fb_info tmp_fbi; + unsigned int fr_xoff, fr_yoff, fr_w, fr_h, line_length; + unsigned long base = 0; + int ret, bw = 0, bh = 0; + bool triple_buffer = false; if (var->xres == 0 || var->yres == 0) return 0; @@ -865,6 +1557,21 @@ static int mxcfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) var->xres = bg_xres - pos_x; if ((var->yres + pos_y) > bg_yres) var->yres = bg_yres - pos_y; + + if (fbi_tmp->var.vmode & FB_VMODE_INTERLACED) + var->vmode |= FB_VMODE_INTERLACED; + else + var->vmode &= ~FB_VMODE_INTERLACED; + + var->pixclock = fbi_tmp->var.pixclock; + var->right_margin = fbi_tmp->var.right_margin; + var->hsync_len = fbi_tmp->var.hsync_len; + var->left_margin = fbi_tmp->var.left_margin + + fbi_tmp->var.xres - var->xres; + var->upper_margin = fbi_tmp->var.upper_margin; + var->vsync_len = fbi_tmp->var.vsync_len; + var->lower_margin = fbi_tmp->var.lower_margin + + fbi_tmp->var.yres - var->yres; } if (var->rotate > IPU_ROTATE_VERT_FLIP) @@ -873,17 +1580,131 @@ 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; - if (var->yres_virtual < var->yres) + if (var->yres_virtual < var->yres) { var->yres_virtual = var->yres * 3; + triple_buffer = true; + } if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) && (var->bits_per_pixel != 16) && (var->bits_per_pixel != 12) && (var->bits_per_pixel != 8)) var->bits_per_pixel = 16; - if (check_var_pixfmt(var)) + if (check_var_pixfmt(var)) { /* Fall back to default */ - bpp_to_var(var->bits_per_pixel, var); + ret = bpp_to_var(var->bits_per_pixel, var); + if (ret < 0) + return ret; + } + + if (ipu_pixel_format_is_gpu_tile(var->nonstd)) { + fmt_to_tile_alignment(var->nonstd, &bw, &bh); + var->xres_virtual = ALIGN(var->xres_virtual, bw); + if (triple_buffer) + var->yres_virtual = 3 * ALIGN(var->yres, bh); + else + var->yres_virtual = ALIGN(var->yres_virtual, bh); + } + + line_length = var->xres_virtual * var->bits_per_pixel / 8; + fr_xoff = var->xoffset; + fr_w = var->xres_virtual; + if (!(var->vmode & FB_VMODE_YWRAP)) { + fr_yoff = var->yoffset % var->yres; + fr_h = var->yres; + base = line_length * var->yres * + (var->yoffset / var->yres); + if (ipu_pixel_format_is_split_gpu_tile(var->nonstd)) + base += (mxc_fbi->gpu_sec_buf_off - + line_length * var->yres / 2) * + (var->yoffset / var->yres); + } else { + fr_yoff = var->yoffset; + fr_h = var->yres_virtual; + } + + tmp_fbi.var = *var; + tmp_fbi.par = mxc_fbi; + if (ipu_pixel_format_is_gpu_tile(var->nonstd)) { + unsigned int crop_line, prg_width = var->xres, offset; + int ipu_stride, prg_stride, bs; + bool tmp_prefetch = mxc_fbi->prefetch; + + if (!(var->xres % 32)) + bs = 32; + else if (!(var->xres % 16)) + bs = 16; + else + bs = 32; + + prg_width += fr_xoff % bw; + if (((fr_xoff % bw) + prg_width) % bs) + prg_width = ALIGN(prg_width, bs); + + mxc_fbi->prefetch = true; + ipu_stride = prg_width * + bytes_per_pixel(fbi_to_pixfmt(&tmp_fbi, false)); + + if (var->vmode & FB_VMODE_INTERLACED) { + if ((fr_yoff % bh) % 2) { + dev_err(info->device, + "wrong crop value in interlaced mode\n"); + return -EINVAL; + } + crop_line = (fr_yoff % bh) / 2; + prg_stride = ipu_stride * 2; + } else { + crop_line = fr_yoff % bh; + prg_stride = ipu_stride; + } + + offset = crop_line * prg_stride + + (fr_xoff % bw) * + bytes_per_pixel(fbi_to_pixfmt(&tmp_fbi, false)); + mxc_fbi->prefetch = tmp_prefetch; + if (offset % 8) { + dev_err(info->device, + "IPU base address is not 8byte aligned\n"); + return -EINVAL; + } + } else { + unsigned int uoff = 0, voff = 0; + int fb_stride; + + switch (fbi_to_pixfmt(&tmp_fbi, true)) { + case IPU_PIX_FMT_YUV420P2: + case IPU_PIX_FMT_YVU420P: + case IPU_PIX_FMT_NV12: + case PRE_PIX_FMT_NV21: + case IPU_PIX_FMT_NV16: + case PRE_PIX_FMT_NV61: + case IPU_PIX_FMT_YUV422P: + case IPU_PIX_FMT_YVU422P: + case IPU_PIX_FMT_YUV420P: + case IPU_PIX_FMT_YUV444P: + fb_stride = var->xres_virtual; + break; + default: + fb_stride = line_length; + } + base += fr_yoff * fb_stride + + fr_xoff * var->bits_per_pixel / 8; + + ipu_get_channel_offset(fbi_to_pixfmt(&tmp_fbi, true), + var->xres, + fr_h, + fr_w, + 0, 0, + fr_yoff, + fr_xoff, + &uoff, + &voff); + if (base % 8 || uoff % 8 || voff % 8) { + dev_err(info->device, + "IPU base address is not 8byte aligned\n"); + return -EINVAL; + } + } if (var->pixclock < 1000) { htotal = var->xres + var->right_margin + var->hsync_len + @@ -1000,7 +1821,7 @@ static int mxcfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg) { struct mxcfb_loc_alpha la; bool bad_pixfmt = - ipu_ch_param_bad_alpha_pos(fbi_to_pixfmt(fbi)); + ipu_ch_param_bad_alpha_pos(fbi_to_pixfmt(fbi, true)); if (copy_from_user(&la, (void *)arg, sizeof(la))) { retval = -EFAULT; @@ -1147,6 +1968,88 @@ static int mxcfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg) gamma.slopek); break; } + case MXCFB_SET_GPU_SPLIT_FMT: + { + struct mxcfb_gpu_split_fmt fmt; + + if (copy_from_user(&fmt, (void *)arg, sizeof(fmt))) { + retval = -EFAULT; + break; + } + + if (fmt.var.nonstd != IPU_PIX_FMT_GPU32_SB_ST && + fmt.var.nonstd != IPU_PIX_FMT_GPU32_SB_SRT && + fmt.var.nonstd != IPU_PIX_FMT_GPU16_SB_ST && + fmt.var.nonstd != IPU_PIX_FMT_GPU16_SB_SRT) { + retval = -EINVAL; + break; + } + + mxc_fbi->gpu_sec_buf_off = fmt.offset; + fmt.var.activate = (fbi->var.activate & ~FB_ACTIVATE_MASK) | + FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE; + console_lock(); + fbi->flags |= FBINFO_MISC_USEREVENT; + retval = fb_set_var(fbi, &fmt.var); + fbi->flags &= ~FBINFO_MISC_USEREVENT; + console_unlock(); + break; + } + case MXCFB_SET_PREFETCH: + { + int enable; + + if (copy_from_user(&enable, (void *)arg, sizeof(enable))) { + retval = -EFAULT; + break; + } + + if (!enable) { + if (ipu_pixel_format_is_gpu_tile(fbi_to_pixfmt(fbi, true))) { + dev_err(fbi->device, "Cannot disable prefetch in " + "resolving mode\n"); + retval = -EINVAL; + break; + } + if (ipu_pixel_format_is_pre_yuv(fbi_to_pixfmt(fbi, true))) { + dev_err(fbi->device, "Cannot disable prefetch when " + "PRE gets NV61 or NV21\n"); + retval = -EINVAL; + break; + } + } else { + if ((fbi->var.vmode & FB_VMODE_INTERLACED) && + ipu_pixel_format_is_multiplanar_yuv(fbi_to_pixfmt(fbi, true))) { + dev_err(fbi->device, "Cannot enable prefetch when " + "PRE gets multiplanar YUV frames\n"); + retval = -EINVAL; + break; + } + } + + if (mxc_fbi->cur_prefetch == !!enable) + break; + + retval = mxcfb_check_var(&fbi->var, fbi); + if (retval) + break; + + mxc_fbi->prefetch = !!enable; + + fbi->var.activate = (fbi->var.activate & ~FB_ACTIVATE_MASK) | + FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE; + retval = mxcfb_set_par(fbi); + break; + } + case MXCFB_GET_PREFETCH: + { + struct mxcfb_info *mxc_fbi = + (struct mxcfb_info *)fbi->par; + + if (put_user(mxc_fbi->cur_prefetch, argp)) + return -EFAULT; + break; + } case MXCFB_WAIT_FOR_VSYNC: { unsigned long long timestamp; @@ -1402,6 +2305,11 @@ static int mxcfb_blank(int blank, struct fb_info *info) if (mxc_fbi->ipu_di >= 0) ipu_uninit_sync_panel(mxc_fbi->ipu, mxc_fbi->ipu_di); ipu_uninit_channel(mxc_fbi->ipu, mxc_fbi->ipu_ch); + if (mxc_fbi->cur_prefetch) { + ipu_prg_disable(mxc_fbi->ipu_id, mxc_fbi->pre_num); + ipu_pre_disable(mxc_fbi->pre_num); + ipu_pre_free(&mxc_fbi->pre_num); + } break; case FB_BLANK_UNBLANK: info->var.activate = (info->var.activate & ~FB_ACTIVATE_MASK) | @@ -1429,12 +2337,25 @@ mxcfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) *mxc_graphic_fbi = NULL; u_int y_bottom; unsigned int fr_xoff, fr_yoff, fr_w, fr_h; - unsigned long base, active_alpha_phy_addr = 0; + unsigned int x_crop = 0, y_crop = 0; + unsigned long base, ipu_base = 0, active_alpha_phy_addr = 0; bool loc_alpha_en = false; int fb_stride; + int bw = 0, bh = 0; int i; int ret; + if (mxc_fbi->resolve) { + fmt_to_tile_block(info->var.nonstd, &bw, &bh); + + if (mxc_fbi->cur_var.xoffset % bw != var->xoffset % bw || + mxc_fbi->cur_var.yoffset % bh != var->yoffset % bh) { + dev_err(info->device, "do not support panning " + "with tile crop settings changed\n"); + return -EINVAL; + } + } + /* no pan display during fb blank */ if (mxc_fbi->ipu_ch == MEM_FG_SYNC) { struct mxcfb_info *bg_mxcfbi = NULL; @@ -1456,10 +2377,13 @@ mxcfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) if (y_bottom > info->var.yres_virtual) return -EINVAL; - switch (fbi_to_pixfmt(info)) { + switch (fbi_to_pixfmt(info, true)) { case IPU_PIX_FMT_YUV420P2: case IPU_PIX_FMT_YVU420P: case IPU_PIX_FMT_NV12: + case PRE_PIX_FMT_NV21: + case IPU_PIX_FMT_NV16: + case PRE_PIX_FMT_NV61: case IPU_PIX_FMT_YUV422P: case IPU_PIX_FMT_YVU422P: case IPU_PIX_FMT_YUV420P: @@ -1479,12 +2403,46 @@ mxcfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) fr_h = info->var.yres; base += info->fix.line_length * info->var.yres * (var->yoffset / info->var.yres); + if (ipu_pixel_format_is_split_gpu_tile(var->nonstd)) + base += (mxc_fbi->gpu_sec_buf_off - + info->fix.line_length * info->var.yres / 2) * + (var->yoffset / info->var.yres); } else { dev_dbg(info->device, "Y wrap enabled\n"); fr_yoff = var->yoffset; fr_h = info->var.yres_virtual; } - base += fr_yoff * fb_stride + fr_xoff; + if (!mxc_fbi->resolve) { + base += fr_yoff * fb_stride + fr_xoff * + bytes_per_pixel(fbi_to_pixfmt(info, true)); + + if (mxc_fbi->cur_prefetch && (info->var.vmode & FB_VMODE_INTERLACED)) + base += info->var.rotate ? + fr_w * bytes_per_pixel(fbi_to_pixfmt(info, true)) : 0; + } else { + x_crop = fr_xoff & ~(bw - 1); + y_crop = fr_yoff & ~(bh - 1); + } + + if (mxc_fbi->cur_prefetch) { + unsigned int sec_buf_off = 0, trd_buf_off = 0; + ipu_get_channel_offset(fbi_to_pixfmt(info, true), + info->var.xres, + fr_h, + fr_w, + 0, 0, + fr_yoff, + fr_xoff, + &sec_buf_off, + &trd_buf_off); + if (mxc_fbi->resolve) + sec_buf_off = mxc_fbi->gpu_sec_buf_off; + ipu_pre_set_fb_buffer(mxc_fbi->pre_num, base, + x_crop, y_crop, + sec_buf_off, trd_buf_off); + } else { + ipu_base = base; + } /* Check if DP local alpha is enabled and find the graphic fb */ if (mxc_fbi->ipu_ch == MEM_BG_SYNC || mxc_fbi->ipu_ch == MEM_FG_SYNC) { @@ -1520,15 +2478,20 @@ mxcfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) return -ETIMEDOUT; } - ++mxc_fbi->cur_ipu_buf; - mxc_fbi->cur_ipu_buf %= 3; + if (!mxc_fbi->cur_prefetch) { + ++mxc_fbi->cur_ipu_buf; + mxc_fbi->cur_ipu_buf %= 3; + dev_dbg(info->device, "Updating SDC %s buf %d address=0x%08lX\n", + info->fix.id, mxc_fbi->cur_ipu_buf, base); + } mxc_fbi->cur_ipu_alpha_buf = !mxc_fbi->cur_ipu_alpha_buf; - dev_dbg(info->device, "Updating SDC %s buf %d address=0x%08lX\n", - info->fix.id, mxc_fbi->cur_ipu_buf, base); + if (mxc_fbi->cur_prefetch) + goto next; if (ipu_update_channel_buffer(mxc_fbi->ipu, mxc_fbi->ipu_ch, IPU_INPUT_BUFFER, - mxc_fbi->cur_ipu_buf, base) == 0) { + mxc_fbi->cur_ipu_buf, ipu_base) == 0) { +next: /* Update the DP local alpha buffer only for graphic plane */ if (loc_alpha_en && mxc_graphic_fbi == mxc_fbi && ipu_update_channel_buffer(mxc_graphic_fbi->ipu, mxc_graphic_fbi->ipu_ch, @@ -1541,18 +2504,20 @@ mxcfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) } /* update u/v offset */ - ipu_update_channel_offset(mxc_fbi->ipu, mxc_fbi->ipu_ch, - IPU_INPUT_BUFFER, - fbi_to_pixfmt(info), - fr_w, - fr_h, - fr_w, - 0, 0, - fr_yoff, - fr_xoff); - - ipu_select_buffer(mxc_fbi->ipu, mxc_fbi->ipu_ch, IPU_INPUT_BUFFER, - mxc_fbi->cur_ipu_buf); + if (!mxc_fbi->cur_prefetch) { + ipu_update_channel_offset(mxc_fbi->ipu, mxc_fbi->ipu_ch, + IPU_INPUT_BUFFER, + fbi_to_pixfmt(info, true), + fr_w, + fr_h, + fr_w, + 0, 0, + fr_yoff, + fr_xoff); + + 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); ipu_enable_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_irq); } else { @@ -1568,10 +2533,12 @@ mxcfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) IPU_INPUT_BUFFER, 1), ipu_check_buffer_ready(mxc_fbi->ipu, mxc_fbi->ipu_ch, IPU_INPUT_BUFFER, 2)); - ++mxc_fbi->cur_ipu_buf; - mxc_fbi->cur_ipu_buf %= 3; - ++mxc_fbi->cur_ipu_buf; - mxc_fbi->cur_ipu_buf %= 3; + if (!mxc_fbi->cur_prefetch) { + ++mxc_fbi->cur_ipu_buf; + mxc_fbi->cur_ipu_buf %= 3; + ++mxc_fbi->cur_ipu_buf; + mxc_fbi->cur_ipu_buf %= 3; + } mxc_fbi->cur_ipu_alpha_buf = !mxc_fbi->cur_ipu_alpha_buf; ipu_clear_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_irq); ipu_enable_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_irq); @@ -1581,6 +2548,8 @@ mxcfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) dev_dbg(info->device, "Update complete\n"); info->var.yoffset = var->yoffset; + mxc_fbi->cur_var.xoffset = var->xoffset; + mxc_fbi->cur_var.yoffset = var->yoffset; return 0; } @@ -1762,10 +2731,22 @@ static int mxcfb_resume(struct platform_device *pdev) */ static int mxcfb_map_video_memory(struct fb_info *fbi) { + struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par; + if (fbi->fix.smem_len < fbi->var.yres_virtual * fbi->fix.line_length) fbi->fix.smem_len = fbi->var.yres_virtual * fbi->fix.line_length; + if (mxc_fbi->resolve && mxc_fbi->gpu_sec_buf_off) { + if (fbi->var.vmode & FB_VMODE_YWRAP) + fbi->fix.smem_len = mxc_fbi->gpu_sec_buf_off + + fbi->fix.smem_len / 2; + else + fbi->fix.smem_len = mxc_fbi->gpu_sec_buf_off * + (fbi->var.yres_virtual / fbi->var.yres) + + fbi->fix.smem_len / 2; + } + fbi->screen_base = dma_alloc_writecombine(fbi->device, fbi->fix.smem_len, (dma_addr_t *)&fbi->fix.smem_start, @@ -2038,6 +3019,10 @@ static int mxcfb_option_setup(struct platform_device *pdev, struct fb_info *fbi) fb_pix_fmt = IPU_PIX_FMT_ABGR32; else if (!strncmp(opt+6, "RGB565", 6)) fb_pix_fmt = IPU_PIX_FMT_RGB565; + else if (!strncmp(opt+6, "BGRA4444", 8)) + fb_pix_fmt = IPU_PIX_FMT_BGRA4444; + else if (!strncmp(opt+6, "BGRA5551", 8)) + fb_pix_fmt = IPU_PIX_FMT_BGRA5551; if (fb_pix_fmt) { pixfmt_to_var(fb_pix_fmt, &fbi->var); @@ -2232,6 +3217,9 @@ static int mxcfb_setup_overlay(struct platform_device *pdev, mxcfbi_fg->ipu_di_pix_fmt = mxcfbi_bg->ipu_di_pix_fmt; mxcfbi_fg->overlay = true; mxcfbi_fg->cur_blank = mxcfbi_fg->next_blank = FB_BLANK_POWERDOWN; + mxcfbi_fg->prefetch = false; + mxcfbi_fg->resolve = false; + mxcfbi_fg->pre_num = -1; /* Need dummy values until real panel is configured */ ovfbi->var.xres = 240; @@ -2329,6 +3317,8 @@ static int mxcfb_get_of_property(struct platform_device *pdev, return err; } + plat_data->prefetch = of_property_read_bool(np, "prefetch"); + if (!strncmp(pixfmt, "RGB24", 5)) plat_data->interface_pix_fmt = IPU_PIX_FMT_RGB24; else if (!strncmp(pixfmt, "BGR24", 5)) @@ -2417,6 +3407,9 @@ static int mxcfb_probe(struct platform_device *pdev) mxcfbi->ipu_int_clk = plat_data->int_clk; mxcfbi->late_init = plat_data->late_init; mxcfbi->first_set_par = true; + mxcfbi->prefetch = plat_data->prefetch; + mxcfbi->pre_num = -1; + ret = mxcfb_dispdrv_init(pdev, fbi); if (ret < 0) goto init_dispdrv_failed; |