Logo Search packages:      
Sourcecode: linux version File versions  Download package

mbxfb.c

/*
 *  linux/drivers/video/mbx/mbxfb.c
 *
 *  Copyright (C) 2006-2007 8D Technologies inc
 *  Raphael Assenat <raph@8d.com>
 *    - Added video overlay support
 *    - Various improvements
 *
 *  Copyright (C) 2006 Compulab, Ltd.
 *  Mike Rapoport <mike@compulab.co.il>
 *    - Creation of driver
 *
 *   Based on pxafb.c
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file COPYING in the main directory of this archive for
 * more details.
 *
 *   Intel 2700G (Marathon) Graphics Accelerator Frame Buffer Driver
 *
 */

#include <linux/delay.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/uaccess.h>

#include <asm/io.h>

#include <video/mbxfb.h>

#include "regs.h"
#include "reg_bits.h"

static unsigned long virt_base_2700;

#define write_reg(val, reg) do { writel((val), (reg)); } while(0)

/* Without this delay, the graphics appears somehow scaled and
 * there is a lot of jitter in scanlines. This delay is probably
 * needed only after setting some specific register(s) somewhere,
 * not all over the place... */
#define write_reg_dly(val, reg) do { writel((val), reg); udelay(1000); } while(0)

#define MIN_XRES  16
#define MIN_YRES  16
#define MAX_XRES  2048
#define MAX_YRES  2048

#define MAX_PALETTES    16

/* FIXME: take care of different chip revisions with different sizes
   of ODFB */
#define MEMORY_OFFSET   0x60000

struct mbxfb_info {
      struct device *dev;

      struct resource *fb_res;
      struct resource *fb_req;

      struct resource *reg_res;
      struct resource *reg_req;

      void __iomem *fb_virt_addr;
      unsigned long fb_phys_addr;

      void __iomem *reg_virt_addr;
      unsigned long reg_phys_addr;

      int (*platform_probe) (struct fb_info * fb);
      int (*platform_remove) (struct fb_info * fb);

      u32 pseudo_palette[MAX_PALETTES];
#ifdef CONFIG_FB_MBX_DEBUG
      void *debugfs_data;
#endif

};

static struct fb_var_screeninfo mbxfb_default __devinitdata = {
      .xres = 640,
      .yres = 480,
      .xres_virtual = 640,
      .yres_virtual = 480,
      .bits_per_pixel = 16,
      .red = {11, 5, 0},
      .green = {5, 6, 0},
      .blue = {0, 5, 0},
      .activate = FB_ACTIVATE_TEST,
      .height = -1,
      .width = -1,
      .pixclock = 40000,
      .left_margin = 48,
      .right_margin = 16,
      .upper_margin = 33,
      .lower_margin = 10,
      .hsync_len = 96,
      .vsync_len = 2,
      .vmode = FB_VMODE_NONINTERLACED,
      .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
};

static struct fb_fix_screeninfo mbxfb_fix  __devinitdata = {
      .id = "MBX",
      .type = FB_TYPE_PACKED_PIXELS,
      .visual = FB_VISUAL_TRUECOLOR,
      .xpanstep = 0,
      .ypanstep = 0,
      .ywrapstep = 0,
      .accel = FB_ACCEL_NONE,
};

struct pixclock_div {
      u8 m;
      u8 n;
      u8 p;
};

static unsigned int mbxfb_get_pixclock(unsigned int pixclock_ps,
                               struct pixclock_div *div)
{
      u8 m, n, p;
      unsigned int err = 0;
      unsigned int min_err = ~0x0;
      unsigned int clk;
      unsigned int best_clk = 0;
      unsigned int ref_clk = 13000; /* FIXME: take from platform data */
      unsigned int pixclock;

      /* convert pixclock to KHz */
      pixclock = PICOS2KHZ(pixclock_ps);

      /* PLL output freq = (ref_clk * M) / (N * 2^P)
       *
       * M: 1 to 63
       * N: 1 to 7
       * P: 0 to 7
       */

      /* RAPH: When N==1, the resulting pixel clock appears to
       * get divided by 2. Preventing N=1 by starting the following
       * loop at 2 prevents this. Is this a bug with my chip
       * revision or something I dont understand? */
      for (m = 1; m < 64; m++) {
            for (n = 2; n < 8; n++) {
                  for (p = 0; p < 8; p++) {
                        clk = (ref_clk * m) / (n * (1 << p));
                        err = (clk > pixclock) ? (clk - pixclock) :
                              (pixclock - clk);
                        if (err < min_err) {
                              min_err = err;
                              best_clk = clk;
                              div->m = m;
                              div->n = n;
                              div->p = p;
                        }
                  }
            }
      }
      return KHZ2PICOS(best_clk);
}

