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@xxxxxxxxxxxxxx>: > 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@xxxxxxxxxxxxxx> > --- > 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@xxxxxxxxxxxxxxx > 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-fbdev" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html