[5/9] Add i.MX5 framebuffer driver
diff mbox series

Message ID 1291902441-24712-6-git-send-email-s.hauer@pengutronix.de
State New, archived
Headers show
Series
  • [RFC] i.MX51 Framebuffer support
Related show

Commit Message

Sascha Hauer Dec. 9, 2010, 1:47 p.m. UTC
This patch adds framebuffer support to the Freescale i.MX SoCs
equipped with an IPU v3, so far these are the i.MX50/51/53.

This driver has been tested on the i.MX51 babbage board with
both DVI and analog VGA in different resolutions and color depths.
It has also been tested on a custom i.MX51 board using a fixed
resolution panel.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/video/Kconfig  |   11 +
 drivers/video/Makefile |    1 +
 drivers/video/mx5fb.c  |  846 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 858 insertions(+), 0 deletions(-)
 create mode 100644 drivers/video/mx5fb.c

Comments

Liu Ying Dec. 12, 2010, 6:13 a.m. UTC | #1
Hello, Sascha,

I have following comments to this patch:
1) Please modify the commit message, as IPUv3 is not embedded in i.MX50 SoC.
2) ADC is not supported yet in the framebuffer driver, so please
modify this comment:
   > + * Framebuffer Framebuffer Driver for SDC and ADC.
3) 'ipu_dp_set_window_pos()' is called only once in
imx_ipu_fb_set_par_overlay(). So, the framebuffer driver doesn't
support to change the overlay framebuffer position. Need a
mechanism/interface for users to change the overlay framebuffer
position.
4) Need to make sure the framebuffer on DP-FG is blanked before the
framebuffer on DP-BG is blanked. Meanwhile, the framebuffer on DP-FG
should be unblanked after the framebuffer on DP-BG is unblanked
5) Need to check the framebuffer on DP-FG doesn't run out of the range
of the framebuffer on DP-BG.
6) I prefer to find the video mode in modedb first, and if we cannot
find the video mode in common video mode data base, we can find a
video mode in custom video mode data base which is defined in platform
data. In this way, we don't need to export common modefb.

Best Regards,
Liu Ying