static int mbxfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
                     u_int trans, struct fb_info *info)
{
      u32 val, ret = 1;

      if (regno < MAX_PALETTES) {
            u32 *pal = info->pseudo_palette;

            val = (red & 0xf800) | ((green & 0xfc00) >> 5) |
                  ((blue & 0xf800) >> 11);
            pal[regno] = val;
            ret = 0;
      }

      return ret;
}

static int mbxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
      struct pixclock_div div;

      var->pixclock = mbxfb_get_pixclock(var->pixclock, &div);

      if (var->xres < MIN_XRES)
            var->xres = MIN_XRES;
      if (var->yres < MIN_YRES)
            var->yres = MIN_YRES;
      if (var->xres > MAX_XRES)
            return -EINVAL;
      if (var->yres > MAX_YRES)
            return -EINVAL;
      var->xres_virtual = max(var->xres_virtual, var->xres);
      var->yres_virtual = max(var->yres_virtual, var->yres);

      switch (var->bits_per_pixel) {
            /* 8 bits-per-pixel is not supported yet */
      case 8:
            return -EINVAL;
      case 16:
            var->green.length = (var->green.length == 5) ? 5 : 6;
            var->red.length = 5;
            var->blue.length = 5;
            var->transp.length = 6 - var->green.length;
            var->blue.offset = 0;
            var->green.offset = 5;
            var->red.offset = 5 + var->green.length;
            var->transp.offset = (5 + var->red.offset) & 15;
            break;
      case 24:          /* RGB 888   */
      case 32:          /* RGBA 8888 */
            var->red.offset = 16;
            var->red.length = 8;
            var->green.offset = 8;
            var->green.length = 8;
            var->blue.offset = 0;
            var->blue.length = 8;
            var->transp.length = var->bits_per_pixel - 24;
            var->transp.offset = (var->transp.length) ? 24 : 0;
            break;
      }
      var->red.msb_right = 0;
      var->green.msb_right = 0;
      var->blue.msb_right = 0;
      var->transp.msb_right = 0;

      return 0;
}

static int mbxfb_set_par(struct fb_info *info)
{
      struct fb_var_screeninfo *var = &info->var;
      struct pixclock_div div;
      ushort hbps, ht, hfps, has;
      ushort vbps, vt, vfps, vas;
      u32 gsctrl = readl(GSCTRL);
      u32 gsadr = readl(GSADR);

      info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8;

      /* setup color mode */
      gsctrl &= ~(FMsk(GSCTRL_GPIXFMT));
      /* FIXME: add *WORKING* support for 8-bits per color */
      if (info->var.bits_per_pixel == 8) {
            return -EINVAL;
      } else {
            fb_dealloc_cmap(&info->cmap);
            gsctrl &= ~GSCTRL_LUT_EN;

            info->fix.visual = FB_VISUAL_TRUECOLOR;
            switch (info->var.bits_per_pixel) {
            case 16:
                  if (info->var.green.length == 5)
                        gsctrl |= GSCTRL_GPIXFMT_ARGB1555;
                  else
                        gsctrl |= GSCTRL_GPIXFMT_RGB565;
                  break;
            case 24:
                  gsctrl |= GSCTRL_GPIXFMT_RGB888;
                  break;
            case 32:
                  gsctrl |= GSCTRL_GPIXFMT_ARGB8888;
                  break;
            }
      }

      /* setup resolution */
      gsctrl &= ~(FMsk(GSCTRL_GSWIDTH) | FMsk(GSCTRL_GSHEIGHT));
      gsctrl |= Gsctrl_Width(info->var.xres) |
            Gsctrl_Height(info->var.yres);
      write_reg_dly(gsctrl, GSCTRL);

      gsadr &= ~(FMsk(GSADR_SRCSTRIDE));
      gsadr |= Gsadr_Srcstride(info->var.xres * info->var.bits_per_pixel /
                         (8 * 16) - 1);
      write_reg_dly(gsadr, GSADR);

      /* setup timings */
      var->pixclock = mbxfb_get_pixclock(info->var.pixclock, &div);

      write_reg_dly((Disp_Pll_M(div.m) | Disp_Pll_N(div.n) |
            Disp_Pll_P(div.p) | DISP_PLL_EN), DISPPLL);

      hbps = var->hsync_len;
      has = hbps + var->left_margin;
      hfps = has + var->xres;
      ht = hfps + var->right_margin;

      vbps = var->vsync_len;
      vas = vbps + var->upper_margin;
      vfps = vas + var->yres;
      vt = vfps + var->lower_margin;

      write_reg_dly((Dht01_Hbps(hbps) | Dht01_Ht(ht)), DHT01);
      write_reg_dly((Dht02_Hlbs(has) | Dht02_Has(has)), DHT02);
      write_reg_dly((Dht03_Hfps(hfps) | Dht03_Hrbs(hfps)), DHT03);
      write_reg_dly((Dhdet_Hdes(has) | Dhdet_Hdef(hfps)), DHDET);

      write_reg_dly((Dvt01_Vbps(vbps) | Dvt01_Vt(vt)), DVT01);
      write_reg_dly((Dvt02_Vtbs(vas) | Dvt02_Vas(vas)), DVT02);
      write_reg_dly((Dvt03_Vfps(vfps) | Dvt03_Vbbs(vfps)), DVT03);
      write_reg_dly((Dvdet_Vdes(vas) | Dvdet_Vdef(vfps)), DVDET);
      write_reg_dly((Dvectrl_Vevent(vfps) | Dvectrl_Vfetch(vbps)), DVECTRL);

      write_reg_dly((readl(DSCTRL) | DSCTRL_SYNCGEN_EN), DSCTRL);

      write_reg_dly(DINTRE_VEVENT0_EN, DINTRE);

      return 0;
}

