Merge branch 'common/fbdev' of master.kernel.org:/pub/scm/linux/kernel/git/lethal...
authorPaul Mundt <lethal@linux-sh.org>
Tue, 29 Mar 2011 07:06:21 +0000 (16:06 +0900)
committerPaul Mundt <lethal@linux-sh.org>
Tue, 29 Mar 2011 07:06:21 +0000 (16:06 +0900)
1  2 
arch/sh/boards/mach-ecovec24/setup.c
drivers/video/sh_mobile_lcdcfb.c
drivers/video/sh_mobile_lcdcfb.h

index e44480ce2ea8c32555c84e4f7caf3e033f65a850,a2c06220bbab3863ed4eb9bcd76a48d4c7e07300..c68e2863bf860d16996c27f023431656b21c9836
@@@ -142,8 -142,6 +142,8 @@@ static struct resource sh_eth_resources
  static struct sh_eth_plat_data sh_eth_plat = {
        .phy = 0x1f, /* SMSC LAN8700 */
        .edmac_endian = EDMAC_LITTLE_ENDIAN,
 +      .register_type = SH_ETH_REG_FAST_SH4,
 +      .phy_interface = PHY_INTERFACE_MODE_MII,
        .ether_link_active_low = 1
  };
  
@@@ -263,6 -261,18 +263,18 @@@ const static struct fb_videomode ecovec
        },
  };
  
+ static int ecovec24_set_brightness(void *board_data, int brightness)
+ {
+       gpio_set_value(GPIO_PTR1, brightness);
+       return 0;
+ }
+ static int ecovec24_get_brightness(void *board_data)
+ {
+       return gpio_get_value(GPIO_PTR1);
+ }
  static struct sh_mobile_lcdc_info lcdc_info = {
        .ch[0] = {
                .interface_type = RGB18,
                        .height = 91,
                },
                .board_cfg = {
+                       .set_brightness = ecovec24_set_brightness,
+                       .get_brightness = ecovec24_get_brightness,
+               },
+               .bl_info = {
+                       .name = "sh_mobile_lcdc_bl",
+                       .max_brightness = 1,
                },
        }
  };