2010/12/9 Sascha Hauer <s.hauer@pengutronix.de>:
> This patch adds framebuffer support to the Freescale i.MX SoCs
> equipped with an IPU v3, so far these are the i.MX50/51/53.
>
> This driver has been tested on the i.MX51 babbage board with
> both DVI and analog VGA in different resolutions and color depths.
> It has also been tested on a custom i.MX51 board using a fixed
> resolution panel.
>
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> ---
>  drivers/video/Kconfig  |   11 +
>  drivers/video/Makefile |    1 +
>  drivers/video/mx5fb.c  |  846 ++++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 858 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/video/mx5fb.c
>
> diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
> index 27c1fb4..1901915 100644
> --- a/drivers/video/Kconfig
> +++ b/drivers/video/Kconfig
> @@ -2236,6 +2236,17 @@ config FB_MX3
>          far only synchronous displays are supported. If you plan to use
>          an LCD display with your i.MX31 system, say Y here.
>
> +config FB_MX5
> +       tristate "MX5 Framebuffer support"
> +       depends on FB && MFD_IMX_IPU_V3
> +       select FB_CFB_FILLRECT
> +       select FB_CFB_COPYAREA
> +       select FB_CFB_IMAGEBLIT
> +       select FB_MODE_HELPERS
> +       help
> +         This is a framebuffer device for the i.MX51 LCD Controller. If you
> +         plan to use an LCD display with your i.MX51 system, say Y here.
> +
>  config FB_BROADSHEET
>        tristate "E-Ink Broadsheet/Epson S1D13521 controller support"
>        depends on FB
> diff --git a/drivers/video/Makefile b/drivers/video/Makefile
> index 485e8ed..ad408d2 100644
> --- a/drivers/video/Makefile
> +++ b/drivers/video/Makefile
> @@ -145,6 +145,7 @@ obj-$(CONFIG_FB_BF54X_LQ043)          += bf54x-lq043fb.o
>  obj-$(CONFIG_FB_BFIN_LQ035Q1)     += bfin-lq035q1-fb.o
>  obj-$(CONFIG_FB_BFIN_T350MCQB)   += bfin-t350mcqb-fb.o
>  obj-$(CONFIG_FB_MX3)             += mx3fb.o
> +obj-$(CONFIG_FB_MX5)             += mx5fb.o
>  obj-$(CONFIG_FB_DA8XX)           += da8xx-fb.o
>
>  # the test framebuffer is last
> diff --git a/drivers/video/mx5fb.c b/drivers/video/mx5fb.c
> new file mode 100644
> index 0000000..fd9baf4
> --- /dev/null
> +++ b/drivers/video/mx5fb.c
> @@ -0,0 +1,846 @@
> +/*
> + * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
> + *
> + * The code contained herein is licensed under the GNU General Public
> + * License. You may obtain a copy of the GNU General Public License
> + * Version 2 or later at the following locations:
> + *
> + * http://www.opensource.org/licenses/gpl-license.html
> + * http://www.gnu.org/copyleft/gpl.html
> + *
> + * Framebuffer Framebuffer Driver for SDC and ADC.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/platform_device.h>
> +#include <linux/errno.h>
> +#include <linux/string.h>
> +#include <linux/interrupt.h>
> +#include <linux/slab.h>
> +#include <linux/fb.h>
> +#include <linux/delay.h>
> +#include <linux/init.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/console.h>
> +#include <linux/mfd/imx-ipu-v3.h>
> +#include <asm/uaccess.h>
> +#include <mach/ipu-v3.h>
> +
> +#define DRIVER_NAME "imx-ipuv3-fb"
> +
> +struct imx_ipu_fb_info {
> +       int                     ipu_channel_num;
> +       struct ipu_channel      *ipu_ch;
> +       int                     dc;
> +       int                     ipu_di;
> +       u32                     ipu_di_pix_fmt;
> +       u32                     ipu_in_pix_fmt;
> +
> +       u32                     pseudo_palette[16];
> +
> +       struct ipu_dp           *dp;
> +       struct dmfc_channel     *dmfc;
> +       struct fb_info          *slave;
> +       struct fb_info          *master;
> +       bool                    enabled;
> +};
> +
> +static int imx_ipu_fb_set_fix(struct fb_info *info)
> +{
> +       struct fb_fix_screeninfo *fix = &info->fix;
> +       struct fb_var_screeninfo *var = &info->var;
> +
> +       fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
> +
> +       fix->type = FB_TYPE_PACKED_PIXELS;
> +       fix->accel = FB_ACCEL_NONE;
> +       fix->visual = FB_VISUAL_TRUECOLOR;
> +       fix->xpanstep = 1;
> +       fix->ypanstep = 1;
> +
> +       return 0;
> +}
> +
> +static int imx_ipu_fb_map_video_memory(struct fb_info *fbi)
> +{
> +       int size;
> +
> +       size = fbi->var.yres_virtual * fbi->fix.line_length;
> +
> +       if (fbi->screen_base) {
> +               if (fbi->fix.smem_len >= size)
> +                       return 0;
> +
> +               dma_free_writecombine(fbi->device, fbi->fix.smem_len,
> +                             fbi->screen_base, fbi->fix.smem_start);
> +       }
> +
> +       fbi->screen_base = dma_alloc_writecombine(fbi->device,
> +                               size,
> +                               (dma_addr_t *)&fbi->fix.smem_start,
> +                               GFP_DMA);
> +       if (fbi->screen_base == 0) {
> +               dev_err(fbi->device, "Unable to allocate framebuffer memory (%d)\n",
> +                               fbi->fix.smem_len);
> +               fbi->fix.smem_len = 0;
> +               fbi->fix.smem_start = 0;
> +               return -ENOMEM;
> +       }
> +
> +       fbi->fix.smem_len = size;
> +       fbi->screen_size = fbi->fix.smem_len;
> +
> +       dev_dbg(fbi->device, "allocated fb @ paddr=0x%08lx, size=%d\n",
> +               fbi->fix.smem_start, fbi->fix.smem_len);
> +
> +       /* Clear the screen */
> +       memset((char *)fbi->screen_base, 0, fbi->fix.smem_len);
> +
> +       return 0;
> +}
> +
> +static void imx_ipu_fb_enable(struct fb_info *fbi)
> +{
> +       struct imx_ipu_fb_info *mxc_fbi = fbi->par;
> +
> +       if (mxc_fbi->enabled)
> +               return;
> +
> +       ipu_di_enable(mxc_fbi->ipu_di);
> +       ipu_dmfc_enable_channel(mxc_fbi->dmfc);
> +       ipu_idmac_enable_channel(mxc_fbi->ipu_ch);
> +       ipu_dc_enable_channel(mxc_fbi->dc);
> +       ipu_dp_enable_channel(mxc_fbi->dp);
> +       mxc_fbi->enabled = 1;
> +}
> +
> +static void imx_ipu_fb_disable(struct fb_info *fbi)
> +{
> +       struct imx_ipu_fb_info *mxc_fbi = fbi->par;
> +
> +       if (!mxc_fbi->enabled)
> +               return;
> +
> +       ipu_dp_disable_channel(mxc_fbi->dp);
> +       ipu_dc_disable_channel(mxc_fbi->dc);
> +       ipu_idmac_disable_channel(mxc_fbi->ipu_ch);
> +       ipu_dmfc_disable_channel(mxc_fbi->dmfc);
> +       ipu_di_disable(mxc_fbi->ipu_di);
> +
> +       mxc_fbi->enabled = 0;
> +}
> +
> +static int calc_vref(struct fb_var_screeninfo *var)
> +{
> +       unsigned long htotal, vtotal;
> +
> +       htotal = var->xres + var->right_margin + var->hsync_len + var->left_margin;
> +       vtotal = var->yres + var->lower_margin + var->vsync_len + var->upper_margin;
> +
> +       if (!htotal || !vtotal)
> +               return 60;
> +
> +       return PICOS2KHZ(var->pixclock) * 1000 / vtotal / htotal;
> +}
> +
> +static int calc_bandwidth(struct fb_var_screeninfo *var, unsigned int vref)
> +{
> +       return var->xres * var->yres * vref;
> +}
> +
> +static int imx_ipu_fb_set_par(struct fb_info *fbi)
> +{
> +       int ret;
> +       struct ipu_di_signal_cfg sig_cfg;
> +       struct imx_ipu_fb_info *mxc_fbi = fbi->par;
> +       u32 out_pixel_fmt;
> +       int interlaced = 0;
> +       struct fb_var_screeninfo *var = &fbi->var;
> +       int enabled = mxc_fbi->enabled;
> +
> +       dev_dbg(fbi->device, "Reconfiguring framebuffer %dx%d-%d\n",
> +               fbi->var.xres, fbi->var.yres, fbi->var.bits_per_pixel);
> +
> +       if (enabled)
> +               imx_ipu_fb_disable(fbi);
> +
> +       fbi->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8;
> +
> +       var->yres_virtual = var->yres;
> +
> +       ret = imx_ipu_fb_map_video_memory(fbi);
> +       if (ret)
> +               return ret;
> +
> +       if (var->vmode & FB_VMODE_INTERLACED)
> +               interlaced = 1;
> +
> +       memset(&sig_cfg, 0, sizeof(sig_cfg));
> +       out_pixel_fmt = mxc_fbi->ipu_di_pix_fmt;
> +
> +       if (var->vmode & FB_VMODE_INTERLACED)
> +               sig_cfg.interlaced = 1;
> +       if (var->vmode & FB_VMODE_ODD_FLD_FIRST) /* PAL */
> +               sig_cfg.odd_field_first = 1;
> +       if (var->sync & FB_SYNC_EXT)
> +               sig_cfg.ext_clk = 1;
> +       if (var->sync & FB_SYNC_HOR_HIGH_ACT)
> +               sig_cfg.Hsync_pol = 1;
> +       if (var->sync & FB_SYNC_VERT_HIGH_ACT)
> +               sig_cfg.Vsync_pol = 1;
> +       if (!(var->sync & FB_SYNC_CLK_LAT_FALL))
> +               sig_cfg.clk_pol = 1;
> +       if (var->sync & FB_SYNC_DATA_INVERT)
> +               sig_cfg.data_pol = 1;
> +       if (!(var->sync & FB_SYNC_OE_LOW_ACT))
> +               sig_cfg.enable_pol = 1;
> +       if (var->sync & FB_SYNC_CLK_IDLE_EN)
> +               sig_cfg.clkidle_en = 1;
> +
> +       dev_dbg(fbi->device, "pixclock = %lu.%03lu MHz\n",
> +               PICOS2KHZ(var->pixclock) / 1000,
> +               PICOS2KHZ(var->pixclock) % 1000);
> +
> +       sig_cfg.width = var->xres;
> +       sig_cfg.height = var->yres;
> +       sig_cfg.pixel_fmt = out_pixel_fmt;
> +       sig_cfg.h_start_width = var->left_margin;
> +       sig_cfg.h_sync_width = var->hsync_len;
> +       sig_cfg.h_end_width = var->right_margin;
> +       sig_cfg.v_start_width = var->upper_margin;
> +       sig_cfg.v_sync_width = var->vsync_len;
> +       sig_cfg.v_end_width = var->lower_margin;
> +       sig_cfg.v_to_h_sync = 0;
> +
> +       if (mxc_fbi->dp) {
> +               ret = ipu_dp_setup_channel(mxc_fbi->dp, mxc_fbi->ipu_in_pix_fmt,
> +                               out_pixel_fmt, 1);
> +               if (ret) {
> +                       dev_dbg(fbi->device, "initializing display processor failed with %d\n",
> +                               ret);
> +                       return ret;
> +               }
> +       }
> +
> +       ret = ipu_dc_init_sync(mxc_fbi->dc, mxc_fbi->ipu_di, interlaced,
> +                       out_pixel_fmt, fbi->var.xres);
> +       if (ret) {
> +               dev_dbg(fbi->device, "initializing display controller failed with %d\n",
> +                               ret);
> +               return ret;
> +       }
> +
> +       ret = ipu_di_init_sync_panel(mxc_fbi->ipu_di,
> +                               PICOS2KHZ(var->pixclock) * 1000UL,
> +                               &sig_cfg);
> +       if (ret) {
> +               dev_dbg(fbi->device, "initializing panel failed with %d\n",
> +                               ret);
> +               return ret;
> +       }
> +
> +       fbi->mode = (struct fb_videomode *)fb_match_mode(var, &fbi->modelist);
> +       var->xoffset = var->yoffset = 0;
> +
> +       if (fbi->var.vmode & FB_VMODE_INTERLACED)
> +               interlaced = 1;
> +
> +       ret = ipu_idmac_init_channel_buffer(mxc_fbi->ipu_ch,
> +                                       mxc_fbi->ipu_in_pix_fmt,
> +                                       var->xres, var->yres,
> +                                       fbi->fix.line_length,
> +                                       IPU_ROTATE_NONE,
> +                                       fbi->fix.smem_start,
> +                                       0,
> +                                       0, 0, interlaced);
> +       if (ret) {
> +               dev_dbg(fbi->device, "init channel buffer failed with %d\n",
> +                               ret);
> +               return ret;
> +       }
> +
> +       ret = ipu_dmfc_init_channel(mxc_fbi->dmfc, var->xres);
> +       if (ret) {
> +               dev_dbg(fbi->device, "initializing dmfc channel failed with %d\n",
> +                               ret);
> +               return ret;
> +       }
> +
> +       ret = ipu_dmfc_alloc_bandwidth(mxc_fbi->dmfc, calc_bandwidth(var, calc_vref(var)));
> +       if (ret) {
> +               dev_dbg(fbi->device, "allocating dmfc bandwidth failed with %d\n",
> +                               ret);
> +               return ret;
> +       }
> +
> +       if (enabled)
> +               imx_ipu_fb_enable(fbi);
> +
> +       return ret;
> +}
> +
> +/*
> + * These are the bitfields for each
> + * display depth that we support.
> + */
> +struct imxfb_rgb {
> +       struct fb_bitfield      red;
> +       struct fb_bitfield      green;
> +       struct fb_bitfield      blue;
> +       struct fb_bitfield      transp;
> +};
> +
> +static struct imxfb_rgb def_rgb_8 = {
> +       .red    = { .offset =  5, .length = 3, },
> +       .green  = { .offset =  2, .length = 3, },
> +       .blue   = { .offset =  0, .length = 2, },
> +       .transp = { .offset =  0, .length = 0, },
> +};
> +
> +static struct imxfb_rgb def_rgb_16 = {
> +       .red    = { .offset = 11, .length = 5, },
> +       .green  = { .offset =  5, .length = 6, },
> +       .blue   = { .offset =  0, .length = 5, },
> +       .transp = { .offset =  0, .length = 0, },
> +};
> +
> +static struct imxfb_rgb def_rgb_24 = {
> +       .red    = { .offset = 16, .length = 8, },
> +       .green  = { .offset =  8, .length = 8, },
> +       .blue   = { .offset =  0, .length = 8, },
> +       .transp = { .offset =  0, .length = 0, },
> +};
> +
> +static struct imxfb_rgb def_rgb_32 = {
> +       .red    = { .offset = 16, .length = 8, },
> +       .green  = { .offset =  8, .length = 8, },
> +       .blue   = { .offset =  0, .length = 8, },
> +       .transp = { .offset = 24, .length = 8, },
> +};
> +
> +/*
> + * Check framebuffer variable parameters and adjust to valid values.
> + *
> + * @param       var      framebuffer variable parameters
> + *
> + * @param       info     framebuffer information pointer
> + */
> +static int imx_ipu_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
> +{
> +       struct imx_ipu_fb_info *mxc_fbi = info->par;
> +       struct imxfb_rgb *rgb;
> +
> +       /* we don't support xpan, force xres_virtual to be equal to xres */
> +       var->xres_virtual = var->xres;
> +
> +       if (var->yres_virtual < var->yres)
> +               var->yres_virtual = var->yres;
> +
> +       switch (var->bits_per_pixel) {
> +       case 8:
> +               rgb = &def_rgb_8;
> +               break;
> +       case 16:
> +               rgb = &def_rgb_16;
> +               mxc_fbi->ipu_in_pix_fmt = IPU_PIX_FMT_RGB565;
> +               break;
> +       case 24:
> +               rgb = &def_rgb_24;
> +               mxc_fbi->ipu_in_pix_fmt = IPU_PIX_FMT_BGR24;
> +               break;
> +       case 32:
> +               rgb = &def_rgb_32;
> +               mxc_fbi->ipu_in_pix_fmt = IPU_PIX_FMT_BGR32;
> +               break;
> +       default:
> +               var->bits_per_pixel = 24;
> +               rgb = &def_rgb_24;
> +               mxc_fbi->ipu_in_pix_fmt = IPU_PIX_FMT_BGR24;
> +       }
> +
> +       var->red    = rgb->red;
> +       var->green  = rgb->green;
> +       var->blue   = rgb->blue;
> +       var->transp = rgb->transp;
> +
> +       return 0;
> +}
> +
> +static inline unsigned int chan_to_field(u_int chan, struct fb_bitfield *bf)
> +{
> +       chan &= 0xffff;
> +       chan >>= 16 - bf->length;
> +       return chan << bf->offset;
> +}
> +
> +static int imx_ipu_fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
> +                          u_int trans, struct fb_info *fbi)
> +{
> +       unsigned int val;
> +       int ret = 1;
> +
> +       /*
> +        * If greyscale is true, then we convert the RGB value
> +        * to greyscale no matter what visual we are using.
> +        */
> +       if (fbi->var.grayscale)
> +               red = green = blue = (19595 * red + 38470 * green +
> +                                     7471 * blue) >> 16;
> +       switch (fbi->fix.visual) {
> +       case FB_VISUAL_TRUECOLOR:
> +               /*
> +                * 16-bit True Colour.  We encode the RGB value
> +                * according to the RGB bitfield information.
> +                */
> +               if (regno < 16) {
> +                       u32 *pal = fbi->pseudo_palette;
> +
> +                       val = chan_to_field(red, &fbi->var.red);
> +                       val |= chan_to_field(green, &fbi->var.green);
> +                       val |= chan_to_field(blue, &fbi->var.blue);
> +
> +                       pal[regno] = val;
> +                       ret = 0;
> +               }
> +               break;
> +
> +       case FB_VISUAL_STATIC_PSEUDOCOLOR:
> +       case FB_VISUAL_PSEUDOCOLOR:
> +               break;
> +       }
> +
> +       return ret;
> +}
> +
> +static int imx_ipu_fb_blank(int blank, struct fb_info *info)
> +{
> +       dev_dbg(info->device, "blank = %d\n", blank);
> +
> +       switch (blank) {
> +       case FB_BLANK_POWERDOWN:
> +       case FB_BLANK_VSYNC_SUSPEND:
> +       case FB_BLANK_HSYNC_SUSPEND:
> +       case FB_BLANK_NORMAL:
> +               imx_ipu_fb_disable(info);
> +               break;
> +       case FB_BLANK_UNBLANK:
> +               imx_ipu_fb_enable(info);
> +               break;
> +       }
> +
> +       return 0;
> +}
> +
> +static int imx_ipu_fb_pan_display(struct fb_var_screeninfo *var,
> +               struct fb_info *info)
> +{
> +       struct imx_ipu_fb_info *mxc_fbi = info->par;
> +       unsigned long base;
> +       int ret;
> +
> +       if (info->var.yoffset == var->yoffset)
> +               return 0;       /* No change, do nothing */
> +
> +       base = var->yoffset * var->xres_virtual * var->bits_per_pixel / 8;
> +       base += info->fix.smem_start;
> +
> +       ret = ipu_wait_for_interrupt(IPU_IRQ_EOF(mxc_fbi->ipu_channel_num), 100);
> +       if (ret)
> +               return ret;
> +
> +       if (ipu_idmac_update_channel_buffer(mxc_fbi->ipu_ch, 0, base)) {
> +               dev_err(info->device,
> +                       "Error updating SDC buf to address=0x%08lX\n", base);
> +       }
> +
> +       info->var.yoffset = var->yoffset;
> +
> +       return 0;
> +}
> +
> +static struct fb_ops imx_ipu_fb_ops = {
> +       .owner          = THIS_MODULE,
> +       .fb_set_par     = imx_ipu_fb_set_par,
> +       .fb_check_var   = imx_ipu_fb_check_var,
> +       .fb_setcolreg   = imx_ipu_fb_setcolreg,
> +       .fb_pan_display = imx_ipu_fb_pan_display,
> +       .fb_fillrect    = cfb_fillrect,
> +       .fb_copyarea    = cfb_copyarea,
> +       .fb_imageblit   = cfb_imageblit,
> +       .fb_blank       = imx_ipu_fb_blank,
> +};
> +
> +/*
> + * Overlay functions
> + */
> +static int imx_ipu_fb_enable_overlay(struct fb_info *fbi)
> +{
> +       struct imx_ipu_fb_info *mxc_fbi = fbi->par;
> +
> +       ipu_dmfc_enable_channel(mxc_fbi->dmfc);
> +       ipu_idmac_enable_channel(mxc_fbi->ipu_ch);
> +       ipu_dp_enable_fg(mxc_fbi->dp);
> +
> +       return 0;
> +}
> +
> +static int imx_ipu_fb_disable_overlay(struct fb_info *fbi)
> +{
> +       struct imx_ipu_fb_info *mxc_fbi = fbi->par;
> +
> +       ipu_dp_disable_fg(mxc_fbi->dp);
> +       ipu_idmac_disable_channel(mxc_fbi->ipu_ch);
> +       ipu_dmfc_disable_channel(mxc_fbi->dmfc);
> +
> +       return 0;
> +}
> +
> +static int imx_ipu_fb_set_par_overlay(struct fb_info *fbi)
> +{
> +       struct imx_ipu_fb_info *mxc_fbi = fbi->par;
> +       struct fb_var_screeninfo *var = &fbi->var;
> +       struct fb_info *fbi_master = mxc_fbi->master;
> +       struct fb_var_screeninfo *var_master = &fbi_master->var;
> +       int ret;
> +       int interlaced = 0;
> +       int enabled = mxc_fbi->enabled;
> +
> +       dev_dbg(fbi->device, "Reconfiguring framebuffer %dx%d-%d\n",
> +               fbi->var.xres, fbi->var.yres, fbi->var.bits_per_pixel);
> +
> +       if (enabled)
> +               imx_ipu_fb_disable_overlay(fbi);
> +
> +       fbi->fix.line_length = var->xres_virtual *
> +                                  var->bits_per_pixel / 8;
> +
> +       ret = imx_ipu_fb_map_video_memory(fbi);
> +       if (ret)
> +               return ret;
> +
> +       ipu_dp_set_window_pos(mxc_fbi->dp, 64, 64);
> +
> +       var->xoffset = var->yoffset = 0;
> +
> +       if (var->vmode & FB_VMODE_INTERLACED)
> +               interlaced = 1;
> +
> +       ret = ipu_idmac_init_channel_buffer(mxc_fbi->ipu_ch,
> +                                       mxc_fbi->ipu_in_pix_fmt,
> +                                       var->xres, var->yres,
> +                                       fbi->fix.line_length,
> +                                       IPU_ROTATE_NONE,
> +                                       fbi->fix.smem_start,
> +                                       0,
> +                                       0, 0, interlaced);
> +       if (ret) {
> +               dev_dbg(fbi->device, "init channel buffer failed with %d\n",
> +                               ret);
> +               return ret;
> +       }
> +
> +       ret = ipu_dmfc_init_channel(mxc_fbi->dmfc, var->xres);
> +       if (ret) {
> +               dev_dbg(fbi->device, "initializing dmfc channel failed with %d\n",
> +                               ret);
> +               return ret;
> +       }
> +
> +       ret = ipu_dmfc_alloc_bandwidth(mxc_fbi->dmfc, calc_bandwidth(var, calc_vref(var_master)));
> +       if (ret) {
> +               dev_dbg(fbi->device, "allocating dmfc bandwidth failed with %d\n",
> +                               ret);
> +               return ret;
> +       }
> +
> +       if (enabled)
> +               imx_ipu_fb_enable_overlay(fbi);
> +
> +       return ret;
> +}
> +
> +static int imx_ipu_fb_blank_overlay(int blank, struct fb_info *fbi)
> +{
> +       dev_dbg(fbi->device, "blank = %d\n", blank);
> +
> +       switch (blank) {
> +       case FB_BLANK_POWERDOWN:
> +       case FB_BLANK_VSYNC_SUSPEND:
> +       case FB_BLANK_HSYNC_SUSPEND:
> +       case FB_BLANK_NORMAL:
> +               imx_ipu_fb_disable_overlay(fbi);
> +               break;
> +       case FB_BLANK_UNBLANK:
> +               imx_ipu_fb_enable_overlay(fbi);
> +               break;
> +       }
> +
> +       return 0;
> +}
> +
> +static struct fb_ops imx_ipu_fb_overlay_ops = {
> +       .owner          = THIS_MODULE,
> +       .fb_set_par     = imx_ipu_fb_set_par_overlay,
> +       .fb_check_var   = imx_ipu_fb_check_var,
> +       .fb_setcolreg   = imx_ipu_fb_setcolreg,
> +       .fb_pan_display = imx_ipu_fb_pan_display,
> +       .fb_fillrect    = cfb_fillrect,
> +       .fb_copyarea    = cfb_copyarea,
> +       .fb_imageblit   = cfb_imageblit,
> +       .fb_blank       = imx_ipu_fb_blank_overlay,
> +};
> +
> +static struct fb_info *imx_ipu_fb_init_fbinfo(struct device *dev, struct fb_ops *ops)
> +{
> +       struct fb_info *fbi;
> +       struct imx_ipu_fb_info *mxc_fbi;
> +
> +       fbi = framebuffer_alloc(sizeof(struct imx_ipu_fb_info), dev);
> +       if (!fbi)
> +               return NULL;
> +
> +       BUG_ON(fbi->par == NULL);
> +       mxc_fbi = fbi->par;
> +
> +       fbi->var.activate = FB_ACTIVATE_NOW;
> +
> +       fbi->fbops = ops;
> +       fbi->flags = FBINFO_FLAG_DEFAULT;
> +       fbi->pseudo_palette = mxc_fbi->pseudo_palette;
> +
> +       fb_alloc_cmap(&fbi->cmap, 16, 0);
> +
> +       return fbi;
> +}
> +
> +static int imx_ipu_fb_init_overlay(struct platform_device *pdev,
> +               struct fb_info *fbi_master, int ipu_channel)
> +{
> +       struct imx_ipu_fb_info *mxc_fbi_master = fbi_master->par;
> +       struct fb_info *ovlfbi;
> +       struct imx_ipu_fb_info *ovl_mxc_fbi;
> +       int ret;
> +
> +       ovlfbi = imx_ipu_fb_init_fbinfo(&pdev->dev, &imx_ipu_fb_overlay_ops);
> +       if (!ovlfbi)
> +               return -ENOMEM;
> +
> +       ovl_mxc_fbi = ovlfbi->par;
> +       ovl_mxc_fbi->ipu_ch = ipu_idmac_get(ipu_channel);
> +       ovl_mxc_fbi->dmfc = ipu_dmfc_get(ipu_channel);
> +       ovl_mxc_fbi->ipu_di = -1;
> +       ovl_mxc_fbi->dp = mxc_fbi_master->dp;
> +       ovl_mxc_fbi->master = fbi_master;
> +       mxc_fbi_master->slave = ovlfbi;
> +
> +       ovlfbi->var.xres = 240;
> +       ovlfbi->var.yres = 320;
> +       ovlfbi->var.yres_virtual = ovlfbi->var.yres;
> +       ovlfbi->var.xres_virtual = ovlfbi->var.xres;
> +       imx_ipu_fb_check_var(&ovlfbi->var, ovlfbi);
> +       imx_ipu_fb_set_fix(ovlfbi);
> +
> +       ret = register_framebuffer(ovlfbi);
> +       if (ret) {
> +               framebuffer_release(ovlfbi);
> +               return ret;
> +       }
> +
> +       ipu_dp_set_global_alpha(ovl_mxc_fbi->dp, 1, 0x80, 1);
> +       ipu_dp_set_color_key(ovl_mxc_fbi->dp, 0, 0);
> +
> +       imx_ipu_fb_set_par_overlay(ovlfbi);
> +
> +       return 0;
> +}
> +
> +static void imx_ipu_fb_exit_overlay(struct platform_device *pdev,
> +               struct fb_info *fbi_master, int ipu_channel)
> +{
> +       struct imx_ipu_fb_info *mxc_fbi_master = fbi_master->par;
> +       struct fb_info *ovlfbi = mxc_fbi_master->slave;
> +       struct imx_ipu_fb_info *ovl_mxc_fbi = ovlfbi->par;
> +
> +       imx_ipu_fb_blank_overlay(FB_BLANK_POWERDOWN, ovlfbi);
> +
> +       unregister_framebuffer(ovlfbi);
> +
> +       ipu_idmac_put(ovl_mxc_fbi->ipu_ch);
> +       ipu_dmfc_free_bandwidth(ovl_mxc_fbi->dmfc);
> +       ipu_dmfc_put(ovl_mxc_fbi->dmfc);
> +
> +       framebuffer_release(ovlfbi);
> +}
> +
> +static int imx_ipu_fb_find_mode(struct fb_info *fbi)
> +{
> +       int ret;
> +       struct fb_videomode *mode_array;
> +       struct fb_modelist *modelist;
> +       struct fb_var_screeninfo *var = &fbi->var;
> +       int i = 0;
> +
> +       list_for_each_entry(modelist, &fbi->modelist, list)
> +               i++;
> +
> +       mode_array = kmalloc(sizeof (struct fb_modelist) * i, GFP_KERNEL);
> +       if (!mode_array)
> +               return -ENOMEM;
> +
> +       i = 0;
> +       list_for_each_entry(modelist, &fbi->modelist, list)
> +               mode_array[i++] = modelist->mode;
> +
> +       ret = fb_find_mode(&fbi->var, fbi, NULL, mode_array, i, NULL, 16);
> +       if (ret == 0)
> +               return -EINVAL;
> +
> +       dev_dbg(fbi->device, "found %dx%d-%d hs:%d:%d:%d vs:%d:%d:%d\n",
> +                       var->xres, var->yres, var->bits_per_pixel,
> +                       var->hsync_len, var->left_margin, var->right_margin,
> +                       var->vsync_len, var->upper_margin, var->lower_margin);
> +
> +       kfree(mode_array);
> +
> +       return 0;
> +}
> +
> +static int __devinit imx_ipu_fb_probe(struct platform_device *pdev)
> +{
> +       struct fb_info *fbi;
> +       struct imx_ipu_fb_info *mxc_fbi;
> +       struct ipuv3_fb_platform_data *plat_data = pdev->dev.platform_data;
> +       int ret = 0, i;
> +
> +       pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
> +
> +       fbi = imx_ipu_fb_init_fbinfo(&pdev->dev, &imx_ipu_fb_ops);
> +       if (!fbi)
> +               return -ENOMEM;
> +
> +       mxc_fbi = fbi->par;
> +
> +       mxc_fbi->ipu_channel_num = plat_data->ipu_channel_bg;
> +       mxc_fbi->dc = plat_data->dc_channel;
> +       mxc_fbi->ipu_di_pix_fmt = plat_data->interface_pix_fmt;
> +       mxc_fbi->ipu_di = pdev->id;
> +
> +       mxc_fbi->ipu_ch = ipu_idmac_get(plat_data->ipu_channel_bg);
> +       if (IS_ERR(mxc_fbi->ipu_ch)) {
> +               ret = PTR_ERR(mxc_fbi->ipu_ch);
> +               goto failed_request_ipu;
> +       }
> +
> +       mxc_fbi->dmfc = ipu_dmfc_get(plat_data->ipu_channel_bg);
> +       if (IS_ERR(mxc_fbi->ipu_ch)) {
> +               ret = PTR_ERR(mxc_fbi->ipu_ch);
> +               goto failed_request_dmfc;
> +       }
> +
> +       if (plat_data->dp_channel >= 0) {
> +               mxc_fbi->dp = ipu_dp_get(plat_data->dp_channel);
> +               if (IS_ERR(mxc_fbi->dp)) {
> +                       ret = PTR_ERR(mxc_fbi->ipu_ch);
> +                       goto failed_request_dp;
> +               }
> +       }
> +
> +       fbi->var.yres_virtual = fbi->var.yres;
> +
> +       INIT_LIST_HEAD(&fbi->modelist);
> +       for (i = 0; i < plat_data->num_modes; i++)
> +               fb_add_videomode(&plat_data->modes[i], &fbi->modelist);
> +
> +       if (plat_data->flags & IMX_IPU_FB_USE_MODEDB) {
> +               for (i = 0; i < num_fb_modes; i++)
> +                       fb_add_videomode(&fb_modes[i], &fbi->modelist);
> +       }
> +
> +       imx_ipu_fb_find_mode(fbi);
> +
> +       imx_ipu_fb_check_var(&fbi->var, fbi);
> +       imx_ipu_fb_set_fix(fbi);
> +       ret = register_framebuffer(fbi);
> +       if (ret < 0)
> +               goto failed_register;
> +
> +       imx_ipu_fb_set_par(fbi);
> +       imx_ipu_fb_blank(FB_BLANK_UNBLANK, fbi);
> +
> +       if (plat_data->ipu_channel_fg >= 0 && plat_data->flags & IMX_IPU_FB_USE_OVERLAY)
> +               imx_ipu_fb_init_overlay(pdev, fbi, plat_data->ipu_channel_fg);
> +
> +       platform_set_drvdata(pdev, fbi);
> +
> +       return 0;
> +
> +failed_register:
> +       if (plat_data->dp_channel >= 0)
> +               ipu_dp_put(mxc_fbi->dp);
> +failed_request_dp:
> +       ipu_dmfc_put(mxc_fbi->dmfc);
> +failed_request_dmfc:
> +       ipu_idmac_put(mxc_fbi->ipu_ch);
> +failed_request_ipu:
> +       fb_dealloc_cmap(&fbi->cmap);
> +       framebuffer_release(fbi);
> +
> +       return ret;
> +}
> +
> +static int __devexit imx_ipu_fb_remove(struct platform_device *pdev)
> +{
> +       struct fb_info *fbi = platform_get_drvdata(pdev);
> +       struct imx_ipu_fb_info *mxc_fbi = fbi->par;
> +       struct ipuv3_fb_platform_data *plat_data = pdev->dev.platform_data;
> +
> +       if (plat_data->ipu_channel_fg >= 0 && plat_data->flags & IMX_IPU_FB_USE_OVERLAY)
> +               imx_ipu_fb_exit_overlay(pdev, fbi, plat_data->ipu_channel_fg);
> +
> +       imx_ipu_fb_blank(FB_BLANK_POWERDOWN, fbi);
> +
> +       dma_free_writecombine(fbi->device, fbi->fix.smem_len,
> +                             fbi->screen_base, fbi->fix.smem_start);
> +
> +       if (&fbi->cmap)
> +               fb_dealloc_cmap(&fbi->cmap);
> +
> +       unregister_framebuffer(fbi);
> +
> +       if (plat_data->dp_channel >= 0)
> +               ipu_dp_put(mxc_fbi->dp);
> +       ipu_dmfc_free_bandwidth(mxc_fbi->dmfc);
> +       ipu_dmfc_put(mxc_fbi->dmfc);
> +       ipu_idmac_put(mxc_fbi->ipu_ch);
> +
> +       framebuffer_release(fbi);
> +
> +       return 0;
> +}
> +
> +static struct platform_driver imx_ipu_fb_driver = {
> +       .driver = {
> +               .name = DRIVER_NAME,
> +       },
> +       .probe = imx_ipu_fb_probe,
> +       .remove = __devexit_p(imx_ipu_fb_remove),
> +};
> +
> +static int __init imx_ipu_fb_init(void)
> +{
> +       return platform_driver_register(&imx_ipu_fb_driver);
> +}
> +
> +static void __exit imx_ipu_fb_exit(void)
> +{
> +       platform_driver_unregister(&imx_ipu_fb_driver);
> +}
> +
> +module_init(imx_ipu_fb_init);
> +module_exit(imx_ipu_fb_exit);
> +
> +MODULE_AUTHOR("Freescale Semiconductor, Inc.");
> +MODULE_DESCRIPTION("i.MX framebuffer driver");
> +MODULE_LICENSE("GPL");
> +MODULE_SUPPORTED_DEVICE("fb");
> --
> 1.7.2.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
Lothar Waßmann Dec. 13, 2010, 7:23 a.m. UTC | #2
Hi,