static int mbxfb_blank(int blank, struct fb_info *info)
{
      switch (blank) {
      case FB_BLANK_POWERDOWN:
      case FB_BLANK_VSYNC_SUSPEND:
      case FB_BLANK_HSYNC_SUSPEND:
      case FB_BLANK_NORMAL:
            write_reg_dly((readl(DSCTRL) & ~DSCTRL_SYNCGEN_EN), DSCTRL);
            write_reg_dly((readl(PIXCLK) & ~PIXCLK_EN), PIXCLK);
            write_reg_dly((readl(VOVRCLK) & ~VOVRCLK_EN), VOVRCLK);
            break;
      case FB_BLANK_UNBLANK:
            write_reg_dly((readl(DSCTRL) | DSCTRL_SYNCGEN_EN), DSCTRL);
            write_reg_dly((readl(PIXCLK) | PIXCLK_EN), PIXCLK);
            break;
      }
      return 0;
}

static int mbxfb_setupOverlay(struct mbxfb_overlaySetup *set)
{
      u32 vsctrl, vscadr, vsadr;
      u32 sssize, spoctrl, shctrl;
      u32 vubase, vvbase;
      u32 vovrclk;

      if (set->scaled_width==0 || set->scaled_height==0)
            return -EINVAL;

      /* read registers which have reserved bits
       * so we can write them back as-is. */
      vovrclk = readl(VOVRCLK);
      vsctrl = readl(VSCTRL);
      vscadr = readl(VSCADR);
      vubase = readl(VUBASE);
      vvbase = readl(VVBASE);
      shctrl = readl(SHCTRL);

      spoctrl = readl(SPOCTRL);
      sssize = readl(SSSIZE);

      vsctrl &= ~(      FMsk(VSCTRL_VSWIDTH) |
                              FMsk(VSCTRL_VSHEIGHT) |
                              FMsk(VSCTRL_VPIXFMT) |
                              VSCTRL_GAMMA_EN | VSCTRL_CSC_EN |
                              VSCTRL_COSITED );
      vsctrl |= Vsctrl_Width(set->width) | Vsctrl_Height(set->height) |
                        VSCTRL_CSC_EN;

      vscadr &= ~(VSCADR_STR_EN | FMsk(VSCADR_VBASE_ADR) );
      vubase &= ~(VUBASE_UVHALFSTR | FMsk(VUBASE_UBASE_ADR));
      vvbase &= ~(FMsk(VVBASE_VBASE_ADR));

      switch (set->fmt) {
      case MBXFB_FMT_YUV16:
            vsctrl |= VSCTRL_VPIXFMT_YUV12;

            set->Y_stride = ((set->width) + 0xf ) & ~0xf;
            break;
      case MBXFB_FMT_YUV12:
            vsctrl |= VSCTRL_VPIXFMT_YUV12;

            set->Y_stride = ((set->width) + 0xf ) & ~0xf;
            vubase |= VUBASE_UVHALFSTR;

            break;
      case MBXFB_FMT_UY0VY1:
            vsctrl |= VSCTRL_VPIXFMT_UY0VY1;
            set->Y_stride = (set->width*2 + 0xf ) & ~0xf;
            break;
      case MBXFB_FMT_VY0UY1:
            vsctrl |= VSCTRL_VPIXFMT_VY0UY1;
            set->Y_stride = (set->width*2 + 0xf ) & ~0xf;
            break;
      case MBXFB_FMT_Y0UY1V:
            vsctrl |= VSCTRL_VPIXFMT_Y0UY1V;
            set->Y_stride = (set->width*2 + 0xf ) & ~0xf;
            break;
      case MBXFB_FMT_Y0VY1U:
            vsctrl |= VSCTRL_VPIXFMT_Y0VY1U;
            set->Y_stride = (set->width*2 + 0xf ) & ~0xf;
                  break;
      default:
            return -EINVAL;
      }

      /* VSCTRL has the bits which sets the Video Pixel Format.
       * When passing from a packed to planar format,
       * if we write VSCTRL first, VVBASE and VUBASE would
       * be zero if we would not set them here. (And then,
       * the chips hangs and only a reset seems to fix it).
       *
       * If course, the values calculated here have no meaning
       * for packed formats.
       */
      set->UV_stride = ((set->width/2) + 0x7 ) & ~0x7;
            set->U_offset = set->height * set->Y_stride;
            set->V_offset = set->U_offset +
                                    set->height * set->UV_stride;
      vubase |= Vubase_Ubase_Adr(
                  (0x60000 + set->mem_offset + set->U_offset)>>3);
      vvbase |= Vvbase_Vbase_Adr(
                  (0x60000 + set->mem_offset + set->V_offset)>>3);


      vscadr |= Vscadr_Vbase_Adr((0x60000 + set->mem_offset)>>4);

      if (set->enable)
            vscadr |= VSCADR_STR_EN;


      vsadr = Vsadr_Srcstride((set->Y_stride)/16-1) |
            Vsadr_Xstart(set->x) | Vsadr_Ystart(set->y);

      sssize &= ~(FMsk(SSSIZE_SC_WIDTH) | FMsk(SSSIZE_SC_HEIGHT));
      sssize = Sssize_Sc_Width(set->scaled_width-1) |
                  Sssize_Sc_Height(set->scaled_height-1);

      spoctrl &= ~(SPOCTRL_H_SC_BP | SPOCTRL_V_SC_BP |
                  SPOCTRL_HV_SC_OR | SPOCTRL_VS_UR_C |
                  FMsk(SPOCTRL_VPITCH));
      spoctrl |= Spoctrl_Vpitch((set->height<<11)/set->scaled_height);

      /* Bypass horiz/vert scaler when same size */
      if (set->scaled_width == set->width)
            spoctrl |= SPOCTRL_H_SC_BP;
      if (set->scaled_height == set->height)
            spoctrl |= SPOCTRL_V_SC_BP;

      shctrl &= ~(FMsk(SHCTRL_HPITCH) | SHCTRL_HDECIM);
      shctrl |= Shctrl_Hpitch((set->width<<11)/set->scaled_width);

      /* Video plane registers */
      write_reg(vsctrl, VSCTRL);
      write_reg(vscadr, VSCADR);
      write_reg(vubase, VUBASE);
      write_reg(vvbase, VVBASE);
      write_reg(vsadr, VSADR);

      /* Video scaler registers */
      write_reg(sssize, SSSIZE);
      write_reg(spoctrl, SPOCTRL);
      write_reg(shctrl, SHCTRL);

      /* Clock */
      if (set->enable)
            vovrclk |= 1;
      else
            vovrclk &= ~1;

      write_reg(vovrclk, VOVRCLK);

      return 0;
}