@@@ -725,7 -741,11 +743,7 @@@ static struct platform_device camera_de
  
  /* FSI */
  static struct sh_fsi_platform_info fsi_info = {
 -      .portb_flags = SH_FSI_BRS_INV |
 -                     SH_FSI_OUT_SLAVE_MODE |
 -                     SH_FSI_IN_SLAVE_MODE |
 -                     SH_FSI_OFMT(I2S) |
 -                     SH_FSI_IFMT(I2S),
 +      .portb_flags = SH_FSI_BRS_INV,
  };
  
  static struct resource fsi_resources[] = {
index 757665bc500f1c009540673ed497f556f1a26024,69e2833bd460a233e27c6c75ea3cd3ba693e4ad4..9bcc61b4ef149a258e6159a9728ffebac2bd5daf
@@@ -69,7 -69,6 +69,7 @@@ static unsigned long lcdc_offs_mainlcd[
        [LDSM1R] = 0x428,
        [LDSM2R] = 0x42c,
        [LDSA1R] = 0x430,
 +      [LDSA2R] = 0x434,
        [LDMLSR] = 0x438,
        [LDHCNR] = 0x448,
        [LDHSYNR] = 0x44c,
@@@ -154,7 -153,6 +154,7 @@@ static bool banked(int reg_nr
        case LDDFR:
        case LDSM1R:
        case LDSA1R:
 +      case LDSA2R:
        case LDMLSR:
        case LDHCNR:
        case LDHSYNR:
@@@ -467,7 -465,6 +467,7 @@@ static int sh_mobile_lcdc_start(struct 
        struct sh_mobile_lcdc_board_cfg *board_cfg;
        unsigned long tmp;
        int bpp = 0;
 +      unsigned long ldddsr;
        int k, m;
        int ret = 0;
  
        }
  
        /* word and long word swap */
 -      switch (bpp) {
 -      case 16:
 -              lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 6);
 -              break;
 -      case 24:
 -              lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 7);
 -              break;
 -      case 32:
 -              lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 4);
 -              break;
 +      ldddsr = lcdc_read(priv, _LDDDSR);
 +      if  (priv->ch[0].info->var.nonstd)
 +              lcdc_write(priv, _LDDDSR, ldddsr | 7);
 +      else {
 +              switch (bpp) {
 +              case 16:
 +                      lcdc_write(priv, _LDDDSR, ldddsr | 6);
 +                      break;
 +              case 24:
 +                      lcdc_write(priv, _LDDDSR, ldddsr | 7);
 +                      break;
 +              case 32:
 +                      lcdc_write(priv, _LDDDSR, ldddsr | 4);
 +                      break;
 +              }
        }
  
        for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
  
                /* set bpp format in PKF[4:0] */
                tmp = lcdc_read_chan(ch, LDDFR);
 -              tmp &= ~0x0001001f;
 -              switch (ch->info->var.bits_per_pixel) {
 -              case 16:
 -                      tmp |= 0x03;
 -                      break;
 -              case 24:
 -                      tmp |= 0x0b;
 -                      break;
 -              case 32:
 -                      break;
 +              tmp &= ~0x0003031f;
 +              if (ch->info->var.nonstd) {
 +                      tmp |= (ch->info->var.nonstd << 16);
 +                      switch (ch->info->var.bits_per_pixel) {
 +                      case 12:
 +                              break;
 +                      case 16:
 +                              tmp |= (0x1 << 8);
 +                              break;
 +                      case 24:
 +                              tmp |= (0x2 << 8);
 +                              break;
 +                      }
 +              } else {
 +                      switch (ch->info->var.bits_per_pixel) {
 +                      case 16:
 +                              tmp |= 0x03;
 +                              break;
 +                      case 24:
 +                              tmp |= 0x0b;
 +                              break;
 +                      case 32:
 +                              break;
 +                      }
                }
                lcdc_write_chan(ch, LDDFR, tmp);
  
                /* point out our frame buffer */
                lcdc_write_chan(ch, LDSA1R, ch->info->fix.smem_start);
 +              if (ch->info->var.nonstd)
 +                      lcdc_write_chan(ch, LDSA2R,
 +                              ch->info->fix.smem_start +
 +                              ch->info->var.xres *
 +                              ch->info->var.yres_virtual);
  
                /* set line size */
                lcdc_write_chan(ch, LDMLSR, ch->info->fix.line_length);
                        continue;
  
                board_cfg = &ch->cfg.board_cfg;
-               if (try_module_get(board_cfg->owner) && board_cfg->display_on) {
+               if (board_cfg->display_on && try_module_get(board_cfg->owner)) {
                        board_cfg->display_on(board_cfg->board_data, ch->info);
                        module_put(board_cfg->owner);
                }
@@@ -688,7 -661,7 +688,7 @@@ static void sh_mobile_lcdc_stop(struct 
                }
  
                board_cfg = &ch->cfg.board_cfg;
-               if (try_module_get(board_cfg->owner) && board_cfg->display_off) {
+               if (board_cfg->display_off && try_module_get(board_cfg->owner)) {
                        board_cfg->display_off(board_cfg->board_data);
                        module_put(board_cfg->owner);
                }
@@@ -843,15 -816,9 +843,15 @@@ static int sh_mobile_fb_pan_display(str
        struct sh_mobile_lcdc_priv *priv = ch->lcdc;
        unsigned long ldrcntr;
        unsigned long new_pan_offset;
 +      unsigned long base_addr_y, base_addr_c;
 +      unsigned long c_offset;
  
 -      new_pan_offset = (var->yoffset * info->fix.line_length) +
 -              (var->xoffset * (info->var.bits_per_pixel / 8));
 +      if (!var->nonstd)
 +              new_pan_offset = (var->yoffset * info->fix.line_length) +
 +                      (var->xoffset * (info->var.bits_per_pixel / 8));
 +      else
 +              new_pan_offset = (var->yoffset * info->fix.line_length) +
 +                      (var->xoffset);
  
        if (new_pan_offset == ch->pan_offset)
                return 0;       /* No change, do nothing */
        ldrcntr = lcdc_read(priv, _LDRCNTR);
  
        /* Set the source address for the next refresh */
 -      lcdc_write_chan_mirror(ch, LDSA1R, ch->dma_handle + new_pan_offset);
 +      base_addr_y = ch->dma_handle + new_pan_offset;
 +      if (var->nonstd) {
 +              /* Set y offset */
 +              c_offset = (var->yoffset *
 +                      info->fix.line_length *
 +                      (info->var.bits_per_pixel - 8)) / 8;
 +              base_addr_c = ch->dma_handle + var->xres * var->yres_virtual +
 +                      c_offset;
 +              /* Set x offset */
 +              if (info->var.bits_per_pixel == 24)
 +                      base_addr_c += 2 * var->xoffset;
 +              else
 +                      base_addr_c += var->xoffset;
 +      } else
 +              base_addr_c = 0;
 +
 +      lcdc_write_chan_mirror(ch, LDSA1R, base_addr_y);
 +      if (base_addr_c)
 +              lcdc_write_chan_mirror(ch, LDSA2R, base_addr_c);
 +
        if (lcdc_chan_is_sublcd(ch))
                lcdc_write(ch->lcdc, _LDRCNTR, ldrcntr ^ LDRCNTR_SRS);
        else
@@@ -949,10 -897,7 +949,10 @@@ static void sh_mobile_fb_reconfig(struc
                /* Couldn't reconfigure, hopefully, can continue as before */
                return;
  
 -      info->fix.line_length = mode1.xres * (ch->cfg.bpp / 8);
 +      if (info->var.nonstd)
 +              info->fix.line_length = mode1.xres;
 +      else
 +              info->fix.line_length = mode1.xres * (ch->cfg.bpp / 8);
  
        /*
         * fb_set_var() calls the notifier change internally, only if
@@@ -1032,6 -977,49 +1032,49 @@@ static int sh_mobile_check_var(struct f
        return 0;
  }
  
+ /*
+  * Screen blanking. Behavior is as follows:
+  * FB_BLANK_UNBLANK: screen unblanked, clocks enabled
+  * FB_BLANK_NORMAL: screen blanked, clocks enabled
+  * FB_BLANK_VSYNC,
+  * FB_BLANK_HSYNC,
+  * FB_BLANK_POWEROFF: screen blanked, clocks disabled
+  */
+ static int sh_mobile_lcdc_blank(int blank, struct fb_info *info)
+ {
+       struct sh_mobile_lcdc_chan *ch = info->par;
+       struct sh_mobile_lcdc_priv *p = ch->lcdc;
+       /* blank the screen? */
+       if (blank > FB_BLANK_UNBLANK && ch->blank_status == FB_BLANK_UNBLANK) {
+               struct fb_fillrect rect = {
+                       .width = info->var.xres,
+                       .height = info->var.yres,
+               };
+               sh_mobile_lcdc_fillrect(info, &rect);
+       }
+       /* turn clocks on? */
+       if (blank <= FB_BLANK_NORMAL && ch->blank_status > FB_BLANK_NORMAL) {
+               sh_mobile_lcdc_clk_on(p);
+       }
+       /* turn clocks off? */
+       if (blank > FB_BLANK_NORMAL && ch->blank_status <= FB_BLANK_NORMAL) {
+               /* make sure the screen is updated with the black fill before
+                * switching the clocks off. one vsync is not enough since
+                * blanking may occur in the middle of a refresh. deferred io
+                * mode will reenable the clocks and update the screen in time,
+                * so it does not need this. */
+               if (!info->fbdefio) {
+                       sh_mobile_wait_for_vsync(info);
+                       sh_mobile_wait_for_vsync(info);
+               }
+               sh_mobile_lcdc_clk_off(p);
+       }
+       ch->blank_status = blank;
+       return 0;
+ }
  static struct fb_ops sh_mobile_lcdc_ops = {
        .owner          = THIS_MODULE,
        .fb_setcolreg   = sh_mobile_lcdc_setcolreg,
        .fb_fillrect    = sh_mobile_lcdc_fillrect,
        .fb_copyarea    = sh_mobile_lcdc_copyarea,
        .fb_imageblit   = sh_mobile_lcdc_imageblit,
+       .fb_blank       = sh_mobile_lcdc_blank,
        .fb_pan_display = sh_mobile_fb_pan_display,
        .fb_ioctl       = sh_mobile_ioctl,
        .fb_open        = sh_mobile_open,
@@@ -1088,9 -1077,8 +1132,9 @@@ static struct backlight_device *sh_mobi
  
        bl = backlight_device_register(ch->cfg.bl_info.name, parent, ch,
                                       &sh_mobile_lcdc_bl_ops, NULL);
 -      if (!bl) {
 -              dev_err(parent, "unable to register backlight device\n");
 +      if (IS_ERR(bl)) {
 +              dev_err(parent, "unable to register backlight device: %ld\n",
 +                      PTR_ERR(bl));
                return NULL;
        }
  
@@@ -1106,22 -1094,8 +1150,22 @@@ static void sh_mobile_lcdc_bl_remove(st
        backlight_device_unregister(bdev);
  }
  
 -static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp)
 +static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp,
 +                                 int nonstd)
  {
 +      if (nonstd) {
 +              switch (bpp) {
 +              case 12:
 +              case 16:
 +              case 24:
 +                      var->bits_per_pixel = bpp;
 +                      var->nonstd = nonstd;
 +                      return 0;
 +              default:
 +                      return -EINVAL;
 +              }
 +      }
 +
        switch (bpp) {
        case 16: /* PKF[4:0] = 00011 - RGB 565 */
                var->red.offset = 11;
@@@ -1254,7 -1228,7 +1298,7 @@@ static int sh_mobile_lcdc_notify(struc
  
        switch(action) {
        case FB_EVENT_SUSPEND:
-               if (try_module_get(board_cfg->owner) && board_cfg->display_off) {
+               if (board_cfg->display_off && try_module_get(board_cfg->owner)) {
                        board_cfg->display_off(board_cfg->board_data);
                        module_put(board_cfg->owner);
                }
                mutex_unlock(&ch->open_lock);
  
                /* HDMI must be enabled before LCDC configuration */
-               if (try_module_get(board_cfg->owner) && board_cfg->display_on) {
+               if (board_cfg->display_on && try_module_get(board_cfg->owner)) {
                        board_cfg->display_on(board_cfg->board_data, info);
                        module_put(board_cfg->owner);
                }
@@@ -1404,14 -1378,6 +1448,14 @@@ static int __devinit sh_mobile_lcdc_pro
                     k < cfg->num_cfg && lcd_cfg;
                     k++, lcd_cfg++) {
                        unsigned long size = lcd_cfg->yres * lcd_cfg->xres;
 +                      /* NV12 buffers must have even number of lines */
 +                      if ((cfg->nonstd) && cfg->bpp == 12 &&
 +                                      (lcd_cfg->yres & 0x1)) {
 +                              dev_err(&pdev->dev, "yres must be multiple of 2"
 +                                              " for YCbCr420 mode.\n");
 +                              error = -EINVAL;
 +                              goto err1;
 +                      }
  
                        if (size > max_size) {
                                max_cfg = lcd_cfg;
                                max_cfg->xres, max_cfg->yres);
  
                info->fix = sh_mobile_lcdc_fix;
 -              info->fix.smem_len = max_size * (cfg->bpp / 8) * 2;
 +              info->fix.smem_len = max_size * 2 * cfg->bpp / 8;
 +
 +               /* Only pan in 2 line steps for NV12 */
 +              if (cfg->nonstd && cfg->bpp == 12)
 +                      info->fix.ypanstep = 2;
  
                if (!mode) {
                        mode = &default_720p;
                var->yres_virtual = var->yres * 2;
                var->activate = FB_ACTIVATE_NOW;
  
 -              error = sh_mobile_lcdc_set_bpp(var, cfg->bpp);
 +              error = sh_mobile_lcdc_set_bpp(var, cfg->bpp, cfg->nonstd);
                if (error)
                        break;
  
                }
  
                info->fix.smem_start = ch->dma_handle;
 -              info->fix.line_length = var->xres * (cfg->bpp / 8);
 +              if (var->nonstd)
 +                      info->fix.line_length = var->xres;
 +              else
 +                      info->fix.line_length = var->xres * (cfg->bpp / 8);
 +
                info->screen_base = buf;
                info->device = &pdev->dev;
                ch->display_var = *var;
index 4635eed63eee0f90e60f505935d8803c4a4c436e,fad353a7e44ceff671f9258d206d3d1f44ddc242..f16cb5645a13fd12b8c95881dd51a5daab6352ad
@@@ -8,7 -8,7 +8,7 @@@
  
  /* per-channel registers */
  enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R,
 -       LDSM2R, LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR,
 +       LDSM2R, LDSA1R, LDSA2R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR,
         LDHAJR,
         NR_CH_REGS };
  
@@@ -37,6 -37,7 +37,7 @@@ struct sh_mobile_lcdc_chan 
        struct completion vsync_completion;
        struct fb_var_screeninfo display_var;
        int use_count;
+       int blank_status;
        struct mutex open_lock;         /* protects the use counter */
  };