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)
12 files changed:
Documentation/devicetree/bindings/video/ssd1307fb.txt
drivers/video/fbdev/Kconfig
drivers/video/fbdev/aty/radeon_base.c
drivers/video/fbdev/aty/radeonfb.h
drivers/video/fbdev/core/fb_ddc.c
drivers/video/fbdev/gxt4500.c
drivers/video/fbdev/omap/omapfb_main.c
drivers/video/fbdev/omap2/dss/hdmi.h
drivers/video/fbdev/omap2/dss/hdmi4.c
drivers/video/fbdev/omap2/dss/hdmi5.c
drivers/video/fbdev/ssd1307fb.c
drivers/video/fbdev/tridentfb.c

index d1be78d..eb31ed4 100644 (file)
@@ -2,7 +2,8 @@
 
 Required properties:
   - compatible: Should be "solomon,<chip>fb-<bus>". The only supported bus for
-    now is i2c, and the supported chips are ssd1305, ssd1306 and ssd1307.
+    now is i2c, and the supported chips are ssd1305, ssd1306, ssd1307 and
+    ssd1309.
   - reg: Should contain address of the controller on the I2C bus. Most likely
          0x3c or 0x3d
   - pwm: Should contain the pwm to use according to the OF device tree PWM
index 8b1d371..e6d16d6 100644 (file)
@@ -1666,6 +1666,8 @@ config FB_TRIDENT
        select FB_CFB_FILLRECT
        select FB_CFB_COPYAREA
        select FB_CFB_IMAGEBLIT
+       select FB_DDC
+       select FB_MODE_HELPERS
        ---help---
          This is the frame buffer device driver for Trident PCI/AGP chipsets.
          Supported chipset families are TGUI 9440/96XX, 3DImage, Blade3D
@@ -2132,7 +2134,7 @@ config FB_UDL
 
 config FB_IBM_GXT4500
        tristate "Framebuffer support for IBM GXT4000P/4500P/6000P/6500P adaptors"
-       depends on FB && PPC
+       depends on FB
        select FB_CFB_FILLRECT
        select FB_CFB_COPYAREA
        select FB_CFB_IMAGEBLIT
@@ -2140,7 +2142,8 @@ config FB_IBM_GXT4500
          Say Y here to enable support for the IBM GXT4000P/6000P and
          GXT4500P/6500P display adaptor based on Raster Engine RC1000,
          found on some IBM System P (pSeries) machines. This driver
-         doesn't use Geometry Engine GT1000.
+         doesn't use Geometry Engine GT1000. This driver also supports
+         AGP Fire GL2/3/4 cards on x86.
 
 config FB_PS3
        tristate "PS3 GPU framebuffer driver"
index 2bdb070..ce0b1d0 100644 (file)
@@ -276,9 +276,138 @@ static int backlight = 1;
 static int backlight = 0;
 #endif
 
-/*
- * prototypes
+/* Note about this function: we have some rare cases where we must not schedule,
+ * this typically happen with our special "wake up early" hook which allows us to
+ * wake up the graphic chip (and thus get the console back) before everything else
+ * on some machines that support that mechanism. At this point, interrupts are off
+ * and scheduling is not permitted
  */