static int mbxfb_ioctl_planeorder(struct mbxfb_planeorder *porder)
{
      unsigned long gscadr, vscadr;

      if (porder->bottom == porder->top)
            return -EINVAL;

      gscadr = readl(GSCADR);
      vscadr = readl(VSCADR);

      gscadr &= ~(FMsk(GSCADR_BLEND_POS));
      vscadr &= ~(FMsk(VSCADR_BLEND_POS));

      switch (porder->bottom) {
      case MBXFB_PLANE_GRAPHICS:
            gscadr |= GSCADR_BLEND_GFX;
            break;
      case MBXFB_PLANE_VIDEO:
            vscadr |= VSCADR_BLEND_GFX;
            break;
      default:
            return -EINVAL;
      }

      switch (porder->top) {
      case MBXFB_PLANE_GRAPHICS:
            gscadr |= GSCADR_BLEND_VID;
            break;
      case MBXFB_PLANE_VIDEO:
            vscadr |= GSCADR_BLEND_VID;
            break;
      default:
            return -EINVAL;
      }

      write_reg_dly(vscadr, VSCADR);
      write_reg_dly(gscadr, GSCADR);

      return 0;

}

static int mbxfb_ioctl_alphactl(struct mbxfb_alphaCtl *alpha)
{
      unsigned long vscadr, vbbase, vcmsk;
      unsigned long gscadr, gbbase, gdrctrl;

      vbbase = Vbbase_Glalpha(alpha->overlay_global_alpha) |
                        Vbbase_Colkey(alpha->overlay_colorkey);

      gbbase = Gbbase_Glalpha(alpha->graphics_global_alpha) |
                        Gbbase_Colkey(alpha->graphics_colorkey);

      vcmsk = readl(VCMSK);
      vcmsk &= ~(FMsk(VCMSK_COLKEY_M));
      vcmsk |= Vcmsk_colkey_m(alpha->overlay_colorkey_mask);

      gdrctrl = readl(GDRCTRL);
      gdrctrl &= ~(FMsk(GDRCTRL_COLKEYM));
      gdrctrl |= Gdrctrl_Colkeym(alpha->graphics_colorkey_mask);

      vscadr = readl(VSCADR);
      vscadr &= ~(FMsk(VSCADR_BLEND_M) | VSCADR_COLKEYSRC | VSCADR_COLKEY_EN);

      gscadr = readl(GSCADR);
      gscadr &= ~(FMsk(GSCADR_BLEND_M) | GSCADR_COLKEY_EN | GSCADR_COLKEYSRC);

      switch (alpha->overlay_colorkey_mode) {
      case MBXFB_COLORKEY_DISABLED:
            break;
      case MBXFB_COLORKEY_PREVIOUS:
            vscadr |= VSCADR_COLKEY_EN;
            break;
      case MBXFB_COLORKEY_CURRENT:
            vscadr |= VSCADR_COLKEY_EN | VSCADR_COLKEYSRC;
            break;
      default:
            return -EINVAL;
      }

      switch (alpha->overlay_blend_mode) {
      case MBXFB_ALPHABLEND_NONE:
            vscadr |= VSCADR_BLEND_NONE;
            break;
      case MBXFB_ALPHABLEND_GLOBAL:
            vscadr |= VSCADR_BLEND_GLOB;
            break;
      case MBXFB_ALPHABLEND_PIXEL:
            vscadr |= VSCADR_BLEND_PIX;
            break;
      default:
            return -EINVAL;
      }

      switch (alpha->graphics_colorkey_mode) {
      case MBXFB_COLORKEY_DISABLED:
            break;
      case MBXFB_COLORKEY_PREVIOUS:
            gscadr |= GSCADR_COLKEY_EN;
            break;
      case MBXFB_COLORKEY_CURRENT:
            gscadr |= GSCADR_COLKEY_EN | GSCADR_COLKEYSRC;
            break;
      default:
            return -EINVAL;
      }

      switch (alpha->graphics_blend_mode) {
      case MBXFB_ALPHABLEND_NONE:
            gscadr |= GSCADR_BLEND_NONE;
            break;
      case MBXFB_ALPHABLEND_GLOBAL:
            gscadr |= GSCADR_BLEND_GLOB;
            break;
      case MBXFB_ALPHABLEND_PIXEL:
            gscadr |= GSCADR_BLEND_PIX;
            break;
      default:
            return -EINVAL;
      }

      write_reg_dly(vbbase, VBBASE);
      write_reg_dly(gbbase, GBBASE);
      write_reg_dly(vcmsk, VCMSK);
      write_reg_dly(gdrctrl, GDRCTRL);
      write_reg_dly(gscadr, GSCADR);
      write_reg_dly(vscadr, VSCADR);

      return 0;
}

