]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge remote-tracking branch 'omap_dss2/for-next'
authorStephen Rothwell <sfr@canb.auug.org.au>
Thu, 5 Nov 2015 01:51:39 +0000 (12:51 +1100)
committerStephen Rothwell <sfr@canb.auug.org.au>
Thu, 5 Nov 2015 01:51:39 +0000 (12:51 +1100)
1  2 
drivers/video/fbdev/tridentfb.c

index 01b43e9ce941acb8751c0c2e8294e19db7ce927c,e5c9b145460e00be05bac970813caec63db0a0c0..8a5bbc13082eb92154b6b6c84fd4c415a6be485d
@@@ -25,6 -25,9 +25,9 @@@
  #include <video/vga.h>
  #include <video/trident.h>
  
+ #include <linux/i2c.h>
+ #include <linux/i2c-algo-bit.h>
  struct tridentfb_par {
        void __iomem *io_virt;  /* iospace virtual memory address */
        u32 pseudo_pal[16];
@@@ -40,6 -43,9 +43,9 @@@
                (struct tridentfb_par *par, const char*,
                 u32, u32, u32, u32, u32, u32);
        unsigned char eng_oper; /* engine operation... */
+       bool ddc_registered;
+       struct i2c_adapter ddc_adapter;
+       struct i2c_algo_bit_data ddc_algo;
  };
  
  static struct fb_fix_screeninfo tridentfb_fix = {
@@@ -53,7 -59,7 +59,7 @@@
  /* defaults which are normally overriden by user values */
  
  /* video mode */
- static char *mode_option = "640x480-8@60";
+ static char *mode_option;
  static int bpp = 8;
  
  static int noaccel;
@@@ -174,6 -180,121 +180,121 @@@ static inline u32 readmmr(struct triden
        return fb_readl(par->io_virt + r);
  }
  
+ #define DDC_SDA_TGUI          BIT(0)
+ #define DDC_SCL_TGUI          BIT(1)
+ #define DDC_SCL_DRIVE_TGUI    BIT(2)
+ #define DDC_SDA_DRIVE_TGUI    BIT(3)
+ #define DDC_MASK_TGUI         (DDC_SCL_DRIVE_TGUI | DDC_SDA_DRIVE_TGUI)
+ static void tridentfb_ddc_setscl_tgui(void *data, int val)
+ {
+       struct tridentfb_par *par = data;
+       u8 reg = vga_mm_rcrt(par->io_virt, I2C) & DDC_MASK_TGUI;
+       if (val)
+               reg &= ~DDC_SCL_DRIVE_TGUI; /* disable drive - don't drive hi */
+       else
+               reg |= DDC_SCL_DRIVE_TGUI; /* drive low */
+       vga_mm_wcrt(par->io_virt, I2C, reg);
+ }
+ static void tridentfb_ddc_setsda_tgui(void *data, int val)
+ {
+       struct tridentfb_par *par = data;
+       u8 reg = vga_mm_rcrt(par->io_virt, I2C) & DDC_MASK_TGUI;
+       if (val)
+               reg &= ~DDC_SDA_DRIVE_TGUI; /* disable drive - don't drive hi */
+       else
+               reg |= DDC_SDA_DRIVE_TGUI; /* drive low */
+       vga_mm_wcrt(par->io_virt, I2C, reg);
+ }
+ static int tridentfb_ddc_getsda_tgui(void *data)
+ {
+       struct tridentfb_par *par = data;
+       return !!(vga_mm_rcrt(par->io_virt, I2C) & DDC_SDA_TGUI);
+ }
+ #define DDC_SDA_IN    BIT(0)
+ #define DDC_SCL_OUT   BIT(1)
+ #define DDC_SDA_OUT   BIT(3)
+ #define DDC_SCL_IN    BIT(6)
+ #define DDC_MASK      (DDC_SCL_OUT | DDC_SDA_OUT)
+ static void tridentfb_ddc_setscl(void *data, int val)
+ {
+       struct tridentfb_par *par = data;
+       unsigned char reg;
+       reg = vga_mm_rcrt(par->io_virt, I2C) & DDC_MASK;
+       if (val)
+               reg |= DDC_SCL_OUT;
+       else
+               reg &= ~DDC_SCL_OUT;
+       vga_mm_wcrt(par->io_virt, I2C, reg);
+ }
+ static void tridentfb_ddc_setsda(void *data, int val)
+ {
+       struct tridentfb_par *par = data;
+       unsigned char reg;
+       reg = vga_mm_rcrt(par->io_virt, I2C) & DDC_MASK;
+       if (!val)
+               reg |= DDC_SDA_OUT;
+       else
+               reg &= ~DDC_SDA_OUT;
+       vga_mm_wcrt(par->io_virt, I2C, reg);
+ }
+ static int tridentfb_ddc_getscl(void *data)
+ {
+       struct tridentfb_par *par = data;
+       return !!(vga_mm_rcrt(par->io_virt, I2C) & DDC_SCL_IN);
+ }
+ static int tridentfb_ddc_getsda(void *data)
+ {
+       struct tridentfb_par *par = data;
+       return !!(vga_mm_rcrt(par->io_virt, I2C) & DDC_SDA_IN);
+ }
+ static int tridentfb_setup_ddc_bus(struct fb_info *info)
+ {
+       struct tridentfb_par *par = info->par;
+       strlcpy(par->ddc_adapter.name, info->fix.id,
+               sizeof(par->ddc_adapter.name));
+       par->ddc_adapter.owner          = THIS_MODULE;
+       par->ddc_adapter.class          = I2C_CLASS_DDC;
+       par->ddc_adapter.algo_data      = &par->ddc_algo;
+       par->ddc_adapter.dev.parent     = info->device;
+       if (is_oldclock(par->chip_id)) { /* not sure if this check is OK */
+               par->ddc_algo.setsda    = tridentfb_ddc_setsda_tgui;
+               par->ddc_algo.setscl    = tridentfb_ddc_setscl_tgui;
+               par->ddc_algo.getsda    = tridentfb_ddc_getsda_tgui;
+               /* no getscl */
+       } else {
+               par->ddc_algo.setsda    = tridentfb_ddc_setsda;
+               par->ddc_algo.setscl    = tridentfb_ddc_setscl;
+               par->ddc_algo.getsda    = tridentfb_ddc_getsda;
+               par->ddc_algo.getscl    = tridentfb_ddc_getscl;
+       }
+       par->ddc_algo.udelay            = 10;
+       par->ddc_algo.timeout           = 20;
+       par->ddc_algo.data              = par;
+       i2c_set_adapdata(&par->ddc_adapter, par);
+       return i2c_bit_add_bus(&par->ddc_adapter);
+ }
  /*
   * Blade specific acceleration.
   */
@@@ -226,7 -347,7 +347,7 @@@ static void blade_image_blit(struct tri
        writemmr(par, DST1, point(x, y));
        writemmr(par, DST2, point(x + w - 1, y + h - 1));
  
 -      memcpy(par->io_virt + 0x10000, data, 4 * size);
 +      iowrite32_rep(par->io_virt + 0x10000, data, size);
  }
  
  static void blade_copy_rect(struct tridentfb_par *par,
@@@ -673,14 -794,8 +794,14 @@@ static int get_nativex(struct tridentfb
  static inline void set_lwidth(struct tridentfb_par *par, int width)
  {
        write3X4(par, VGA_CRTC_OFFSET, width & 0xFF);
 -      write3X4(par, AddColReg,
 -               (read3X4(par, AddColReg) & 0xCF) | ((width & 0x300) >> 4));
 +      /* chips older than TGUI9660 have only 1 width bit in AddColReg */
 +      /* touching the other one breaks I2C/DDC */
 +      if (par->chip_id == TGUI9440 || par->chip_id == CYBER9320)
 +              write3X4(par, AddColReg,
 +                   (read3X4(par, AddColReg) & 0xEF) | ((width & 0x100) >> 4));
 +      else
 +              write3X4(par, AddColReg,
 +                   (read3X4(par, AddColReg) & 0xCF) | ((width & 0x300) >> 4));
  }
  
  /* For resolutions smaller than FP resolution stretch */
@@@ -1346,6 -1461,7 +1467,7 @@@ static int trident_pci_probe(struct pci
        struct tridentfb_par *default_par;
        int chip3D;
        int chip_id;
+       bool found = false;
  
        err = pci_enable_device(dev);
        if (err)
        info->pixmap.scan_align = 1;
        info->pixmap.access_align = 32;
        info->pixmap.flags = FB_PIXMAP_SYSTEM;
+       info->var.bits_per_pixel = 8;
  
        if (default_par->image_blit) {
                info->flags |= FBINFO_HWACCEL_IMAGEBLIT;
                info->pixmap.scan_align = 1;
        }
  
-       if (!fb_find_mode(&info->var, info,
-                         mode_option, NULL, 0, NULL, bpp)) {
-               err = -EINVAL;
-               goto out_unmap2;
+       if (tridentfb_setup_ddc_bus(info) == 0) {
+               u8 *edid = fb_ddc_read(&default_par->ddc_adapter);
+               default_par->ddc_registered = true;
+               if (edid) {
+                       fb_edid_to_monspecs(edid, &info->monspecs);
+                       kfree(edid);
+                       if (!info->monspecs.modedb)
+                               dev_err(info->device, "error getting mode database\n");
+                       else {
+                               const struct fb_videomode *m;
+                               fb_videomode_to_modelist(info->monspecs.modedb,
+                                                info->monspecs.modedb_len,
+                                                &info->modelist);
+                               m = fb_find_best_display(&info->monspecs,
+                                                        &info->modelist);
+                               if (m) {
+                                       fb_videomode_to_var(&info->var, m);
+                                       /* fill all other info->var's fields */
+                                       if (tridentfb_check_var(&info->var,
+                                                               info) == 0)
+                                               found = true;
+                               }
+                       }
+               }
        }
+       if (!mode_option && !found)
+               mode_option = "640x480-8@60";
+       /* Prepare startup mode */
+       if (mode_option) {
+               err = fb_find_mode(&info->var, info, mode_option,
+                                  info->monspecs.modedb,
+                                  info->monspecs.modedb_len,
+                                  NULL, info->var.bits_per_pixel);
+               if (!err || err == 4) {
+                       err = -EINVAL;
+                       dev_err(info->device, "mode %s not found\n",
+                                                               mode_option);
+                       fb_destroy_modedb(info->monspecs.modedb);
+                       info->monspecs.modedb = NULL;
+                       goto out_unmap2;
+               }
+       }
+       fb_destroy_modedb(info->monspecs.modedb);
+       info->monspecs.modedb = NULL;
        err = fb_alloc_cmap(&info->cmap, 256, 0);
        if (err < 0)
                goto out_unmap2;
        return 0;
  
  out_unmap2:
+       if (default_par->ddc_registered)
+               i2c_del_adapter(&default_par->ddc_adapter);
        kfree(info->pixmap.addr);
        if (info->screen_base)
                iounmap(info->screen_base);
@@@ -1555,6 -1719,8 +1725,8 @@@ static void trident_pci_remove(struct p
        struct tridentfb_par *par = info->par;
  
        unregister_framebuffer(info);
+       if (par->ddc_registered)
+               i2c_del_adapter(&par->ddc_adapter);
        iounmap(par->io_virt);
        iounmap(info->screen_base);
        release_mem_region(tridentfb_fix.smem_start, tridentfb_fix.smem_len);