Liu Ying writes:
> 6) I prefer to find the video mode in modedb first, and if we cannot
> find the video mode in common video mode data base, we can find a
> video mode in custom video mode data base which is defined in platform
> data. In this way, we don't need to export common modefb.
> 
IMO platform specific video modes should take precedence over generic
ones, so the platform data should be evaluated first.


Lothar Waßmann
Liu Ying Dec. 13, 2010, 11:35 a.m. UTC | #3
Hello, Lothar Waßmann,

I think your opinion is reasonable and better.
So, first, we can find the video mode in the video mode data base
defined in platform data, and then, if this fails, we can find it in
common video mode data base.

Best Regards,
Liu Ying

2010/12/13 Lothar Waßmann <LW@karo-electronics.de>:
> Hi,
>
> Liu Ying writes:
>> 6) I prefer to find the video mode in modedb first, and if we cannot
>> find the video mode in common video mode data base, we can find a
>> video mode in custom video mode data base which is defined in platform
>> data. In this way, we don't need to export common modefb.
>>
> IMO platform specific video modes should take precedence over generic
> ones, so the platform data should be evaluated first.
>
>
> Lothar Waßmann
> --
> ___________________________________________________________
>
> Ka-Ro electronics GmbH | Pascalstraße 22 | D - 52076 Aachen
> Phone: +49 2408 1402-0 | Fax: +49 2408 1402-10
> Geschäftsführer: Matthias Kaussen
> Handelsregistereintrag: Amtsgericht Aachen, HRB 4996
>
> www.karo-electronics.de | info@karo-electronics.de
> ___________________________________________________________
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
Sascha Hauer Dec. 13, 2010, 11:38 a.m. UTC | #4
On Sun, Dec 12, 2010 at 02:13:40PM +0800, Liu Ying wrote:
> Hello, Sascha,
> 
> I have following comments to this patch:
> 1) Please modify the commit message, as IPUv3 is not embedded in i.MX50 SoC.