static int mbxfb_ioctl(struct fb_info *info, unsigned int cmd,
                        unsigned long arg)
{
      struct mbxfb_overlaySetup     setup;
      struct mbxfb_planeorder       porder;
      struct mbxfb_alphaCtl         alpha;
      struct mbxfb_reg              reg;
      int res;
      __u32 tmp;

      switch (cmd)
      {
            case MBXFB_IOCX_OVERLAY:
                  if (copy_from_user(&setup, (void __user*)arg,
                                    sizeof(struct mbxfb_overlaySetup)))
                        return -EFAULT;

                  res = mbxfb_setupOverlay(&setup);
                  if (res)
                        return res;

                  if (copy_to_user((void __user*)arg, &setup,
                                    sizeof(struct mbxfb_overlaySetup)))
                        return -EFAULT;

                  return 0;

            case MBXFB_IOCS_PLANEORDER:
                  if (copy_from_user(&porder, (void __user*)arg,
                              sizeof(struct mbxfb_planeorder)))
                  return -EFAULT;

                  return mbxfb_ioctl_planeorder(&porder);

            case MBXFB_IOCS_ALPHA:
                  if (copy_from_user(&alpha, (void __user*)arg,
                              sizeof(struct mbxfb_alphaCtl)))
                  return -EFAULT;

                  return mbxfb_ioctl_alphactl(&alpha);

            case MBXFB_IOCS_REG:
                  if (copy_from_user(&reg, (void __user*)arg,
                                    sizeof(struct mbxfb_reg)))
                        return -EFAULT;

                  if (reg.addr >= 0x10000) /* regs are from 0x3fe0000 to 0x3feffff */
                        return -EINVAL;

                  tmp = readl(virt_base_2700 + reg.addr);
                  tmp &= ~reg.mask;
                  tmp |= reg.val & reg.mask;
                  writel(tmp, virt_base_2700 + reg.addr);

                  return 0;
            case MBXFB_IOCX_REG:
                  if (copy_from_user(&reg, (void __user*)arg,
                                    sizeof(struct mbxfb_reg)))
                        return -EFAULT;

                  if (reg.addr >= 0x10000)      /* regs are from 0x3fe0000 to 0x3feffff */
                        return -EINVAL;
                  reg.val = readl(virt_base_2700 + reg.addr);

                  if (copy_to_user((void __user*)arg, &reg,
                                    sizeof(struct mbxfb_reg)))
                        return -EFAULT;

                  return 0;
      }
      return -EINVAL;
}

