From 555d21d9a918b8b64dd24c62f5ce67d84193208d Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Sun, 16 Sep 2012 00:06:43 +0800 Subject: [PATCH] ENGR00224700 IPUv3 FB:Support RGB24/RGB32/ABGR32 pixel formats Users may call FBIOPUT_VSCREENINFO framebuffer ioctrl to set a framebuffer's pixel format by specifying bits_per_pixel field and red/green/blue/transp fields of struct fb_var_screeninfo. Users may also know a framebuffer's pixel format at which it is working by calling FBIOGET_VSCREENINFO framebuffer ioctrl, red/ green/blue/transp bitfields of struct fb_var_screeninfo tell the exact offset and length of each color component in a pixel. This patch supports RGB24/RGB32/ABGR32 pixel formats with these 2 ioctrls. To change the default pixel format when initializeing a framebuffer at the first time, users may add 'fbpix=' option in framebuffer kernel command line option, for example, 'video=mxcfb0:fbpix=ABGR32' makes fb0's default pixel format be ABGR32. 'bpp=' option cannot overwrite 'fbpix=' option, for example, 'video=mxfb0:fbpix=RGB24, bpp=16' still makes fb0 work at RGB24 by default. To be back compatible, this patch doesn't change the default pixel formats (BGR24/BGR32/RGB565) for each bits_per_pixel, if users don't provide valid 'fbpix=' option. Signed-off-by: Liu Ying --- drivers/video/mxc/mxc_ipuv3_fb.c | 284 +++++++++++++++++-------------- 1 file changed, 157 insertions(+), 127 deletions(-) diff --git a/drivers/video/mxc/mxc_ipuv3_fb.c b/drivers/video/mxc/mxc_ipuv3_fb.c index 8152c47a4434..a6b191b79805 100644 --- a/drivers/video/mxc/mxc_ipuv3_fb.c +++ b/drivers/video/mxc/mxc_ipuv3_fb.c @@ -97,6 +97,25 @@ struct mxcfb_info { struct fb_var_screeninfo cur_var; }; +struct mxcfb_pfmt { + u32 fb_pix_fmt; + int bpp; + struct fb_bitfield red; + struct fb_bitfield green; + struct fb_bitfield blue; + struct fb_bitfield transp; +}; + +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_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} }, +}; + struct mxcfb_alloc_list { struct list_head list; dma_addr_t phy_addr; @@ -114,14 +133,12 @@ enum { static bool g_dp_in_use[2]; LIST_HEAD(fb_alloc_list); -static uint32_t bpp_to_pixfmt(struct fb_info *fbi) +/* Return default standard(RGB) pixel format */ +static uint32_t bpp_to_pixfmt(int bpp) { uint32_t pixfmt = 0; - if (fbi->var.nonstd) - return fbi->var.nonstd; - - switch (fbi->var.bits_per_pixel) { + switch (bpp) { case 24: pixfmt = IPU_PIX_FMT_BGR24; break; @@ -135,6 +152,82 @@ static uint32_t bpp_to_pixfmt(struct fb_info *fbi) return pixfmt; } +static inline int bitfield_is_equal(struct fb_bitfield f1, + struct fb_bitfield f2) +{ + return !memcmp(&f1, &f2, sizeof(f1)); +} + +static int pixfmt_to_var(uint32_t pixfmt, struct fb_var_screeninfo *var) +{ + int i, ret = -1; + + for (i = 0; i < ARRAY_SIZE(mxcfb_pfmts); i++) { + if (pixfmt == mxcfb_pfmts[i].fb_pix_fmt) { + var->red = mxcfb_pfmts[i].red; + var->green = mxcfb_pfmts[i].green; + var->blue = mxcfb_pfmts[i].blue; + var->transp = mxcfb_pfmts[i].transp; + var->bits_per_pixel = mxcfb_pfmts[i].bpp; + ret = 0; + break; + } + } + return ret; +} + +static int bpp_to_var(int bpp, struct fb_var_screeninfo *var) +{ + uint32_t pixfmt = 0; + + pixfmt = bpp_to_pixfmt(bpp); + if (pixfmt) + return pixfmt_to_var(pixfmt, var); + else + return -1; +} + +static int check_var_pixfmt(struct fb_var_screeninfo *var) +{ + int i, ret = -1; + + 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) && + bitfield_is_equal(var->blue, mxcfb_pfmts[i].blue) && + bitfield_is_equal(var->transp, mxcfb_pfmts[i].transp) && + var->bits_per_pixel == mxcfb_pfmts[i].bpp) { + ret = 0; + break; + } + } + return ret; +} + +static uint32_t fbi_to_pixfmt(struct fb_info *fbi) +{ + int i; + uint32_t pixfmt = 0; + + if (fbi->var.nonstd) + return fbi->var.nonstd; + + 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) && + bitfield_is_equal(fbi->var.blue, mxcfb_pfmts[i].blue) && + bitfield_is_equal(fbi->var.transp, mxcfb_pfmts[i].transp)) { + pixfmt = mxcfb_pfmts[i].fb_pix_fmt; + break; + } + } + + if (pixfmt == 0) + dev_err(fbi->device, "cannot get pixel format\n"); + + return pixfmt; +} + static struct fb_info *found_registered_fb(ipu_channel_t ipu_ch, int ipu_id) { int i; @@ -194,13 +287,13 @@ 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 = bpp_to_pixfmt(fbi); + params.mem_dc_sync.in_pixel_fmt = fbi_to_pixfmt(fbi); } 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 = bpp_to_pixfmt(fbi); + params.mem_dp_bg_sync.in_pixel_fmt = fbi_to_pixfmt(fbi); if (mxc_fbi->alpha_chan_en) params.mem_dp_bg_sync.alpha_chan_en = true; } @@ -217,7 +310,7 @@ static int _setup_disp_channel2(struct fb_info *fbi) unsigned long base; unsigned int fr_xoff, fr_yoff, fr_w, fr_h; - switch (bpp_to_pixfmt(fbi)) { + switch (fbi_to_pixfmt(fbi)) { case IPU_PIX_FMT_YUV420P2: case IPU_PIX_FMT_YVU420P: case IPU_PIX_FMT_NV12: @@ -265,7 +358,7 @@ static int _setup_disp_channel2(struct fb_info *fbi) retval = ipu_init_channel_buffer(mxc_fbi->ipu, mxc_fbi->ipu_ch, IPU_INPUT_BUFFER, - bpp_to_pixfmt(fbi), + fbi_to_pixfmt(fbi), fbi->var.xres, fbi->var.yres, fb_stride, fbi->var.rotate, @@ -283,7 +376,7 @@ static int _setup_disp_channel2(struct fb_info *fbi) /* update u/v offset */ ipu_update_channel_offset(mxc_fbi->ipu, mxc_fbi->ipu_ch, IPU_INPUT_BUFFER, - bpp_to_pixfmt(fbi), + fbi_to_pixfmt(fbi), fr_w, fr_h, fr_w, @@ -753,76 +846,9 @@ static int mxcfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) (var->bits_per_pixel != 8)) var->bits_per_pixel = 16; - switch (var->bits_per_pixel) { - case 8: - var->red.length = 3; - var->red.offset = 5; - var->red.msb_right = 0; - - var->green.length = 3; - var->green.offset = 2; - var->green.msb_right = 0; - - var->blue.length = 2; - var->blue.offset = 0; - var->blue.msb_right = 0; - - var->transp.length = 0; - var->transp.offset = 0; - var->transp.msb_right = 0; - break; - case 16: - var->red.length = 5; - var->red.offset = 11; - var->red.msb_right = 0; - - var->green.length = 6; - var->green.offset = 5; - var->green.msb_right = 0; - - var->blue.length = 5; - var->blue.offset = 0; - var->blue.msb_right = 0; - - var->transp.length = 0; - var->transp.offset = 0; - var->transp.msb_right = 0; - break; - case 24: - var->red.length = 8; - var->red.offset = 16; - var->red.msb_right = 0; - - var->green.length = 8; - var->green.offset = 8; - var->green.msb_right = 0; - - var->blue.length = 8; - var->blue.offset = 0; - var->blue.msb_right = 0; - - var->transp.length = 0; - var->transp.offset = 0; - var->transp.msb_right = 0; - break; - case 32: - var->red.length = 8; - var->red.offset = 16; - var->red.msb_right = 0; - - var->green.length = 8; - var->green.offset = 8; - var->green.msb_right = 0; - - var->blue.length = 8; - var->blue.offset = 0; - var->blue.msb_right = 0; - - var->transp.length = 8; - var->transp.offset = 24; - var->transp.msb_right = 0; - break; - } + if (check_var_pixfmt(var)) + /* Fall back to default */ + bpp_to_var(var->bits_per_pixel, var); if (var->pixclock < 1000) { htotal = var->xres + var->right_margin + var->hsync_len + @@ -1360,7 +1386,7 @@ mxcfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) if (y_bottom > info->var.yres_virtual) return -EINVAL; - switch (bpp_to_pixfmt(info)) { + switch (fbi_to_pixfmt(info)) { case IPU_PIX_FMT_YUV420P2: case IPU_PIX_FMT_YVU420P: case IPU_PIX_FMT_NV12: @@ -1447,7 +1473,7 @@ 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, - bpp_to_pixfmt(info), + fbi_to_pixfmt(info), fr_w, fr_h, fr_w, @@ -1858,12 +1884,14 @@ static int mxcfb_dispdrv_init(struct platform_device *pdev, * Parse user specified options (`video=trident:') * example: * video=mxcfb0:dev=lcd,800x480M-16@55,if=RGB565,bpp=16,noaccel + * video=mxcfb0:dev=lcd,800x480M-16@55,if=RGB565,fbpix=RGB565 */ -static int mxcfb_option_setup(struct platform_device *pdev) +static int mxcfb_option_setup(struct platform_device *pdev, struct fb_info *fbi) { struct ipuv3_fb_platform_data *pdata = pdev->dev.platform_data; char *options, *opt, *fb_mode_str = NULL; char name[] = "mxcfb0"; + uint32_t fb_pix_fmt = 0; name[5] += pdev->id; if (fb_get_options(name, &options)) { @@ -1881,61 +1909,63 @@ static int mxcfb_option_setup(struct platform_device *pdev) if (!strncmp(opt, "dev=", 4)) { memcpy(pdata->disp_dev, opt + 4, strlen(opt) - 4); pdata->disp_dev[strlen(opt) - 4] = '\0'; - continue; - } - if (!strncmp(opt, "if=", 3)) { - if (!strncmp(opt+3, "RGB24", 5)) { + } else if (!strncmp(opt, "if=", 3)) { + if (!strncmp(opt+3, "RGB24", 5)) pdata->interface_pix_fmt = IPU_PIX_FMT_RGB24; - continue; - } else if (!strncmp(opt+3, "BGR24", 5)) { + else if (!strncmp(opt+3, "BGR24", 5)) pdata->interface_pix_fmt = IPU_PIX_FMT_BGR24; - continue; - } - if (!strncmp(opt+3, "GBR24", 5)) { + else if (!strncmp(opt+3, "GBR24", 5)) pdata->interface_pix_fmt = IPU_PIX_FMT_GBR24; - continue; - } - if (!strncmp(opt+3, "RGB565", 6)) { + else if (!strncmp(opt+3, "RGB565", 6)) pdata->interface_pix_fmt = IPU_PIX_FMT_RGB565; - continue; - } - if (!strncmp(opt+3, "RGB666", 6)) { + else if (!strncmp(opt+3, "RGB666", 6)) pdata->interface_pix_fmt = IPU_PIX_FMT_RGB666; - continue; - } - if (!strncmp(opt+3, "YUV444", 6)) { + else if (!strncmp(opt+3, "YUV444", 6)) pdata->interface_pix_fmt = IPU_PIX_FMT_YUV444; - continue; - } - if (!strncmp(opt+3, "LVDS666", 7)) { + else if (!strncmp(opt+3, "LVDS666", 7)) pdata->interface_pix_fmt = IPU_PIX_FMT_LVDS666; - continue; - } - if (!strncmp(opt+3, "YUYV16", 6)) { + else if (!strncmp(opt+3, "YUYV16", 6)) pdata->interface_pix_fmt = IPU_PIX_FMT_YUYV; - continue; - } - if (!strncmp(opt+3, "UYVY16", 6)) { + else if (!strncmp(opt+3, "UYVY16", 6)) pdata->interface_pix_fmt = IPU_PIX_FMT_UYVY; - continue; - } - if (!strncmp(opt+3, "YVYU16", 6)) { + else if (!strncmp(opt+3, "YVYU16", 6)) pdata->interface_pix_fmt = IPU_PIX_FMT_YVYU; - continue; - } - if (!strncmp(opt+3, "VYUY16", 6)) { + else if (!strncmp(opt+3, "VYUY16", 6)) pdata->interface_pix_fmt = IPU_PIX_FMT_VYUY; - continue; + } else if (!strncmp(opt, "fbpix=", 6)) { + if (!strncmp(opt+6, "RGB24", 5)) + fb_pix_fmt = IPU_PIX_FMT_RGB24; + else if (!strncmp(opt+6, "BGR24", 5)) + fb_pix_fmt = IPU_PIX_FMT_BGR24; + else if (!strncmp(opt+6, "RGB32", 5)) + fb_pix_fmt = IPU_PIX_FMT_RGB32; + else if (!strncmp(opt+6, "BGR32", 5)) + fb_pix_fmt = IPU_PIX_FMT_BGR32; + else if (!strncmp(opt+6, "ABGR32", 6)) + fb_pix_fmt = IPU_PIX_FMT_ABGR32; + else if (!strncmp(opt+6, "RGB565", 6)) + fb_pix_fmt = IPU_PIX_FMT_RGB565; + + if (fb_pix_fmt) { + pixfmt_to_var(fb_pix_fmt, &fbi->var); + pdata->default_bpp = + fbi->var.bits_per_pixel; } - } - if (!strncmp(opt, "int_clk", 7)) { + } else if (!strncmp(opt, "int_clk", 7)) { pdata->int_clk = true; continue; - } - if (!strncmp(opt, "bpp=", 4)) + } else if (!strncmp(opt, "bpp=", 4)) { + /* bpp setting cannot overwirte fbpix setting */ + if (fb_pix_fmt) + continue; + pdata->default_bpp = simple_strtoul(opt + 4, NULL, 0); - else + + fb_pix_fmt = bpp_to_pixfmt(pdata->default_bpp); + if (fb_pix_fmt) + pixfmt_to_var(fb_pix_fmt, &fbi->var); + } else fb_mode_str = opt; } @@ -2144,10 +2174,6 @@ static int mxcfb_probe(struct platform_device *pdev) struct resource *res; int ret = 0; - ret = mxcfb_option_setup(pdev); - if (ret) - goto get_fb_option_failed; - /* Initialize FB structures */ fbi = mxcfb_init_fbinfo(&pdev->dev, &mxcfb_ops); if (!fbi) { @@ -2155,6 +2181,10 @@ static int mxcfb_probe(struct platform_device *pdev) goto init_fbinfo_failed; } + ret = mxcfb_option_setup(pdev, fbi); + if (ret) + goto get_fb_option_failed; + mxcfbi = (struct mxcfb_info *)fbi->par; mxcfbi->ipu_int_clk = plat_data->int_clk; ret = mxcfb_dispdrv_init(pdev, fbi); @@ -2263,8 +2293,8 @@ ipu_in_busy: init_dispdrv_failed: fb_dealloc_cmap(&fbi->cmap); framebuffer_release(fbi); -init_fbinfo_failed: get_fb_option_failed: +init_fbinfo_failed: return ret; } -- 2.39.5