ok.

> 2) ADC is not supported yet in the framebuffer driver, so please
> modify this comment:
>    > + * Framebuffer Framebuffer Driver for SDC and ADC.

ok.

> 3) 'ipu_dp_set_window_pos()' is called only once in
> imx_ipu_fb_set_par_overlay(). So, the framebuffer driver doesn't
> support to change the overlay framebuffer position. Need a
> mechanism/interface for users to change the overlay framebuffer
> position.

Yes. The overlay support is currently only present in the driver because
I didn't want to break it in general. There is no generic overlay API in
the kernel and I didn't want to invent the n+1 API. Currently the
possible use cases of the overlay support are not clear to me. Number
one use case would probably be to display video output, but the
corresponding driver would be a v4l2 driver, so in this case we would
only need an in-kernel API.
Anyway, I have not the resources to implement complete overlay support.
So either we keep it like it is and it more has an example character
or we remove it completely from the driver for now.

> 4) Need to make sure the framebuffer on DP-FG is blanked before the
> framebuffer on DP-BG is blanked. Meanwhile, the framebuffer on DP-FG
> should be unblanked after the framebuffer on DP-BG is unblanked
> 5) Need to check the framebuffer on DP-FG doesn't run out of the range
> of the framebuffer on DP-BG.

I tend to remove the overlay support.

> 6) I prefer to find the video mode in modedb first, and if we cannot
> find the video mode in common video mode data base, we can find a
> video mode in custom video mode data base which is defined in platform
> data. In this way, we don't need to export common modefb.

I exported the modedb to be able to show the different modes under
/sys/class/graphics/fb0/modes and to set it with
/sys/class/graphics/fb0/mode. If you want this there is no way around
exporting it. The ioctl interface for mode setting is independent of the
specific modes anyway.

BTW please make comments specific to specific code inline.

Sascha
Liu Ying Dec. 14, 2010, 6:40 a.m. UTC | #5
Hello, Sascha,

Please find my feedback with [LY] embedded.

And, a bug related with this patch:
1) Bootup the babbage board.
2) echo U:640x480p-60 > /sys/class/graphics/fb0/mode
3) cat /sys/class/graphics/fb0/virtual_size, it shows '640,480'.
4) echo 640,960 > /sys/class/graphics/fb0/virtual_size, change virtual
yres to be 960.
5) cat /sys/class/graphics/fb0/virtual_size, it still shows '640,480'.
I think this is caused by 'var->yres_virtual = var->yres;' in custom
fb_set_par() function(Sorry, I cannot make the comment inline this
time, as I don't find the original code within the mail I am
replying). This makes fb0 use only one block of memory whose size is
equal to screen size, so pan display can not really play her role and
screen tearing issue may happen.

Best Regards,
Liu Ying