static struct fb_ops mbxfb_ops = {
      .owner = THIS_MODULE,
      .fb_check_var = mbxfb_check_var,
      .fb_set_par = mbxfb_set_par,
      .fb_setcolreg = mbxfb_setcolreg,
      .fb_fillrect = cfb_fillrect,
      .fb_copyarea = cfb_copyarea,
      .fb_imageblit = cfb_imageblit,
      .fb_blank = mbxfb_blank,
      .fb_ioctl = mbxfb_ioctl,
};

/*
  Enable external SDRAM controller. Assume that all clocks are active
  by now.
*/
static void __devinit setup_memc(struct fb_info *fbi)
{
      unsigned long tmp;
      int i;

      /* FIXME: use platfrom specific parameters */
      /* setup SDRAM controller */
      write_reg_dly((LMCFG_LMC_DS | LMCFG_LMC_TS | LMCFG_LMD_TS |
            LMCFG_LMA_TS),
             LMCFG);

      write_reg_dly(LMPWR_MC_PWR_ACT, LMPWR);

      /* setup SDRAM timings */
      write_reg_dly((Lmtim_Tras(7) | Lmtim_Trp(3) | Lmtim_Trcd(3) |
            Lmtim_Trc(9) | Lmtim_Tdpl(2)),
             LMTIM);
      /* setup SDRAM refresh rate */
      write_reg_dly(0xc2b, LMREFRESH);
      /* setup SDRAM type parameters */
      write_reg_dly((LMTYPE_CASLAT_3 | LMTYPE_BKSZ_2 | LMTYPE_ROWSZ_11 |
            LMTYPE_COLSZ_8),
             LMTYPE);
      /* enable memory controller */
      write_reg_dly(LMPWR_MC_PWR_ACT, LMPWR);
      /* perform dummy reads */
      for ( i = 0; i < 16; i++ ) {
            tmp = readl(fbi->screen_base);
      }
}

static void enable_clocks(struct fb_info *fbi)
{
      /* enable clocks */
      write_reg_dly(SYSCLKSRC_PLL_2, SYSCLKSRC);
      write_reg_dly(PIXCLKSRC_PLL_1, PIXCLKSRC);
      write_reg_dly(0x00000000, CLKSLEEP);

      /* PLL output = (Frefclk * M) / (N * 2^P )
       *
       * M: 0x17, N: 0x3, P: 0x0 == 100 Mhz!
       * M: 0xb, N: 0x1, P: 0x1 == 71 Mhz
       * */
      write_reg_dly((Core_Pll_M(0xb) | Core_Pll_N(0x1) | Core_Pll_P(0x1) |
            CORE_PLL_EN),
             COREPLL);

      write_reg_dly((Disp_Pll_M(0x1b) | Disp_Pll_N(0x7) | Disp_Pll_P(0x1) |
            DISP_PLL_EN),
             DISPPLL);

      write_reg_dly(0x00000000, VOVRCLK);
      write_reg_dly(PIXCLK_EN, PIXCLK);
      write_reg_dly(MEMCLK_EN, MEMCLK);
      write_reg_dly(0x00000001, M24CLK);
      write_reg_dly(0x00000001, MBXCLK);
      write_reg_dly(SDCLK_EN, SDCLK);
      write_reg_dly(0x00000001, PIXCLKDIV);
}

static void __devinit setup_graphics(struct fb_info *fbi)
{
      unsigned long gsctrl;
      unsigned long vscadr;

      gsctrl = GSCTRL_GAMMA_EN | Gsctrl_Width(fbi->var.xres) |
            Gsctrl_Height(fbi->var.yres);
      switch (fbi->var.bits_per_pixel) {
      case 16:
            if (fbi->var.green.length == 5)
                  gsctrl |= GSCTRL_GPIXFMT_ARGB1555;
            else
                  gsctrl |= GSCTRL_GPIXFMT_RGB565;
            break;
      case 24:
            gsctrl |= GSCTRL_GPIXFMT_RGB888;
            break;
      case 32:
            gsctrl |= GSCTRL_GPIXFMT_ARGB8888;
            break;
      }

      write_reg_dly(gsctrl, GSCTRL);
      write_reg_dly(0x00000000, GBBASE);
      write_reg_dly(0x00ffffff, GDRCTRL);
      write_reg_dly((GSCADR_STR_EN | Gscadr_Gbase_Adr(0x6000)), GSCADR);
      write_reg_dly(0x00000000, GPLUT);

      vscadr = readl(VSCADR);
      vscadr &= ~(FMsk(VSCADR_BLEND_POS) | FMsk(VSCADR_BLEND_M));
      vscadr |= VSCADR_BLEND_VID | VSCADR_BLEND_NONE;
      write_reg_dly(vscadr, VSCADR);
}

static void __devinit setup_display(struct fb_info *fbi)
{
      unsigned long dsctrl = 0;

      dsctrl = DSCTRL_BLNK_POL;
      if (fbi->var.sync & FB_SYNC_HOR_HIGH_ACT)
            dsctrl |= DSCTRL_HS_POL;
      if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT)
            dsctrl |= DSCTRL_VS_POL;
      write_reg_dly(dsctrl, DSCTRL);
      write_reg_dly(0xd0303010, DMCTRL);
      write_reg_dly((readl(DSCTRL) | DSCTRL_SYNCGEN_EN), DSCTRL);
}