+void _radeon_msleep(struct radeonfb_info *rinfo, unsigned long ms)
+{
+       if (rinfo->no_schedule || oops_in_progress)
+               mdelay(ms);
+       else
+               msleep(ms);
+}
+
+void radeon_pll_errata_after_index_slow(struct radeonfb_info *rinfo)
+{
+       /* Called if (rinfo->errata & CHIP_ERRATA_PLL_DUMMYREADS) is set */
+       (void)INREG(CLOCK_CNTL_DATA);
+       (void)INREG(CRTC_GEN_CNTL);
+}
+
+void radeon_pll_errata_after_data_slow(struct radeonfb_info *rinfo)
+{
+       if (rinfo->errata & CHIP_ERRATA_PLL_DELAY) {
+               /* we can't deal with posted writes here ... */
+               _radeon_msleep(rinfo, 5);
+       }
+       if (rinfo->errata & CHIP_ERRATA_R300_CG) {
+               u32 save, tmp;
+               save = INREG(CLOCK_CNTL_INDEX);
+               tmp = save & ~(0x3f | PLL_WR_EN);
+               OUTREG(CLOCK_CNTL_INDEX, tmp);
+               tmp = INREG(CLOCK_CNTL_DATA);
+               OUTREG(CLOCK_CNTL_INDEX, save);
+       }
+}
+
+void _OUTREGP(struct radeonfb_info *rinfo, u32 addr, u32 val, u32 mask)
+{
+       unsigned long flags;
+       unsigned int tmp;
+
+       spin_lock_irqsave(&rinfo->reg_lock, flags);
+       tmp = INREG(addr);
+       tmp &= (mask);
+       tmp |= (val);
+       OUTREG(addr, tmp);
+       spin_unlock_irqrestore(&rinfo->reg_lock, flags);
+}
+
+u32 __INPLL(struct radeonfb_info *rinfo, u32 addr)
+{
+       u32 data;
+
+       OUTREG8(CLOCK_CNTL_INDEX, addr & 0x0000003f);
+       radeon_pll_errata_after_index(rinfo);
+       data = INREG(CLOCK_CNTL_DATA);
+       radeon_pll_errata_after_data(rinfo);
+       return data;
+}
+
+void __OUTPLL(struct radeonfb_info *rinfo, unsigned int index, u32 val)
+{
+       OUTREG8(CLOCK_CNTL_INDEX, (index & 0x0000003f) | 0x00000080);
+       radeon_pll_errata_after_index(rinfo);
+       OUTREG(CLOCK_CNTL_DATA, val);
+       radeon_pll_errata_after_data(rinfo);
+}
+
+void __OUTPLLP(struct radeonfb_info *rinfo, unsigned int index,
+                            u32 val, u32 mask)
+{
+       unsigned int tmp;
+
+       tmp  = __INPLL(rinfo, index);
+       tmp &= (mask);
+       tmp |= (val);
+       __OUTPLL(rinfo, index, tmp);
+}
+
+void _radeon_fifo_wait(struct radeonfb_info *rinfo, int entries)
+{
+       int i;
+
+       for (i=0; i<2000000; i++) {
+               if ((INREG(RBBM_STATUS) & 0x7f) >= entries)
+                       return;
+               udelay(1);
+       }
+       printk(KERN_ERR "radeonfb: FIFO Timeout !\n");
+}
+
+void radeon_engine_flush(struct radeonfb_info *rinfo)
+{
+       int i;
+
+       /* Initiate flush */
+       OUTREGP(DSTCACHE_CTLSTAT, RB2D_DC_FLUSH_ALL,
+               ~RB2D_DC_FLUSH_ALL);
+
+       /* Ensure FIFO is empty, ie, make sure the flush commands
+        * has reached the cache
+        */
+       _radeon_fifo_wait(rinfo, 64);
+
+       /* Wait for the flush to complete */
+       for (i=0; i < 2000000; i++) {
+               if (!(INREG(DSTCACHE_CTLSTAT) & RB2D_DC_BUSY))
+                       return;
+               udelay(1);
+       }
+       printk(KERN_ERR "radeonfb: Flush Timeout !\n");
+}
+
+void _radeon_engine_idle(struct radeonfb_info *rinfo)
+{
+       int i;
+
+       /* ensure FIFO is empty before waiting for idle */
+       _radeon_fifo_wait(rinfo, 64);
+
+       for (i=0; i<2000000; i++) {
+               if (((INREG(RBBM_STATUS) & GUI_ACTIVE)) == 0) {
+                       radeon_engine_flush(rinfo);
+                       return;
+               }
+               udelay(1);
+       }
+       printk(KERN_ERR "radeonfb: Idle Timeout !\n");
+}
+
+
 
 static void radeon_unmap_ROM(struct radeonfb_info *rinfo, struct pci_dev *dev)
 {
index 5bc1944..962e312 100644 (file)
@@ -370,20 +370,7 @@ struct radeonfb_info {
  * IO macros
  */
 
-/* Note about this function: we have some rare cases where we must not schedule,
- * this typically happen with our special "wake up early" hook which allows us to
- * wake up the graphic chip (and thus get the console back) before everything else
- * on some machines that support that mechanism. At this point, interrupts are off
- * and scheduling is not permitted
- */
-static inline void _radeon_msleep(struct radeonfb_info *rinfo, unsigned long ms)
-{
-       if (rinfo->no_schedule || oops_in_progress)
-               mdelay(ms);
-       else
-               msleep(ms);
-}
-
+void _radeon_msleep(struct radeonfb_info *rinfo, unsigned long ms);
 
 #define INREG8(addr)           readb((rinfo->mmio_base)+addr)
 #define OUTREG8(addr,val)      writeb(val, (rinfo->mmio_base)+addr)
@@ -392,19 +379,7 @@ static inline void _radeon_msleep(struct radeonfb_info *rinfo, unsigned long ms)
 #define INREG(addr)            readl((rinfo->mmio_base)+addr)
 #define OUTREG(addr,val)       writel(val, (rinfo->mmio_base)+addr)
 
-static inline void _OUTREGP(struct radeonfb_info *rinfo, u32 addr,
-                      u32 val, u32 mask)
-{
-       unsigned long flags;
-       unsigned int tmp;
-
-       spin_lock_irqsave(&rinfo->reg_lock, flags);
-       tmp = INREG(addr);
-       tmp &= (mask);
-       tmp |= (val);
-       OUTREG(addr, tmp);
-       spin_unlock_irqrestore(&rinfo->reg_lock, flags);
-}
+void _OUTREGP(struct radeonfb_info *rinfo, u32 addr, u32 val, u32 mask);
 
 #define OUTREGP(addr,val,mask) _OUTREGP(rinfo, addr, val,mask)
 
@@ -425,64 +400,24 @@ static inline void _OUTREGP(struct radeonfb_info *rinfo, u32 addr,
  * possible exception to this rule is the call to unblank(), which may
  * be done at irq time if an oops is in progress.
  */
+void radeon_pll_errata_after_index_slow(struct radeonfb_info *rinfo);
 static inline void radeon_pll_errata_after_index(struct radeonfb_info *rinfo)
 {
-       if (!(rinfo->errata & CHIP_ERRATA_PLL_DUMMYREADS))
-               return;
-
-       (void)INREG(CLOCK_CNTL_DATA);
-       (void)INREG(CRTC_GEN_CNTL);
+       if (rinfo->errata & CHIP_ERRATA_PLL_DUMMYREADS)
+               radeon_pll_errata_after_index_slow(rinfo);
 }
 
+void radeon_pll_errata_after_data_slow(struct radeonfb_info *rinfo);
 static inline void radeon_pll_errata_after_data(struct radeonfb_info *rinfo)
 {
-       if (rinfo->errata & CHIP_ERRATA_PLL_DELAY) {
-               /* we can't deal with posted writes here ... */
-               _radeon_msleep(rinfo, 5);
-       }
-       if (rinfo->errata & CHIP_ERRATA_R300_CG) {
-               u32 save, tmp;
-               save = INREG(CLOCK_CNTL_INDEX);
-               tmp = save & ~(0x3f | PLL_WR_EN);
-               OUTREG(CLOCK_CNTL_INDEX, tmp);
-               tmp = INREG(CLOCK_CNTL_DATA);
-               OUTREG(CLOCK_CNTL_INDEX, save);
-       }
-}
-
-static inline u32 __INPLL(struct radeonfb_info *rinfo, u32 addr)
-{
-       u32 data;
-
-       OUTREG8(CLOCK_CNTL_INDEX, addr & 0x0000003f);
-       radeon_pll_errata_after_index(rinfo);
-       data = INREG(CLOCK_CNTL_DATA);
-       radeon_pll_errata_after_data(rinfo);
-       return data;
-}
-
-static inline void __OUTPLL(struct radeonfb_info *rinfo, unsigned int index,
-                           u32 val)
-{
-
-       OUTREG8(CLOCK_CNTL_INDEX, (index & 0x0000003f) | 0x00000080);
-       radeon_pll_errata_after_index(rinfo);
-       OUTREG(CLOCK_CNTL_DATA, val);
-       radeon_pll_errata_after_data(rinfo);
-}
-
-
-static inline void __OUTPLLP(struct radeonfb_info *rinfo, unsigned int index,
-                            u32 val, u32 mask)
-{
-       unsigned int tmp;
-
-       tmp  = __INPLL(rinfo, index);
-       tmp &= (mask);
-       tmp |= (val);
-       __OUTPLL(rinfo, index, tmp);
+       if (rinfo->errata & (CHIP_ERRATA_PLL_DELAY|CHIP_ERRATA_R300_CG))
+               radeon_pll_errata_after_data_slow(rinfo);
 }
 
+u32 __INPLL(struct radeonfb_info *rinfo, u32 addr);
+void __OUTPLL(struct radeonfb_info *rinfo, unsigned int index, u32 val);
+void __OUTPLLP(struct radeonfb_info *rinfo, unsigned int index,
+                            u32 val, u32 mask);
 
 #define INPLL(addr)                    __INPLL(rinfo, addr)
 #define OUTPLL(index, val)             __OUTPLL(rinfo, index, val)
@@ -532,58 +467,9 @@ static inline u32 radeon_get_dstbpp(u16 depth)
  * 2D Engine helper routines
  */
 
-static inline void _radeon_fifo_wait(struct radeonfb_info *rinfo, int entries)
-{
-       int i;
-
-       for (i=0; i<2000000; i++) {
-               if ((INREG(RBBM_STATUS) & 0x7f) >= entries)
-                       return;
-               udelay(1);
-       }
-       printk(KERN_ERR "radeonfb: FIFO Timeout !\n");
-}
-
-static inline void radeon_engine_flush (struct radeonfb_info *rinfo)
-{
-       int i;
-
-       /* Initiate flush */
-       OUTREGP(DSTCACHE_CTLSTAT, RB2D_DC_FLUSH_ALL,
-               ~RB2D_DC_FLUSH_ALL);
-
-       /* Ensure FIFO is empty, ie, make sure the flush commands
-        * has reached the cache
-        */
-       _radeon_fifo_wait (rinfo, 64);
-
-       /* Wait for the flush to complete */
-       for (i=0; i < 2000000; i++) {
-               if (!(INREG(DSTCACHE_CTLSTAT) & RB2D_DC_BUSY))
-                       return;
-               udelay(1);
-       }
-       printk(KERN_ERR "radeonfb: Flush Timeout !\n");
-}
-
-
-static inline void _radeon_engine_idle(struct radeonfb_info *rinfo)
-{
-       int i;
-
-       /* ensure FIFO is empty before waiting for idle */
-       _radeon_fifo_wait (rinfo, 64);
-
-       for (i=0; i<2000000; i++) {
-               if (((INREG(RBBM_STATUS) & GUI_ACTIVE)) == 0) {
-                       radeon_engine_flush (rinfo);
-                       return;
-               }
-               udelay(1);
-       }
-       printk(KERN_ERR "radeonfb: Idle Timeout !\n");
-}
-
+void _radeon_fifo_wait(struct radeonfb_info *rinfo, int entries);
+void radeon_engine_flush(struct radeonfb_info *rinfo);
+void _radeon_engine_idle(struct radeonfb_info *rinfo);
 
 #define radeon_engine_idle()           _radeon_engine_idle(rinfo)
 #define radeon_fifo_wait(entries)      _radeon_fifo_wait(rinfo,entries)
index 94322cc..8bf5f2f 100644 (file)
@@ -67,13 +67,17 @@ unsigned char *fb_ddc_read(struct i2c_adapter *adapter)
                msleep(13);
 
                algo_data->setscl(algo_data->data, 1);
-               for (j = 0; j < 5; j++) {
-                       msleep(10);
-                       if (algo_data->getscl(algo_data->data))
-                               break;
+               if (algo_data->getscl) {
+                       for (j = 0; j < 5; j++) {
+                               msleep(10);
+                               if (algo_data->getscl(algo_data->data))
+                                       break;
+                       }
+                       if (j == 5)
+                               continue;
+               } else {
+                       udelay(algo_data->udelay);
                }
-               if (j == 5)
-                       continue;
 
                algo_data->setsda(algo_data->data, 0);
                msleep(15);
@@ -89,10 +93,14 @@ unsigned char *fb_ddc_read(struct i2c_adapter *adapter)
                msleep(15);
 
                algo_data->setscl(algo_data->data, 1);
-               for (j = 0; j < 10; j++) {
-                       msleep(10);
-                       if (algo_data->getscl(algo_data->data))
-                               break;
+               if (algo_data->getscl) {
+                       for (j = 0; j < 10; j++) {
+                               msleep(10);
+                               if (algo_data->getscl(algo_data->data))
+                                       break;
+                       }
+               } else {
+                       udelay(algo_data->udelay);
                }
 
                algo_data->setsda(algo_data->data, 1);
index f19133a..f438546 100644 (file)
@@ -142,7 +142,7 @@ static const unsigned char watfmt[] = {
 
 struct gxt4500_par {
        void __iomem *regs;
-
+       int wc_cookie;
        int pixfmt;             /* pixel format, see DFA_PIX_* values */
 
        /* PLL parameters */
@@ -347,11 +347,12 @@ static void gxt4500_unpack_pixfmt(struct fb_var_screeninfo *var,
                break;
        }
        if (pixfmt != DFA_PIX_8BIT) {
-               var->green.offset = var->red.length;
-               var->blue.offset = var->green.offset + var->green.length;
+               var->blue.offset = 0;
+               var->green.offset = var->blue.length;
+               var->red.offset = var->green.offset + var->green.length;
                if (var->transp.length)
                        var->transp.offset =
-                               var->blue.offset + var->blue.length;
+                               var->red.offset + var->red.length;
        }
 }
 
@@ -525,7 +526,7 @@ static int gxt4500_setcolreg(unsigned int reg, unsigned int red,
                u32 val = reg;
                switch (par->pixfmt) {
                case DFA_PIX_16BIT_565:
-                       val |= (reg << 11) | (reg << 6);
+                       val |= (reg << 11) | (reg << 5);
                        break;
                case DFA_PIX_16BIT_1555:
                        val |= (reg << 10) | (reg << 5);
@@ -670,11 +671,22 @@ static int gxt4500_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        pci_set_drvdata(pdev, info);
 
+       par->wc_cookie = arch_phys_wc_add(info->fix.smem_start,
+                                         info->fix.smem_len);
+
+#ifdef __BIG_ENDIAN
        /* Set byte-swapping for DFA aperture for all pixel sizes */
        pci_write_config_dword(pdev, CFG_ENDIAN0, 0x333300);
+#else /* __LITTLE_ENDIAN */
+       /* not sure what this means but fgl23 driver does that */
+       pci_write_config_dword(pdev, CFG_ENDIAN0, 0x2300);
+/*     pci_write_config_dword(pdev, CFG_ENDIAN0 + 4, 0x400000);*/
+       pci_write_config_dword(pdev, CFG_ENDIAN0 + 8, 0x98530000);
+#endif
 
        info->fbops = &gxt4500_ops;
-       info->flags = FBINFO_FLAG_DEFAULT;
+       info->flags = FBINFO_FLAG_DEFAULT | FBINFO_HWACCEL_XPAN |
+                                           FBINFO_HWACCEL_YPAN;
 
        err = fb_alloc_cmap(&info->cmap, 256, 0);
        if (err) {
@@ -727,6 +739,7 @@ static void gxt4500_remove(struct pci_dev *pdev)
                return;
        par = info->par;
        unregister_framebuffer(info);
+       arch_phys_wc_del(par->wc_cookie);
        fb_dealloc_cmap(&info->cmap);
        iounmap(par->regs);
        iounmap(info->screen_base);
index 1fb3ea3..393ae1b 100644 (file)
@@ -276,11 +276,6 @@ static int _setcolreg(struct fb_info *info, u_int regno, u_int red, u_int green,
                if (r != 0)
                        break;
 
-               if (regno < 0) {
-                       r = -EINVAL;
-                       break;
-               }
-
                if (regno < 16) {
                        u16 pal;
                        pal = ((red >> (16 - var->red.length)) <<
index e4a32fe..53616b0 100644 (file)
@@ -351,13 +351,20 @@ struct omap_hdmi {
        struct regulator *vdda_reg;
 
        bool core_enabled;
-       bool display_enabled;
 
        struct omap_dss_device output;
 
        struct platform_device *audio_pdev;
        void (*audio_abort_cb)(struct device *dev);
        int wp_idlemode;
+
+       bool audio_configured;
+       struct omap_dss_audio audio_config;
+
+       /* This lock should be taken when booleans bellow are touched. */
+       spinlock_t audio_playing_lock;
+       bool audio_playing;
+       bool display_enabled;
 };
 
 #endif
index 6d3aa3f..94c8d55 100644 (file)
@@ -321,9 +321,22 @@ static int read_edid(u8 *buf, int len)
        return r;
 }
 
+static void hdmi_start_audio_stream(struct omap_hdmi *hd)
+{
+       hdmi_wp_audio_enable(&hd->wp, true);
+       hdmi4_audio_start(&hd->core, &hd->wp);
+}
+
+static void hdmi_stop_audio_stream(struct omap_hdmi *hd)
+{
+       hdmi4_audio_stop(&hd->core, &hd->wp);
+       hdmi_wp_audio_enable(&hd->wp, false);
+}
+
 static int hdmi_display_enable(struct omap_dss_device *dssdev)
 {
        struct omap_dss_device *out = &hdmi.output;
+       unsigned long flags;
        int r = 0;
 
        DSSDBG("ENTER hdmi_display_enable\n");
@@ -342,7 +355,21 @@ static int hdmi_display_enable(struct omap_dss_device *dssdev)
                goto err0;
        }
 
+       if (hdmi.audio_configured) {
+               r = hdmi4_audio_config(&hdmi.core, &hdmi.wp, &hdmi.audio_config,
+                                      hdmi.cfg.timings.pixelclock);
+               if (r) {
+                       DSSERR("Error restoring audio configuration: %d", r);
+                       hdmi.audio_abort_cb(&hdmi.pdev->dev);
+                       hdmi.audio_configured = false;
+               }
+       }
+
+       spin_lock_irqsave(&hdmi.audio_playing_lock, flags);
+       if (hdmi.audio_configured && hdmi.audio_playing)
+               hdmi_start_audio_stream(&hdmi);
        hdmi.display_enabled = true;
+       spin_unlock_irqrestore(&hdmi.audio_playing_lock, flags);
 
        mutex_unlock(&hdmi.lock);
        return 0;
@@ -354,17 +381,19 @@ err0:
 
 static void hdmi_display_disable(struct omap_dss_device *dssdev)
 {
+       unsigned long flags;
+
        DSSDBG("Enter hdmi_display_disable\n");
 
        mutex_lock(&hdmi.lock);
 
-       if (hdmi.audio_pdev && hdmi.audio_abort_cb)
-               hdmi.audio_abort_cb(&hdmi.audio_pdev->dev);
+       spin_lock_irqsave(&hdmi.audio_playing_lock, flags);
+       hdmi_stop_audio_stream(&hdmi);
+       hdmi.display_enabled = false;
+       spin_unlock_irqrestore(&hdmi.audio_playing_lock, flags);
 
        hdmi_power_off_full(dssdev);
 
-       hdmi.display_enabled = false;
-
        mutex_unlock(&hdmi.lock);
 }
 
@@ -568,6 +597,8 @@ static int hdmi_audio_shutdown(struct device *dev)
 
        mutex_lock(&hd->lock);
        hd->audio_abort_cb = NULL;
+       hd->audio_configured = false;
+       hd->audio_playing = false;
        mutex_unlock(&hd->lock);
 
        return 0;
@@ -576,25 +607,34 @@ static int hdmi_audio_shutdown(struct device *dev)
 static int hdmi_audio_start(struct device *dev)
 {
        struct omap_hdmi *hd = dev_get_drvdata(dev);
+       unsigned long flags;
 
        WARN_ON(!hdmi_mode_has_audio(&hd->cfg));
-       WARN_ON(!hd->display_enabled);
 
-       hdmi_wp_audio_enable(&hd->wp, true);
-       hdmi4_audio_start(&hd->core, &hd->wp);
+       spin_lock_irqsave(&hd->audio_playing_lock, flags);
+
+       if (hd->display_enabled)
+               hdmi_start_audio_stream(hd);
+       hd->audio_playing = true;
 
+       spin_unlock_irqrestore(&hd->audio_playing_lock, flags);
        return 0;
 }
 
 static void hdmi_audio_stop(struct device *dev)
 {
        struct omap_hdmi *hd = dev_get_drvdata(dev);
+       unsigned long flags;
 
        WARN_ON(!hdmi_mode_has_audio(&hd->cfg));
-       WARN_ON(!hd->display_enabled);
 
-       hdmi4_audio_stop(&hd->core, &hd->wp);
-       hdmi_wp_audio_enable(&hd->wp, false);
+       spin_lock_irqsave(&hd->audio_playing_lock, flags);
+
+       if (hd->display_enabled)
+               hdmi_stop_audio_stream(hd);
+       hd->audio_playing = false;
+
+       spin_unlock_irqrestore(&hd->audio_playing_lock, flags);
 }
 
 static int hdmi_audio_config(struct device *dev,
@@ -612,7 +652,10 @@ static int hdmi_audio_config(struct device *dev,
 
        ret = hdmi4_audio_config(&hd->core, &hd->wp, dss_audio,
                                 hd->cfg.timings.pixelclock);
-
+       if (!ret) {
+               hd->audio_configured = true;
+               hd->audio_config = *dss_audio;
+       }
 out:
        mutex_unlock(&hd->lock);
 
@@ -657,6 +700,7 @@ static int hdmi4_bind(struct device *dev, struct device *master, void *data)
        dev_set_drvdata(&pdev->dev, &hdmi);
 
        mutex_init(&hdmi.lock);
+       spin_lock_init(&hdmi.audio_playing_lock);
 
        if (pdev->dev.of_node) {
                r = hdmi_probe_of(pdev);
index 7f87578..b59ba79 100644 (file)
@@ -349,9 +349,24 @@ static int read_edid(u8 *buf, int len)
        return r;
 }
 
+static void hdmi_start_audio_stream(struct omap_hdmi *hd)
+{
+       REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2);
+       hdmi_wp_audio_enable(&hd->wp, true);
+       hdmi_wp_audio_core_req_enable(&hd->wp, true);
+}
+
+static void hdmi_stop_audio_stream(struct omap_hdmi *hd)
+{
+       hdmi_wp_audio_core_req_enable(&hd->wp, false);
+       hdmi_wp_audio_enable(&hd->wp, false);
+       REG_FLD_MOD(hd->wp.base, HDMI_WP_SYSCONFIG, hd->wp_idlemode, 3, 2);
+}
+
 static int hdmi_display_enable(struct omap_dss_device *dssdev)
 {
        struct omap_dss_device *out = &hdmi.output;
+       unsigned long flags;
        int r = 0;
 
        DSSDBG("ENTER hdmi_display_enable\n");
@@ -370,7 +385,21 @@ static int hdmi_display_enable(struct omap_dss_device *dssdev)
                goto err0;
        }
 
+       if (hdmi.audio_configured) {
+               r = hdmi5_audio_config(&hdmi.core, &hdmi.wp, &hdmi.audio_config,
+                                      hdmi.cfg.timings.pixelclock);
+               if (r) {
+                       DSSERR("Error restoring audio configuration: %d", r);
+                       hdmi.audio_abort_cb(&hdmi.pdev->dev);
+                       hdmi.audio_configured = false;
+               }
+       }
+
+       spin_lock_irqsave(&hdmi.audio_playing_lock, flags);
+       if (hdmi.audio_configured && hdmi.audio_playing)
+               hdmi_start_audio_stream(&hdmi);
        hdmi.display_enabled = true;
+       spin_unlock_irqrestore(&hdmi.audio_playing_lock, flags);
 
        mutex_unlock(&hdmi.lock);
        return 0;
@@ -382,17 +411,19 @@ err0:
 
 static void hdmi_display_disable(struct omap_dss_device *dssdev)
 {
+       unsigned long flags;
+
        DSSDBG("Enter hdmi_display_disable\n");
 
        mutex_lock(&hdmi.lock);
 
-       if (hdmi.audio_pdev && hdmi.audio_abort_cb)
-               hdmi.audio_abort_cb(&hdmi.audio_pdev->dev);
+       spin_lock_irqsave(&hdmi.audio_playing_lock, flags);
+       hdmi_stop_audio_stream(&hdmi);
+       hdmi.display_enabled = false;
+       spin_unlock_irqrestore(&hdmi.audio_playing_lock, flags);
 
        hdmi_power_off_full(dssdev);
 
-       hdmi.display_enabled = false;
-
        mutex_unlock(&hdmi.lock);
 }
 
@@ -596,6 +627,8 @@ static int hdmi_audio_shutdown(struct device *dev)
 
        mutex_lock(&hd->lock);
        hd->audio_abort_cb = NULL;
+       hd->audio_configured = false;
+       hd->audio_playing = false;
        mutex_unlock(&hd->lock);
 
        return 0;
@@ -604,32 +637,34 @@ static int hdmi_audio_shutdown(struct device *dev)
 static int hdmi_audio_start(struct device *dev)
 {
        struct omap_hdmi *hd = dev_get_drvdata(dev);
+       unsigned long flags;
 
        WARN_ON(!hdmi_mode_has_audio(&hd->cfg));
-       WARN_ON(!hd->display_enabled);
 
-       /* No-idle while playing audio, store the old value */
-       hd->wp_idlemode = REG_GET(hdmi.wp.base, HDMI_WP_SYSCONFIG, 3, 2);
-       REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2);
+       spin_lock_irqsave(&hd->audio_playing_lock, flags);
 
-       hdmi_wp_audio_enable(&hd->wp, true);
-       hdmi_wp_audio_core_req_enable(&hd->wp, true);
+       if (hd->display_enabled)
+               hdmi_start_audio_stream(hd);
+       hd->audio_playing = true;
 
+       spin_unlock_irqrestore(&hd->audio_playing_lock, flags);
        return 0;
 }
 
 static void hdmi_audio_stop(struct device *dev)
 {
        struct omap_hdmi *hd = dev_get_drvdata(dev);
+       unsigned long flags;
 
        WARN_ON(!hdmi_mode_has_audio(&hd->cfg));
-       WARN_ON(!hd->display_enabled);
 
-       hdmi_wp_audio_core_req_enable(&hd->wp, false);
-       hdmi_wp_audio_enable(&hd->wp, false);
+       spin_lock_irqsave(&hd->audio_playing_lock, flags);
+
+       if (hd->display_enabled)
+               hdmi_stop_audio_stream(hd);
+       hd->audio_playing = false;
 
-       /* Playback stopped, restore original idlemode */
-       REG_FLD_MOD(hdmi.wp.base, HDMI_WP_SYSCONFIG, hd->wp_idlemode, 3, 2);
+       spin_unlock_irqrestore(&hd->audio_playing_lock, flags);
 }
 
 static int hdmi_audio_config(struct device *dev,
@@ -648,6 +683,10 @@ static int hdmi_audio_config(struct device *dev,
        ret = hdmi5_audio_config(&hd->core, &hd->wp, dss_audio,
                                 hd->cfg.timings.pixelclock);
 
+       if (!ret) {
+               hd->audio_configured = true;
+               hd->audio_config = *dss_audio;
+       }
 out:
        mutex_unlock(&hd->lock);
 
@@ -678,6 +717,11 @@ static int hdmi_audio_register(struct device *dev)
        if (IS_ERR(hdmi.audio_pdev))
                return PTR_ERR(hdmi.audio_pdev);
 
+       hdmi_runtime_get();
+       hdmi.wp_idlemode =
+               REG_GET(hdmi.wp.base, HDMI_WP_SYSCONFIG, 3, 2);
+       hdmi_runtime_put();
+
        return 0;
 }
 
@@ -692,6 +736,7 @@ static int hdmi5_bind(struct device *dev, struct device *master, void *data)
        dev_set_drvdata(&pdev->dev, &hdmi);
 
        mutex_init(&hdmi.lock);
+       spin_lock_init(&hdmi.audio_playing_lock);
 
        if (pdev->dev.of_node) {
                r = hdmi_probe_of(pdev);
index 93f4c90..fa34808 100644 (file)
@@ -6,16 +6,16 @@
  * Licensed under the GPLv2 or later.
  */
 
-#include <linux/module.h>
 #include <linux/backlight.h>
-#include <linux/kernel.h>
-#include <linux/i2c.h>
+#include <linux/delay.h>
 #include <linux/fb.h>
-#include <linux/uaccess.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
 #include <linux/of_device.h>
 #include <linux/of_gpio.h>
 #include <linux/pwm.h>
-#include <linux/delay.h>
+#include <linux/uaccess.h>
 
 #define SSD1307FB_DATA                 0x40
 #define SSD1307FB_COMMAND              0x80
@@ -495,6 +495,12 @@ static struct ssd1307fb_deviceinfo ssd1307fb_ssd1307_deviceinfo = {
        .need_pwm = 1,
 };
 
+static struct ssd1307fb_deviceinfo ssd1307fb_ssd1309_deviceinfo = {
+       .default_vcomh = 0x34,
+       .default_dclk_div = 1,
+       .default_dclk_frq = 10,
+};
+
 static const struct of_device_id ssd1307fb_of_match[] = {
        {
                .compatible = "solomon,ssd1305fb-i2c",
@@ -508,6 +514,10 @@ static const struct of_device_id ssd1307fb_of_match[] = {
                .compatible = "solomon,ssd1307fb-i2c",
                .data = (void *)&ssd1307fb_ssd1307_deviceinfo,
        },
+       {
+               .compatible = "solomon,ssd1309fb-i2c",
+               .data = (void *)&ssd1307fb_ssd1309_deviceinfo,
+       },
        {},
 };
 MODULE_DEVICE_TABLE(of, ssd1307fb_of_match);
@@ -709,6 +719,7 @@ static const struct i2c_device_id ssd1307fb_i2c_id[] = {
        { "ssd1305fb", 0 },
        { "ssd1306fb", 0 },
        { "ssd1307fb", 0 },
+       { "ssd1309fb", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, ssd1307fb_i2c_id);
index 01b43e9..8a5bbc1 100644 (file)
@@ -25,6 +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 @@ struct tridentfb_par {
                (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 @@ static struct fb_fix_screeninfo tridentfb_fix = {
 /* 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 @@ static inline u32 readmmr(struct tridentfb_par *par, u16 r)
        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.
  */
@@ -1346,6 +1467,7 @@ static int trident_pci_probe(struct pci_dev *dev,
        struct tridentfb_par *default_par;
        int chip3D;
        int chip_id;
+       bool found = false;
 
        err = pci_enable_device(dev);
        if (err)
@@ -1499,6 +1621,7 @@ static int trident_pci_probe(struct pci_dev *dev,
        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;
@@ -1511,11 +1634,56 @@ static int trident_pci_probe(struct pci_dev *dev,
                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;
@@ -1536,6 +1704,8 @@ static int trident_pci_probe(struct pci_dev *dev,
        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 +1725,8 @@ static void trident_pci_remove(struct pci_dev *dev)
        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);