2010/12/13 Sascha Hauer <s.hauer@pengutronix.de>:
> On Sun, Dec 12, 2010 at 02:13:40PM +0800, Liu Ying wrote:
>> Hello, Sascha,
>>
>> I have following comments to this patch:
>> 1) Please modify the commit message, as IPUv3 is not embedded in i.MX50 SoC.
>
> ok.
>
>> 2) ADC is not supported yet in the framebuffer driver, so please
>> modify this comment:
>>    > + * Framebuffer Framebuffer Driver for SDC and ADC.
>
> ok.
>
>> 3) 'ipu_dp_set_window_pos()' is called only once in
>> imx_ipu_fb_set_par_overlay(). So, the framebuffer driver doesn't
>> support to change the overlay framebuffer position. Need a
>> mechanism/interface for users to change the overlay framebuffer
>> position.
>
> Yes. The overlay support is currently only present in the driver because
> I didn't want to break it in general. There is no generic overlay API in
> the kernel and I didn't want to invent the n+1 API. Currently the
> possible use cases of the overlay support are not clear to me. Number
> one use case would probably be to display video output, but the
> corresponding driver would be a v4l2 driver, so in this case we would
> only need an in-kernel API.
> Anyway, I have not the resources to implement complete overlay support.
> So either we keep it like it is and it more has an example character
> or we remove it completely from the driver for now.
>
>> 4) Need to make sure the framebuffer on DP-FG is blanked before the
>> framebuffer on DP-BG is blanked. Meanwhile, the framebuffer on DP-FG
>> should be unblanked after the framebuffer on DP-BG is unblanked
>> 5) Need to check the framebuffer on DP-FG doesn't run out of the range
>> of the framebuffer on DP-BG.
>
> I tend to remove the overlay support.
>
>> 6) I prefer to find the video mode in modedb first, and if we cannot
>> find the video mode in common video mode data base, we can find a
>> video mode in custom video mode data base which is defined in platform
>> data. In this way, we don't need to export common modefb.
>
> I exported the modedb to be able to show the different modes under
> /sys/class/graphics/fb0/modes and to set it with
> /sys/class/graphics/fb0/mode. If you want this there is no way around
> exporting it. The ioctl interface for mode setting is independent of the
> specific modes anyway.
[LY]  Just a concern. If a platform choose to add the generic video
mode data base to IPUv3 framebuffer modelist, 'cat
/sys/class/graphics/fb0/modes' will show all the video modes in the
generic data base to users. I am not sure whether showing them to
users means the IPUv3 framebuffer driver acknowledges to support all
of them or not. I tried to do 'echo U:1800x1440p-70 >
/sys/class/graphics/fb0/mode' with the kernel on ipuv3 branch, there
will be a kernel dump(seems it can not allocate enough memory).
>
> BTW please make comments specific to specific code inline.
[LY] Ok.
>
> Sascha
>
>
> --
> Pengutronix e.K.                           |                             |
> Industrial Linux Solutions                 | http://www.pengutronix.de/  |
> Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
> Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
Sascha Hauer Dec. 14, 2010, 8:45 a.m. UTC | #6
On Tue, Dec 14, 2010 at 02:40:09PM +0800, Liu Ying wrote:
> Hello, Sascha,
> 
> Please find my feedback with [LY] embedded.
> 
> And, a bug related with this patch:
> 1) Bootup the babbage board.
> 2) echo U:640x480p-60 > /sys/class/graphics/fb0/mode
> 3) cat /sys/class/graphics/fb0/virtual_size, it shows '640,480'.
> 4) echo 640,960 > /sys/class/graphics/fb0/virtual_size, change virtual
> yres to be 960.
> 5) cat /sys/class/graphics/fb0/virtual_size, it still shows '640,480'.
> I think this is caused by 'var->yres_virtual = var->yres;' in custom
> fb_set_par() function(Sorry, I cannot make the comment inline this
> time, as I don't find the original code within the mail I am
> replying). This makes fb0 use only one block of memory whose size is
> equal to screen size, so pan display can not really play her role and
> screen tearing issue may happen.
> 
> Best Regards,
> Liu Ying
> 
> 2010/12/13 Sascha Hauer <s.hauer@pengutronix.de>:
> > On Sun, Dec 12, 2010 at 02:13:40PM +0800, Liu Ying wrote:
> >> Hello, Sascha,
> >>
> >> I have following comments to this patch:
> >> 1) Please modify the commit message, as IPUv3 is not embedded in i.MX50 SoC.
> >
> > ok.
> >
> >> 2) ADC is not supported yet in the framebuffer driver, so please
> >> modify this comment:
> >>    > + * Framebuffer Framebuffer Driver for SDC and ADC.
> >
> > ok.
> >
> >> 3) 'ipu_dp_set_window_pos()' is called only once in
> >> imx_ipu_fb_set_par_overlay(). So, the framebuffer driver doesn't
> >> support to change the overlay framebuffer position. Need a
> >> mechanism/interface for users to change the overlay framebuffer
> >> position.
> >
> > Yes. The overlay support is currently only present in the driver because
> > I didn't want to break it in general. There is no generic overlay API in
> > the kernel and I didn't want to invent the n+1 API. Currently the
> > possible use cases of the overlay support are not clear to me. Number
> > one use case would probably be to display video output, but the
> > corresponding driver would be a v4l2 driver, so in this case we would
> > only need an in-kernel API.
> > Anyway, I have not the resources to implement complete overlay support.
> > So either we keep it like it is and it more has an example character
> > or we remove it completely from the driver for now.
> >
> >> 4) Need to make sure the framebuffer on DP-FG is blanked before the
> >> framebuffer on DP-BG is blanked. Meanwhile, the framebuffer on DP-FG
> >> should be unblanked after the framebuffer on DP-BG is unblanked
> >> 5) Need to check the framebuffer on DP-FG doesn't run out of the range
> >> of the framebuffer on DP-BG.
> >
> > I tend to remove the overlay support.
> >
> >> 6) I prefer to find the video mode in modedb first, and if we cannot
> >> find the video mode in common video mode data base, we can find a
> >> video mode in custom video mode data base which is defined in platform
> >> data. In this way, we don't need to export common modefb.
> >
> > I exported the modedb to be able to show the different modes under
> > /sys/class/graphics/fb0/modes and to set it with
> > /sys/class/graphics/fb0/mode. If you want this there is no way around
> > exporting it. The ioctl interface for mode setting is independent of the
> > specific modes anyway.
> [LY]  Just a concern. If a platform choose to add the generic video
> mode data base to IPUv3 framebuffer modelist, 'cat
> /sys/class/graphics/fb0/modes' will show all the video modes in the
> generic data base to users. I am not sure whether showing them to
> users means the IPUv3 framebuffer driver acknowledges to support all
> of them or not. I tried to do 'echo U:1800x1440p-70 >
> /sys/class/graphics/fb0/mode' with the kernel on ipuv3 branch, there
> will be a kernel dump(seems it can not allocate enough memory).

We'll have to increase the dma coherent size (and max zone order) for
these resolutions to work. I have patches for this in my local tree but
decided to wait until some contiguous memory allocator hits the tree.
Also, the fb driver should sanity check the generic modes before adding
them to the list. FOr example the IPU can't do 1920x1200@60. This code
is missing currently.

Sascha
Liu Ying Dec. 14, 2010, 1:23 p.m. UTC | #7
2010/12/14 Sascha Hauer <s.hauer@pengutronix.de>:
> On Tue, Dec 14, 2010 at 02:40:09PM +0800, Liu Ying wrote:
>> Hello, Sascha,
>>
>> Please find my feedback with [LY] embedded.
>>
>> And, a bug related with this patch:
>> 1) Bootup the babbage board.
>> 2) echo U:640x480p-60 > /sys/class/graphics/fb0/mode
>> 3) cat /sys/class/graphics/fb0/virtual_size, it shows '640,480'.
>> 4) echo 640,960 > /sys/class/graphics/fb0/virtual_size, change virtual
>> yres to be 960.
>> 5) cat /sys/class/graphics/fb0/virtual_size, it still shows '640,480'.
>> I think this is caused by 'var->yres_virtual = var->yres;' in custom
>> fb_set_par() function(Sorry, I cannot make the comment inline this
>> time, as I don't find the original code within the mail I am
>> replying). This makes fb0 use only one block of memory whose size is
>> equal to screen size, so pan display can not really play her role and
>> screen tearing issue may happen.

Sascha,

Just would like to know if you've caught this bug.

>>
>> Best Regards,
>> Liu Ying
>>
>> 2010/12/13 Sascha Hauer <s.hauer@pengutronix.de>:
>> > On Sun, Dec 12, 2010 at 02:13:40PM +0800, Liu Ying wrote:
>> >> Hello, Sascha,
>> >>
>> >> I have following comments to this patch:
>> >> 1) Please modify the commit message, as IPUv3 is not embedded in i.MX50 SoC.
>> >
>> > ok.
>> >
>> >> 2) ADC is not supported yet in the framebuffer driver, so please
>> >> modify this comment:
>> >>    > + * Framebuffer Framebuffer Driver for SDC and ADC.
>> >
>> > ok.
>> >
>> >> 3) 'ipu_dp_set_window_pos()' is called only once in
>> >> imx_ipu_fb_set_par_overlay(). So, the framebuffer driver doesn't
>> >> support to change the overlay framebuffer position. Need a
>> >> mechanism/interface for users to change the overlay framebuffer
>> >> position.
>> >
>> > Yes. The overlay support is currently only present in the driver because
>> > I didn't want to break it in general. There is no generic overlay API in
>> > the kernel and I didn't want to invent the n+1 API. Currently the
>> > possible use cases of the overlay support are not clear to me. Number
>> > one use case would probably be to display video output, but the
>> > corresponding driver would be a v4l2 driver, so in this case we would
>> > only need an in-kernel API.
>> > Anyway, I have not the resources to implement complete overlay support.
>> > So either we keep it like it is and it more has an example character
>> > or we remove it completely from the driver for now.
>> >
>> >> 4) Need to make sure the framebuffer on DP-FG is blanked before the
>> >> framebuffer on DP-BG is blanked. Meanwhile, the framebuffer on DP-FG
>> >> should be unblanked after the framebuffer on DP-BG is unblanked
>> >> 5) Need to check the framebuffer on DP-FG doesn't run out of the range
>> >> of the framebuffer on DP-BG.
>> >
>> > I tend to remove the overlay support.
>> >
>> >> 6) I prefer to find the video mode in modedb first, and if we cannot
>> >> find the video mode in common video mode data base, we can find a
>> >> video mode in custom video mode data base which is defined in platform
>> >> data. In this way, we don't need to export common modefb.
>> >
>> > I exported the modedb to be able to show the different modes under
>> > /sys/class/graphics/fb0/modes and to set it with
>> > /sys/class/graphics/fb0/mode. If you want this there is no way around
>> > exporting it. The ioctl interface for mode setting is independent of the
>> > specific modes anyway.
>> [LY]  Just a concern. If a platform choose to add the generic video
>> mode data base to IPUv3 framebuffer modelist, 'cat
>> /sys/class/graphics/fb0/modes' will show all the video modes in the
>> generic data base to users. I am not sure whether showing them to
>> users means the IPUv3 framebuffer driver acknowledges to support all
>> of them or not. I tried to do 'echo U:1800x1440p-70 >
>> /sys/class/graphics/fb0/mode' with the kernel on ipuv3 branch, there
>> will be a kernel dump(seems it can not allocate enough memory).
>
> We'll have to increase the dma coherent size (and max zone order) for
> these resolutions to work. I have patches for this in my local tree but
> decided to wait until some contiguous memory allocator hits the tree.
> Also, the fb driver should sanity check the generic modes before adding
> them to the list. FOr example the IPU can't do 1920x1200@60. This code
> is missing currently.
>
> Sascha
>
>
> --
> Pengutronix e.K.                           |                             |
> Industrial Linux Solutions                 | http://www.pengutronix.de/  |
> Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
> Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
>
Sascha Hauer Dec. 15, 2010, 11:17 a.m. UTC | #8
On Tue, Dec 14, 2010 at 09:23:30PM +0800, Liu Ying wrote:
> 2010/12/14 Sascha Hauer <s.hauer@pengutronix.de>:
> > On Tue, Dec 14, 2010 at 02:40:09PM +0800, Liu Ying wrote:
> >> Hello, Sascha,
> >>
> >> Please find my feedback with [LY] embedded.
> >>
> >> And, a bug related with this patch:
> >> 1) Bootup the babbage board.
> >> 2) echo U:640x480p-60 > /sys/class/graphics/fb0/mode
> >> 3) cat /sys/class/graphics/fb0/virtual_size, it shows '640,480'.
> >> 4) echo 640,960 > /sys/class/graphics/fb0/virtual_size, change virtual
> >> yres to be 960.
> >> 5) cat /sys/class/graphics/fb0/virtual_size, it still shows '640,480'.
> >> I think this is caused by 'var->yres_virtual = var->yres;' in custom
> >> fb_set_par() function(Sorry, I cannot make the comment inline this
> >> time, as I don't find the original code within the mail I am
> >> replying). This makes fb0 use only one block of memory whose size is
> >> equal to screen size, so pan display can not really play her role and
> >> screen tearing issue may happen.
> 
> Sascha,
> 
> Just would like to know if you've caught this bug.

Yes, it will be fixed in the next series. It was the framebuffer driver
forcing yres_virtual to yres in set_par.

Sascha