static void __devinit enable_controller(struct fb_info *fbi)
{
      u32 svctrl, shctrl;

      write_reg_dly(SYSRST_RST, SYSRST);

      /* setup a timeout, raise drive strength */
      write_reg_dly(0xffffff0c, SYSCFG);

      enable_clocks(fbi);
      setup_memc(fbi);
      setup_graphics(fbi);
      setup_display(fbi);

      shctrl = readl(SHCTRL);
      shctrl &= ~(FMsk(SHCTRL_HINITIAL));
      shctrl |= Shctrl_Hinitial(4<<11);
      writel(shctrl, SHCTRL);

      svctrl = Svctrl_Initial1(1<<10) | Svctrl_Initial2(1<<10);
      writel(svctrl, SVCTRL);

      writel(SPOCTRL_H_SC_BP | SPOCTRL_V_SC_BP | SPOCTRL_VORDER_4TAP
                  , SPOCTRL);

      /* Those coefficients are good for scaling up. For scaling
       * down, the application has to calculate them. */
      write_reg(0xff000100, VSCOEFF0);
      write_reg(0xfdfcfdfe, VSCOEFF1);
      write_reg(0x170d0500, VSCOEFF2);
      write_reg(0x3d372d22, VSCOEFF3);
      write_reg(0x00000040, VSCOEFF4);

      write_reg(0xff010100, HSCOEFF0);
      write_reg(0x00000000, HSCOEFF1);
      write_reg(0x02010000, HSCOEFF2);
      write_reg(0x01020302, HSCOEFF3);
      write_reg(0xf9fbfe00, HSCOEFF4);
      write_reg(0xfbf7f6f7, HSCOEFF5);
      write_reg(0x1c110700, HSCOEFF6);
      write_reg(0x3e393127, HSCOEFF7);
      write_reg(0x00000040, HSCOEFF8);

}

#ifdef CONFIG_PM
/*
 * Power management hooks.  Note that we won't be called from IRQ context,
 * unlike the blank functions above, so we may sleep.
 */
static int mbxfb_suspend(struct platform_device *dev, pm_message_t state)
{
      /* make frame buffer memory enter self-refresh mode */
      write_reg_dly(LMPWR_MC_PWR_SRM, LMPWR);
      while (LMPWRSTAT != LMPWRSTAT_MC_PWR_SRM)
            ; /* empty statement */

      /* reset the device, since it's initial state is 'mostly sleeping' */
      write_reg_dly(SYSRST_RST, SYSRST);
      return 0;
}

static int mbxfb_resume(struct platform_device *dev)
{
      struct fb_info *fbi = platform_get_drvdata(dev);

      enable_clocks(fbi);
/*    setup_graphics(fbi); */
/*    setup_display(fbi); */

      write_reg_dly((readl(DSCTRL) | DSCTRL_SYNCGEN_EN), DSCTRL);
      return 0;
}
#else
#define mbxfb_suspend   NULL
#define mbxfb_resume    NULL
#endif

/* debugfs entries */
#ifndef CONFIG_FB_MBX_DEBUG
#define mbxfb_debugfs_init(x) do {} while(0)
#define mbxfb_debugfs_remove(x)     do {} while(0)
#endif

#define res_size(_r) (((_r)->end - (_r)->start) + 1)

static int __devinit mbxfb_probe(struct platform_device *dev)
{
      int ret;
      struct fb_info *fbi;
      struct mbxfb_info *mfbi;
      struct mbxfb_platform_data *pdata;

      dev_dbg(dev, "mbxfb_probe\n");

      pdata = dev->dev.platform_data;
      if (!pdata) {
            dev_err(&dev->dev, "platform data is required\n");
            return -EINVAL;
      }

      fbi = framebuffer_alloc(sizeof(struct mbxfb_info), &dev->dev);
      if (fbi == NULL) {
            dev_err(&dev->dev, "framebuffer_alloc failed\n");
            return -ENOMEM;
      }

      mfbi = fbi->par;
      fbi->pseudo_palette = mfbi->pseudo_palette;


      if (pdata->probe)
            mfbi->platform_probe = pdata->probe;
      if (pdata->remove)
            mfbi->platform_remove = pdata->remove;

      mfbi->fb_res = platform_get_resource(dev, IORESOURCE_MEM, 0);
      mfbi->reg_res = platform_get_resource(dev, IORESOURCE_MEM, 1);

      if (!mfbi->fb_res || !mfbi->reg_res) {
            dev_err(&dev->dev, "no resources found\n");
            ret = -ENODEV;
            goto err1;
      }

      mfbi->fb_req = request_mem_region(mfbi->fb_res->start,
                                res_size(mfbi->fb_res), dev->name);
      if (mfbi->fb_req == NULL) {
            dev_err(&dev->dev, "failed to claim framebuffer memory\n");
            ret = -EINVAL;
            goto err1;
      }
      mfbi->fb_phys_addr = mfbi->fb_res->start;

      mfbi->reg_req = request_mem_region(mfbi->reg_res->start,
                                 res_size(mfbi->reg_res), dev->name);
      if (mfbi->reg_req == NULL) {
            dev_err(&dev->dev, "failed to claim Marathon registers\n");
            ret = -EINVAL;
            goto err2;
      }
      mfbi->reg_phys_addr = mfbi->reg_res->start;

      mfbi->reg_virt_addr = ioremap_nocache(mfbi->reg_phys_addr,
                                    res_size(mfbi->reg_req));
      if (!mfbi->reg_virt_addr) {
            dev_err(&dev->dev, "failed to ioremap Marathon registers\n");
            ret = -EINVAL;
            goto err3;
      }
      virt_base_2700 = (unsigned long)mfbi->reg_virt_addr;

      mfbi->fb_virt_addr = ioremap_nocache(mfbi->fb_phys_addr,
                                   res_size(mfbi->fb_req));
      if (!mfbi->reg_virt_addr) {
            dev_err(&dev->dev, "failed to ioremap frame buffer\n");
            ret = -EINVAL;
            goto err4;
      }

      fbi->screen_base = (char __iomem *)(mfbi->fb_virt_addr + 0x60000);
      fbi->screen_size = pdata->memsize;
      fbi->fbops = &mbxfb_ops;

      fbi->var = mbxfb_default;
      fbi->fix = mbxfb_fix;
      fbi->fix.smem_start = mfbi->fb_phys_addr + 0x60000;
      fbi->fix.smem_len = pdata->memsize;
      fbi->fix.line_length = mbxfb_default.xres_virtual *
                              mbxfb_default.bits_per_pixel / 8;

      ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
      if (ret < 0) {
            dev_err(&dev->dev, "fb_alloc_cmap failed\n");
            ret = -EINVAL;
            goto err5;
      }

      platform_set_drvdata(dev, fbi);

      printk(KERN_INFO "fb%d: mbx frame buffer device\n", fbi->node);

      if (mfbi->platform_probe)
            mfbi->platform_probe(fbi);

      enable_controller(fbi);

      mbxfb_debugfs_init(fbi);

      ret = register_framebuffer(fbi);
      if (ret < 0) {
            dev_err(&dev->dev, "register_framebuffer failed\n");
            ret = -EINVAL;
            goto err6;
      }

      return 0;

err6:
      fb_dealloc_cmap(&fbi->cmap);
err5:
      iounmap(mfbi->fb_virt_addr);
err4:
      iounmap(mfbi->reg_virt_addr);
err3:
      release_mem_region(mfbi->reg_res->start, res_size(mfbi->reg_res));
err2:
      release_mem_region(mfbi->fb_res->start, res_size(mfbi->fb_res));
err1:
      framebuffer_release(fbi);

      return ret;
}

static int __devexit mbxfb_remove(struct platform_device *dev)
{
      struct fb_info *fbi = platform_get_drvdata(dev);

      write_reg_dly(SYSRST_RST, SYSRST);

      mbxfb_debugfs_remove(fbi);

      if (fbi) {
            struct mbxfb_info *mfbi = fbi->par;

            unregister_framebuffer(fbi);
            if (mfbi) {
                  if (mfbi->platform_remove)
                        mfbi->platform_remove(fbi);

                  if (mfbi->fb_virt_addr)
                        iounmap(mfbi->fb_virt_addr);
                  if (mfbi->reg_virt_addr)
                        iounmap(mfbi->reg_virt_addr);
                  if (mfbi->reg_req)
                        release_mem_region(mfbi->reg_req->start,
                                       res_size(mfbi->reg_req));
                  if (mfbi->fb_req)
                        release_mem_region(mfbi->fb_req->start,
                                       res_size(mfbi->fb_req));
            }
            framebuffer_release(fbi);
      }

      return 0;
}

static struct platform_driver mbxfb_driver = {
      .probe = mbxfb_probe,
      .remove = mbxfb_remove,
      .suspend = mbxfb_suspend,
      .resume = mbxfb_resume,
      .driver = {
            .name = "mbx-fb",
      },
};

int __devinit mbxfb_init(void)
{
      return platform_driver_register(&mbxfb_driver);
}

static void __devexit mbxfb_exit(void)
{
      platform_driver_unregister(&mbxfb_driver);
}

module_init(mbxfb_init);
module_exit(mbxfb_exit);

MODULE_DESCRIPTION("loadable framebuffer driver for Marathon device");
MODULE_AUTHOR("Mike Rapoport, Compulab");
MODULE_LICENSE("GPL");

Generated by  Doxygen 1.6.0   Back to index