> 
> >>
> >> Best Regards,
> >> Liu Ying
> >>
> >> 2010/12/13 Sascha Hauer <s.hauer@pengutronix.de>:
> >> > On Sun, Dec 12, 2010 at 02:13:40PM +0800, Liu Ying wrote:
> >> >> Hello, Sascha,
> >> >>
> >> >> I have following comments to this patch:
> >> >> 1) Please modify the commit message, as IPUv3 is not embedded in i.MX50 SoC.
> >> >
> >> > ok.
> >> >
> >> >> 2) ADC is not supported yet in the framebuffer driver, so please
> >> >> modify this comment:
> >> >>    > + * Framebuffer Framebuffer Driver for SDC and ADC.
> >> >
> >> > ok.
> >> >
> >> >> 3) 'ipu_dp_set_window_pos()' is called only once in
> >> >> imx_ipu_fb_set_par_overlay(). So, the framebuffer driver doesn't
> >> >> support to change the overlay framebuffer position. Need a
> >> >> mechanism/interface for users to change the overlay framebuffer
> >> >> position.
> >> >
> >> > Yes. The overlay support is currently only present in the driver because
> >> > I didn't want to break it in general. There is no generic overlay API in
> >> > the kernel and I didn't want to invent the n+1 API. Currently the
> >> > possible use cases of the overlay support are not clear to me. Number
> >> > one use case would probably be to display video output, but the
> >> > corresponding driver would be a v4l2 driver, so in this case we would
> >> > only need an in-kernel API.
> >> > Anyway, I have not the resources to implement complete overlay support.
> >> > So either we keep it like it is and it more has an example character
> >> > or we remove it completely from the driver for now.
> >> >
> >> >> 4) Need to make sure the framebuffer on DP-FG is blanked before the
> >> >> framebuffer on DP-BG is blanked. Meanwhile, the framebuffer on DP-FG
> >> >> should be unblanked after the framebuffer on DP-BG is unblanked
> >> >> 5) Need to check the framebuffer on DP-FG doesn't run out of the range
> >> >> of the framebuffer on DP-BG.
> >> >
> >> > I tend to remove the overlay support.
> >> >
> >> >> 6) I prefer to find the video mode in modedb first, and if we cannot
> >> >> find the video mode in common video mode data base, we can find a
> >> >> video mode in custom video mode data base which is defined in platform
> >> >> data. In this way, we don't need to export common modefb.
> >> >
> >> > I exported the modedb to be able to show the different modes under
> >> > /sys/class/graphics/fb0/modes and to set it with
> >> > /sys/class/graphics/fb0/mode. If you want this there is no way around
> >> > exporting it. The ioctl interface for mode setting is independent of the
> >> > specific modes anyway.
> >> [LY]  Just a concern. If a platform choose to add the generic video
> >> mode data base to IPUv3 framebuffer modelist, 'cat
> >> /sys/class/graphics/fb0/modes' will show all the video modes in the
> >> generic data base to users. I am not sure whether showing them to
> >> users means the IPUv3 framebuffer driver acknowledges to support all
> >> of them or not. I tried to do 'echo U:1800x1440p-70 >
> >> /sys/class/graphics/fb0/mode' with the kernel on ipuv3 branch, there
> >> will be a kernel dump(seems it can not allocate enough memory).
> >
> > We'll have to increase the dma coherent size (and max zone order) for
> > these resolutions to work. I have patches for this in my local tree but
> > decided to wait until some contiguous memory allocator hits the tree.
> > Also, the fb driver should sanity check the generic modes before adding
> > them to the list. FOr example the IPU can't do 1920x1200@60. This code
> > is missing currently.
> >
> > Sascha
> >
> >
> > --
> > Pengutronix e.K.                           |                             |
> > Industrial Linux Solutions                 | http://www.pengutronix.de/  |
> > Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
> > Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
> >
> 
> 
> 
> -- 
> Best Regards,
> Liu Ying
>

Patch
diff mbox series

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 27c1fb4..1901915 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2236,6 +2236,17 @@  config FB_MX3
 	  far only synchronous displays are supported. If you plan to use
 	  an LCD display with your i.MX31 system, say Y here.
 
+config FB_MX5
+	tristate "MX5 Framebuffer support"
+	depends on FB && MFD_IMX_IPU_V3
+	select FB_CFB_FILLRECT
+	select FB_CFB_COPYAREA
+	select FB_CFB_IMAGEBLIT
+	select FB_MODE_HELPERS
+	help
+	  This is a framebuffer device for the i.MX51 LCD Controller. If you
+	  plan to use an LCD display with your i.MX51 system, say Y here.
+
 config FB_BROADSHEET
 	tristate "E-Ink Broadsheet/Epson S1D13521 controller support"
 	depends on FB
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 485e8ed..ad408d2 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -145,6 +145,7 @@  obj-$(CONFIG_FB_BF54X_LQ043)	  += bf54x-lq043fb.o
 obj-$(CONFIG_FB_BFIN_LQ035Q1)     += bfin-lq035q1-fb.o
 obj-$(CONFIG_FB_BFIN_T350MCQB)	  += bfin-t350mcqb-fb.o
 obj-$(CONFIG_FB_MX3)		  += mx3fb.o
+obj-$(CONFIG_FB_MX5)		  += mx5fb.o
 obj-$(CONFIG_FB_DA8XX)		  += da8xx-fb.o
 
 # the test framebuffer is last
diff --git a/drivers/video/mx5fb.c b/drivers/video/mx5fb.c
new file mode 100644
index 0000000..fd9baf4
--- /dev/null
+++ b/drivers/video/mx5fb.c
@@ -0,0 +1,846 @@ 
+/*
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * Framebuffer Framebuffer Driver for SDC and ADC.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/fb.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/dma-mapping.h>
+#include <linux/console.h>
+#include <linux/mfd/imx-ipu-v3.h>
+#include <asm/uaccess.h>
+#include <mach/ipu-v3.h>
+
+#define DRIVER_NAME "imx-ipuv3-fb"
+
+struct imx_ipu_fb_info {
+	int			ipu_channel_num;
+	struct ipu_channel	*ipu_ch;
+	int			dc;
+	int			ipu_di;
+	u32			ipu_di_pix_fmt;
+	u32			ipu_in_pix_fmt;
+
+	u32			pseudo_palette[16];
+
+	struct ipu_dp		*dp;
+	struct dmfc_channel	*dmfc;
+	struct fb_info		*slave;
+	struct fb_info		*master;
+	bool			enabled;
+};
+
+static int imx_ipu_fb_set_fix(struct fb_info *info)
+{
+	struct fb_fix_screeninfo *fix = &info->fix;
+	struct fb_var_screeninfo *var = &info->var;
+
+	fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
+
+	fix->type = FB_TYPE_PACKED_PIXELS;
+	fix->accel = FB_ACCEL_NONE;
+	fix->visual = FB_VISUAL_TRUECOLOR;
+	fix->xpanstep = 1;
+	fix->ypanstep = 1;
+
+	return 0;
+}
+
+static int imx_ipu_fb_map_video_memory(struct fb_info *fbi)
+{
+	int size;
+
+	size = fbi->var.yres_virtual * fbi->fix.line_length;
+
+	if (fbi->screen_base) {
+		if (fbi->fix.smem_len >= size)
+			return 0;
+
+		dma_free_writecombine(fbi->device, fbi->fix.smem_len,
+			      fbi->screen_base, fbi->fix.smem_start);
+	}
+
+	fbi->screen_base = dma_alloc_writecombine(fbi->device,
+				size,
+				(dma_addr_t *)&fbi->fix.smem_start,
+				GFP_DMA);
+	if (fbi->screen_base == 0) {
+		dev_err(fbi->device, "Unable to allocate framebuffer memory (%d)\n",
+				fbi->fix.smem_len);
+		fbi->fix.smem_len = 0;
+		fbi->fix.smem_start = 0;
+		return -ENOMEM;
+	}
+
+	fbi->fix.smem_len = size;
+	fbi->screen_size = fbi->fix.smem_len;
+
+	dev_dbg(fbi->device, "allocated fb @ paddr=0x%08lx, size=%d\n",
+		fbi->fix.smem_start, fbi->fix.smem_len);
+
+	/* Clear the screen */
+	memset((char *)fbi->screen_base, 0, fbi->fix.smem_len);
+
+	return 0;
+}
+
+static void imx_ipu_fb_enable(struct fb_info *fbi)
+{
+	struct imx_ipu_fb_info *mxc_fbi = fbi->par;
+
+	if (mxc_fbi->enabled)
+		return;
+
+	ipu_di_enable(mxc_fbi->ipu_di);
+	ipu_dmfc_enable_channel(mxc_fbi->dmfc);
+	ipu_idmac_enable_channel(mxc_fbi->ipu_ch);
+	ipu_dc_enable_channel(mxc_fbi->dc);
+	ipu_dp_enable_channel(mxc_fbi->dp);
+	mxc_fbi->enabled = 1;
+}
+
+static void imx_ipu_fb_disable(struct fb_info *fbi)
+{
+	struct imx_ipu_fb_info *mxc_fbi = fbi->par;
+
+	if (!mxc_fbi->enabled)
+		return;
+
+	ipu_dp_disable_channel(mxc_fbi->dp);
+	ipu_dc_disable_channel(mxc_fbi->dc);
+	ipu_idmac_disable_channel(mxc_fbi->ipu_ch);
+	ipu_dmfc_disable_channel(mxc_fbi->dmfc);
+	ipu_di_disable(mxc_fbi->ipu_di);
+
+	mxc_fbi->enabled = 0;
+}
+
+static int calc_vref(struct fb_var_screeninfo *var)
+{
+	unsigned long htotal, vtotal;
+
+	htotal = var->xres + var->right_margin + var->hsync_len + var->left_margin;
+	vtotal = var->yres + var->lower_margin + var->vsync_len + var->upper_margin;
+
+	if (!htotal || !vtotal)
+		return 60;
+
+	return PICOS2KHZ(var->pixclock) * 1000 / vtotal / htotal;
+}
+
+static int calc_bandwidth(struct fb_var_screeninfo *var, unsigned int vref)
+{
+	return var->xres * var->yres * vref;
+}
+
+static int imx_ipu_fb_set_par(struct fb_info *fbi)
+{
+	int ret;
+	struct ipu_di_signal_cfg sig_cfg;
+	struct imx_ipu_fb_info *mxc_fbi = fbi->par;
+	u32 out_pixel_fmt;
+	int interlaced = 0;
+	struct fb_var_screeninfo *var = &fbi->var;
+	int enabled = mxc_fbi->enabled;
+
+	dev_dbg(fbi->device, "Reconfiguring framebuffer %dx%d-%d\n",
+		fbi->var.xres, fbi->var.yres, fbi->var.bits_per_pixel);
+
+	if (enabled)
+		imx_ipu_fb_disable(fbi);
+
+	fbi->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8;
+
+	var->yres_virtual = var->yres;
+
+	ret = imx_ipu_fb_map_video_memory(fbi);
+	if (ret)
+		return ret;
+
+	if (var->vmode & FB_VMODE_INTERLACED)
+		interlaced = 1;
+
+	memset(&sig_cfg, 0, sizeof(sig_cfg));
+	out_pixel_fmt = mxc_fbi->ipu_di_pix_fmt;
+
+	if (var->vmode & FB_VMODE_INTERLACED)
+		sig_cfg.interlaced = 1;
+	if (var->vmode & FB_VMODE_ODD_FLD_FIRST) /* PAL */
+		sig_cfg.odd_field_first = 1;
+	if (var->sync & FB_SYNC_EXT)
+		sig_cfg.ext_clk = 1;
+	if (var->sync & FB_SYNC_HOR_HIGH_ACT)
+		sig_cfg.Hsync_pol = 1;
+	if (var->sync & FB_SYNC_VERT_HIGH_ACT)
+		sig_cfg.Vsync_pol = 1;
+	if (!(var->sync & FB_SYNC_CLK_LAT_FALL))
+		sig_cfg.clk_pol = 1;
+	if (var->sync & FB_SYNC_DATA_INVERT)
+		sig_cfg.data_pol = 1;
+	if (!(var->sync & FB_SYNC_OE_LOW_ACT))
+		sig_cfg.enable_pol = 1;
+	if (var->sync & FB_SYNC_CLK_IDLE_EN)
+		sig_cfg.clkidle_en = 1;
+
+	dev_dbg(fbi->device, "pixclock = %lu.%03lu MHz\n",
+		PICOS2KHZ(var->pixclock) / 1000,
+		PICOS2KHZ(var->pixclock) % 1000);
+
+	sig_cfg.width = var->xres;
+	sig_cfg.height = var->yres;
+	sig_cfg.pixel_fmt = out_pixel_fmt;
+	sig_cfg.h_start_width = var->left_margin;
+	sig_cfg.h_sync_width = var->hsync_len;
+	sig_cfg.h_end_width = var->right_margin;
+	sig_cfg.v_start_width = var->upper_margin;
+	sig_cfg.v_sync_width = var->vsync_len;
+	sig_cfg.v_end_width = var->lower_margin;
+	sig_cfg.v_to_h_sync = 0;
+
+	if (mxc_fbi->dp) {
+		ret = ipu_dp_setup_channel(mxc_fbi->dp, mxc_fbi->ipu_in_pix_fmt,
+				out_pixel_fmt, 1);
+		if (ret) {
+			dev_dbg(fbi->device, "initializing display processor failed with %d\n",
+				ret);
+			return ret;
+		}
+	}
+
+	ret = ipu_dc_init_sync(mxc_fbi->dc, mxc_fbi->ipu_di, interlaced,
+			out_pixel_fmt, fbi->var.xres);
+	if (ret) {
+		dev_dbg(fbi->device, "initializing display controller failed with %d\n",
+				ret);
+		return ret;
+	}
+
+	ret = ipu_di_init_sync_panel(mxc_fbi->ipu_di,
+				PICOS2KHZ(var->pixclock) * 1000UL,
+				&sig_cfg);
+	if (ret) {
+		dev_dbg(fbi->device, "initializing panel failed with %d\n",
+				ret);
+		return ret;
+	}
+
+	fbi->mode = (struct fb_videomode *)fb_match_mode(var, &fbi->modelist);
+	var->xoffset = var->yoffset = 0;
+
+	if (fbi->var.vmode & FB_VMODE_INTERLACED)
+		interlaced = 1;
+
+	ret = ipu_idmac_init_channel_buffer(mxc_fbi->ipu_ch,
+					mxc_fbi->ipu_in_pix_fmt,
+					var->xres, var->yres,
+					fbi->fix.line_length,
+					IPU_ROTATE_NONE,
+					fbi->fix.smem_start,
+					0,
+					0, 0, interlaced);
+	if (ret) {
+		dev_dbg(fbi->device, "init channel buffer failed with %d\n",
+				ret);
+		return ret;
+	}
+
+	ret = ipu_dmfc_init_channel(mxc_fbi->dmfc, var->xres);
+	if (ret) {
+		dev_dbg(fbi->device, "initializing dmfc channel failed with %d\n",
+				ret);
+		return ret;
+	}
+
+	ret = ipu_dmfc_alloc_bandwidth(mxc_fbi->dmfc, calc_bandwidth(var, calc_vref(var)));
+	if (ret) {
+		dev_dbg(fbi->device, "allocating dmfc bandwidth failed with %d\n",
+				ret);
+		return ret;
+	}
+
+	if (enabled)
+		imx_ipu_fb_enable(fbi);
+
+	return ret;
+}
+
+/*
+ * These are the bitfields for each
+ * display depth that we support.
+ */
+struct imxfb_rgb {
+	struct fb_bitfield	red;
+	struct fb_bitfield	green;
+	struct fb_bitfield	blue;
+	struct fb_bitfield	transp;
+};
+
+static struct imxfb_rgb def_rgb_8 = {
+	.red	= { .offset =  5, .length = 3, },
+	.green	= { .offset =  2, .length = 3, },
+	.blue	= { .offset =  0, .length = 2, },
+	.transp = { .offset =  0, .length = 0, },
+};
+
+static struct imxfb_rgb def_rgb_16 = {
+	.red	= { .offset = 11, .length = 5, },
+	.green	= { .offset =  5, .length = 6, },
+	.blue	= { .offset =  0, .length = 5, },
+	.transp = { .offset =  0, .length = 0, },
+};
+
+static struct imxfb_rgb def_rgb_24 = {
+	.red	= { .offset = 16, .length = 8, },
+	.green	= { .offset =  8, .length = 8, },
+	.blue	= { .offset =  0, .length = 8, },
+	.transp = { .offset =  0, .length = 0, },
+};
+
+static struct imxfb_rgb def_rgb_32 = {
+	.red	= { .offset = 16, .length = 8, },
+	.green	= { .offset =  8, .length = 8, },
+	.blue	= { .offset =  0, .length = 8, },
+	.transp = { .offset = 24, .length = 8, },
+};
+
+/*
+ * Check framebuffer variable parameters and adjust to valid values.
+ *
+ * @param       var      framebuffer variable parameters
+ *
+ * @param       info     framebuffer information pointer
+ */
+static int imx_ipu_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+	struct imx_ipu_fb_info *mxc_fbi = info->par;
+	struct imxfb_rgb *rgb;
+
+	/* we don't support xpan, force xres_virtual to be equal to xres */
+	var->xres_virtual = var->xres;
+
+	if (var->yres_virtual < var->yres)
+		var->yres_virtual = var->yres;
+
+	switch (var->bits_per_pixel) {
+	case 8:
+		rgb = &def_rgb_8;
+		break;
+	case 16:
+		rgb = &def_rgb_16;
+		mxc_fbi->ipu_in_pix_fmt = IPU_PIX_FMT_RGB565;
+		break;
+	case 24:
+		rgb = &def_rgb_24;
+		mxc_fbi->ipu_in_pix_fmt = IPU_PIX_FMT_BGR24;
+		break;
+	case 32:
+		rgb = &def_rgb_32;
+		mxc_fbi->ipu_in_pix_fmt = IPU_PIX_FMT_BGR32;
+		break;
+	default:
+		var->bits_per_pixel = 24;
+		rgb = &def_rgb_24;
+		mxc_fbi->ipu_in_pix_fmt = IPU_PIX_FMT_BGR24;
+	}
+
+	var->red    = rgb->red;
+	var->green  = rgb->green;
+	var->blue   = rgb->blue;
+	var->transp = rgb->transp;
+
+	return 0;
+}
+
+static inline unsigned int chan_to_field(u_int chan, struct fb_bitfield *bf)
+{
+	chan &= 0xffff;
+	chan >>= 16 - bf->length;
+	return chan << bf->offset;
+}
+
+static int imx_ipu_fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+			   u_int trans, struct fb_info *fbi)
+{
+	unsigned int val;
+	int ret = 1;
+
+	/*
+	 * If greyscale is true, then we convert the RGB value
+	 * to greyscale no matter what visual we are using.
+	 */
+	if (fbi->var.grayscale)
+		red = green = blue = (19595 * red + 38470 * green +
+				      7471 * blue) >> 16;
+	switch (fbi->fix.visual) {
+	case FB_VISUAL_TRUECOLOR:
+		/*
+		 * 16-bit True Colour.  We encode the RGB value
+		 * according to the RGB bitfield information.
+		 */
+		if (regno < 16) {
+			u32 *pal = fbi->pseudo_palette;
+
+			val = chan_to_field(red, &fbi->var.red);
+			val |= chan_to_field(green, &fbi->var.green);
+			val |= chan_to_field(blue, &fbi->var.blue);
+
+			pal[regno] = val;
+			ret = 0;
+		}
+		break;
+
+	case FB_VISUAL_STATIC_PSEUDOCOLOR:
+	case FB_VISUAL_PSEUDOCOLOR:
+		break;
+	}
+
+	return ret;
+}
+
+static int imx_ipu_fb_blank(int blank, struct fb_info *info)
+{
+	dev_dbg(info->device, "blank = %d\n", blank);
+
+	switch (blank) {
+	case FB_BLANK_POWERDOWN:
+	case FB_BLANK_VSYNC_SUSPEND:
+	case FB_BLANK_HSYNC_SUSPEND:
+	case FB_BLANK_NORMAL:
+		imx_ipu_fb_disable(info);
+		break;
+	case FB_BLANK_UNBLANK:
+		imx_ipu_fb_enable(info);
+		break;
+	}
+
+	return 0;
+}
+
+static int imx_ipu_fb_pan_display(struct fb_var_screeninfo *var,
+		struct fb_info *info)
+{
+	struct imx_ipu_fb_info *mxc_fbi = info->par;
+	unsigned long base;
+	int ret;
+
+	if (info->var.yoffset == var->yoffset)
+		return 0;	/* No change, do nothing */
+
+	base = var->yoffset * var->xres_virtual * var->bits_per_pixel / 8;
+	base += info->fix.smem_start;
+
+	ret = ipu_wait_for_interrupt(IPU_IRQ_EOF(mxc_fbi->ipu_channel_num), 100);
+	if (ret)
+		return ret;
+
+	if (ipu_idmac_update_channel_buffer(mxc_fbi->ipu_ch, 0, base)) {
+		dev_err(info->device,
+			"Error updating SDC buf to address=0x%08lX\n", base);
+	}
+
+	info->var.yoffset = var->yoffset;
+
+	return 0;
+}
+
+static struct fb_ops imx_ipu_fb_ops = {
+	.owner		= THIS_MODULE,
+	.fb_set_par	= imx_ipu_fb_set_par,
+	.fb_check_var	= imx_ipu_fb_check_var,
+	.fb_setcolreg	= imx_ipu_fb_setcolreg,
+	.fb_pan_display	= imx_ipu_fb_pan_display,
+	.fb_fillrect	= cfb_fillrect,
+	.fb_copyarea	= cfb_copyarea,
+	.fb_imageblit	= cfb_imageblit,
+	.fb_blank	= imx_ipu_fb_blank,
+};
+
+/*
+ * Overlay functions
+ */
+static int imx_ipu_fb_enable_overlay(struct fb_info *fbi)
+{
+	struct imx_ipu_fb_info *mxc_fbi = fbi->par;
+
+	ipu_dmfc_enable_channel(mxc_fbi->dmfc);
+	ipu_idmac_enable_channel(mxc_fbi->ipu_ch);
+	ipu_dp_enable_fg(mxc_fbi->dp);
+
+	return 0;
+}
+
+static int imx_ipu_fb_disable_overlay(struct fb_info *fbi)
+{
+	struct imx_ipu_fb_info *mxc_fbi = fbi->par;
+
+	ipu_dp_disable_fg(mxc_fbi->dp);
+	ipu_idmac_disable_channel(mxc_fbi->ipu_ch);
+	ipu_dmfc_disable_channel(mxc_fbi->dmfc);
+
+	return 0;
+}
+
+static int imx_ipu_fb_set_par_overlay(struct fb_info *fbi)
+{
+	struct imx_ipu_fb_info *mxc_fbi = fbi->par;
+	struct fb_var_screeninfo *var = &fbi->var;
+	struct fb_info *fbi_master = mxc_fbi->master;
+	struct fb_var_screeninfo *var_master = &fbi_master->var;
+	int ret;
+	int interlaced = 0;
+	int enabled = mxc_fbi->enabled;
+
+	dev_dbg(fbi->device, "Reconfiguring framebuffer %dx%d-%d\n",
+		fbi->var.xres, fbi->var.yres, fbi->var.bits_per_pixel);
+
+	if (enabled)
+		imx_ipu_fb_disable_overlay(fbi);
+
+	fbi->fix.line_length = var->xres_virtual *
+                                  var->bits_per_pixel / 8;
+
+	ret = imx_ipu_fb_map_video_memory(fbi);
+	if (ret)
+		return ret;
+
+	ipu_dp_set_window_pos(mxc_fbi->dp, 64, 64);
+
+	var->xoffset = var->yoffset = 0;
+
+	if (var->vmode & FB_VMODE_INTERLACED)
+		interlaced = 1;
+
+	ret = ipu_idmac_init_channel_buffer(mxc_fbi->ipu_ch,
+					mxc_fbi->ipu_in_pix_fmt,
+					var->xres, var->yres,
+					fbi->fix.line_length,
+					IPU_ROTATE_NONE,
+					fbi->fix.smem_start,
+					0,
+					0, 0, interlaced);
+	if (ret) {
+		dev_dbg(fbi->device, "init channel buffer failed with %d\n",
+				ret);
+		return ret;
+	}
+
+	ret = ipu_dmfc_init_channel(mxc_fbi->dmfc, var->xres);
+	if (ret) {
+		dev_dbg(fbi->device, "initializing dmfc channel failed with %d\n",
+				ret);
+		return ret;
+	}
+
+	ret = ipu_dmfc_alloc_bandwidth(mxc_fbi->dmfc, calc_bandwidth(var, calc_vref(var_master)));
+	if (ret) {
+		dev_dbg(fbi->device, "allocating dmfc bandwidth failed with %d\n",
+				ret);
+		return ret;
+	}
+
+	if (enabled)
+		imx_ipu_fb_enable_overlay(fbi);
+
+	return ret;
+}
+
+static int imx_ipu_fb_blank_overlay(int blank, struct fb_info *fbi)
+{
+	dev_dbg(fbi->device, "blank = %d\n", blank);
+
+	switch (blank) {
+	case FB_BLANK_POWERDOWN:
+	case FB_BLANK_VSYNC_SUSPEND:
+	case FB_BLANK_HSYNC_SUSPEND:
+	case FB_BLANK_NORMAL:
+		imx_ipu_fb_disable_overlay(fbi);
+		break;
+	case FB_BLANK_UNBLANK:
+		imx_ipu_fb_enable_overlay(fbi);
+		break;
+	}
+
+	return 0;
+}
+
+static struct fb_ops imx_ipu_fb_overlay_ops = {
+	.owner		= THIS_MODULE,
+	.fb_set_par	= imx_ipu_fb_set_par_overlay,
+	.fb_check_var	= imx_ipu_fb_check_var,
+	.fb_setcolreg	= imx_ipu_fb_setcolreg,
+	.fb_pan_display	= imx_ipu_fb_pan_display,
+	.fb_fillrect	= cfb_fillrect,
+	.fb_copyarea	= cfb_copyarea,
+	.fb_imageblit	= cfb_imageblit,
+	.fb_blank	= imx_ipu_fb_blank_overlay,
+};
+
+static struct fb_info *imx_ipu_fb_init_fbinfo(struct device *dev, struct fb_ops *ops)
+{
+	struct fb_info *fbi;
+	struct imx_ipu_fb_info *mxc_fbi;
+
+	fbi = framebuffer_alloc(sizeof(struct imx_ipu_fb_info), dev);
+	if (!fbi)
+		return NULL;
+
+	BUG_ON(fbi->par == NULL);
+	mxc_fbi = fbi->par;
+
+	fbi->var.activate = FB_ACTIVATE_NOW;
+
+	fbi->fbops = ops;
+	fbi->flags = FBINFO_FLAG_DEFAULT;
+	fbi->pseudo_palette = mxc_fbi->pseudo_palette;
+
+	fb_alloc_cmap(&fbi->cmap, 16, 0);
+
+	return fbi;
+}
+
+static int imx_ipu_fb_init_overlay(struct platform_device *pdev,
+		struct fb_info *fbi_master, int ipu_channel)
+{
+	struct imx_ipu_fb_info *mxc_fbi_master = fbi_master->par;
+	struct fb_info *ovlfbi;
+	struct imx_ipu_fb_info *ovl_mxc_fbi;
+	int ret;
+
+	ovlfbi = imx_ipu_fb_init_fbinfo(&pdev->dev, &imx_ipu_fb_overlay_ops);
+	if (!ovlfbi)
+		return -ENOMEM;
+
+	ovl_mxc_fbi = ovlfbi->par;
+	ovl_mxc_fbi->ipu_ch = ipu_idmac_get(ipu_channel);
+	ovl_mxc_fbi->dmfc = ipu_dmfc_get(ipu_channel);
+	ovl_mxc_fbi->ipu_di = -1;
+	ovl_mxc_fbi->dp = mxc_fbi_master->dp;
+	ovl_mxc_fbi->master = fbi_master;
+	mxc_fbi_master->slave = ovlfbi;
+
+	ovlfbi->var.xres = 240;
+	ovlfbi->var.yres = 320;
+	ovlfbi->var.yres_virtual = ovlfbi->var.yres;
+	ovlfbi->var.xres_virtual = ovlfbi->var.xres;
+	imx_ipu_fb_check_var(&ovlfbi->var, ovlfbi);
+	imx_ipu_fb_set_fix(ovlfbi);
+
+	ret = register_framebuffer(ovlfbi);
+	if (ret) {
+		framebuffer_release(ovlfbi);
+		return ret;
+	}
+
+	ipu_dp_set_global_alpha(ovl_mxc_fbi->dp, 1, 0x80, 1);
+	ipu_dp_set_color_key(ovl_mxc_fbi->dp, 0, 0);
+
+	imx_ipu_fb_set_par_overlay(ovlfbi);
+
+	return 0;
+}
+
+static void imx_ipu_fb_exit_overlay(struct platform_device *pdev,
+		struct fb_info *fbi_master, int ipu_channel)
+{
+	struct imx_ipu_fb_info *mxc_fbi_master = fbi_master->par;
+	struct fb_info *ovlfbi = mxc_fbi_master->slave;
+	struct imx_ipu_fb_info *ovl_mxc_fbi = ovlfbi->par;
+
+	imx_ipu_fb_blank_overlay(FB_BLANK_POWERDOWN, ovlfbi);
+
+	unregister_framebuffer(ovlfbi);
+
+	ipu_idmac_put(ovl_mxc_fbi->ipu_ch);
+	ipu_dmfc_free_bandwidth(ovl_mxc_fbi->dmfc);
+	ipu_dmfc_put(ovl_mxc_fbi->dmfc);
+
+	framebuffer_release(ovlfbi);
+}
+
+static int imx_ipu_fb_find_mode(struct fb_info *fbi)
+{
+	int ret;
+	struct fb_videomode *mode_array;
+	struct fb_modelist *modelist;
+	struct fb_var_screeninfo *var = &fbi->var;
+	int i = 0;
+
+	list_for_each_entry(modelist, &fbi->modelist, list)
+		i++;
+
+	mode_array = kmalloc(sizeof (struct fb_modelist) * i, GFP_KERNEL);
+	if (!mode_array)
+		return -ENOMEM;
+
+	i = 0;
+	list_for_each_entry(modelist, &fbi->modelist, list)
+		mode_array[i++] = modelist->mode;
+
+	ret = fb_find_mode(&fbi->var, fbi, NULL, mode_array, i, NULL, 16);
+	if (ret == 0)
+		return -EINVAL;
+
+	dev_dbg(fbi->device, "found %dx%d-%d hs:%d:%d:%d vs:%d:%d:%d\n",
+			var->xres, var->yres, var->bits_per_pixel,
+			var->hsync_len, var->left_margin, var->right_margin,
+			var->vsync_len, var->upper_margin, var->lower_margin);
+
+	kfree(mode_array);
+
+	return 0;
+}
+
+static int __devinit imx_ipu_fb_probe(struct platform_device *pdev)
+{
+	struct fb_info *fbi;
+	struct imx_ipu_fb_info *mxc_fbi;
+	struct ipuv3_fb_platform_data *plat_data = pdev->dev.platform_data;
+	int ret = 0, i;
+
+	pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+
+	fbi = imx_ipu_fb_init_fbinfo(&pdev->dev, &imx_ipu_fb_ops);
+	if (!fbi)
+		return -ENOMEM;
+
+	mxc_fbi = fbi->par;
+
+	mxc_fbi->ipu_channel_num = plat_data->ipu_channel_bg;
+	mxc_fbi->dc = plat_data->dc_channel;
+	mxc_fbi->ipu_di_pix_fmt = plat_data->interface_pix_fmt;
+	mxc_fbi->ipu_di = pdev->id;
+
+	mxc_fbi->ipu_ch = ipu_idmac_get(plat_data->ipu_channel_bg);
+	if (IS_ERR(mxc_fbi->ipu_ch)) {
+		ret = PTR_ERR(mxc_fbi->ipu_ch);
+		goto failed_request_ipu;
+	}
+
+	mxc_fbi->dmfc = ipu_dmfc_get(plat_data->ipu_channel_bg);
+	if (IS_ERR(mxc_fbi->ipu_ch)) {
+		ret = PTR_ERR(mxc_fbi->ipu_ch);
+		goto failed_request_dmfc;
+	}
+
+	if (plat_data->dp_channel >= 0) {
+		mxc_fbi->dp = ipu_dp_get(plat_data->dp_channel);
+		if (IS_ERR(mxc_fbi->dp)) {
+			ret = PTR_ERR(mxc_fbi->ipu_ch);
+			goto failed_request_dp;
+		}
+	}
+
+	fbi->var.yres_virtual = fbi->var.yres;
+
+	INIT_LIST_HEAD(&fbi->modelist);
+	for (i = 0; i < plat_data->num_modes; i++)
+		fb_add_videomode(&plat_data->modes[i], &fbi->modelist);
+
+	if (plat_data->flags & IMX_IPU_FB_USE_MODEDB) {
+		for (i = 0; i < num_fb_modes; i++)
+			fb_add_videomode(&fb_modes[i], &fbi->modelist);
+	}
+
+	imx_ipu_fb_find_mode(fbi);
+
+	imx_ipu_fb_check_var(&fbi->var, fbi);
+	imx_ipu_fb_set_fix(fbi);
+	ret = register_framebuffer(fbi);
+	if (ret < 0)
+		goto failed_register;
+
+	imx_ipu_fb_set_par(fbi);
+	imx_ipu_fb_blank(FB_BLANK_UNBLANK, fbi);
+
+	if (plat_data->ipu_channel_fg >= 0 && plat_data->flags & IMX_IPU_FB_USE_OVERLAY)
+		imx_ipu_fb_init_overlay(pdev, fbi, plat_data->ipu_channel_fg);
+
+	platform_set_drvdata(pdev, fbi);
+
+	return 0;
+
+failed_register:
+	if (plat_data->dp_channel >= 0)
+		ipu_dp_put(mxc_fbi->dp);
+failed_request_dp:
+	ipu_dmfc_put(mxc_fbi->dmfc);
+failed_request_dmfc:
+	ipu_idmac_put(mxc_fbi->ipu_ch);
+failed_request_ipu:
+	fb_dealloc_cmap(&fbi->cmap);
+	framebuffer_release(fbi);
+
+	return ret;
+}
+
+static int __devexit imx_ipu_fb_remove(struct platform_device *pdev)
+{
+	struct fb_info *fbi = platform_get_drvdata(pdev);
+	struct imx_ipu_fb_info *mxc_fbi = fbi->par;
+	struct ipuv3_fb_platform_data *plat_data = pdev->dev.platform_data;
+
+	if (plat_data->ipu_channel_fg >= 0 && plat_data->flags & IMX_IPU_FB_USE_OVERLAY)
+		imx_ipu_fb_exit_overlay(pdev, fbi, plat_data->ipu_channel_fg);
+
+	imx_ipu_fb_blank(FB_BLANK_POWERDOWN, fbi);
+
+	dma_free_writecombine(fbi->device, fbi->fix.smem_len,
+			      fbi->screen_base, fbi->fix.smem_start);
+
+	if (&fbi->cmap)
+		fb_dealloc_cmap(&fbi->cmap);
+
+	unregister_framebuffer(fbi);
+
+	if (plat_data->dp_channel >= 0)
+		ipu_dp_put(mxc_fbi->dp);
+	ipu_dmfc_free_bandwidth(mxc_fbi->dmfc);
+	ipu_dmfc_put(mxc_fbi->dmfc);
+	ipu_idmac_put(mxc_fbi->ipu_ch);
+
+	framebuffer_release(fbi);
+
+	return 0;
+}
+
+static struct platform_driver imx_ipu_fb_driver = {
+	.driver = {
+		.name = DRIVER_NAME,
+	},
+	.probe = imx_ipu_fb_probe,
+	.remove = __devexit_p(imx_ipu_fb_remove),
+};
+
+static int __init imx_ipu_fb_init(void)
+{
+	return platform_driver_register(&imx_ipu_fb_driver);
+}
+
+static void __exit imx_ipu_fb_exit(void)
+{
+	platform_driver_unregister(&imx_ipu_fb_driver);
+}
+
+module_init(imx_ipu_fb_init);
+module_exit(imx_ipu_fb_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("i.MX framebuffer driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("fb");