Merge branch 'fbdev/edid'
authorPaul Mundt <lethal@linux-sh.org>
Tue, 16 Nov 2010 07:25:03 +0000 (16:25 +0900)
committerPaul Mundt <lethal@linux-sh.org>
Tue, 16 Nov 2010 07:25:03 +0000 (16:25 +0900)
16 files changed:
drivers/video/Kconfig
drivers/video/Makefile
drivers/video/sh_mobile_hdmi.c
drivers/video/sh_mobile_lcdcfb.c
drivers/video/via/via-core.c
drivers/video/via/via-gpio.c
drivers/video/via/viafbdev.c
drivers/video/via/viafbdev.h
drivers/video/vt8500lcdfb.c [new file with mode: 0644]
drivers/video/vt8500lcdfb.h [new file with mode: 0644]
drivers/video/wm8505fb.c [new file with mode: 0644]
drivers/video/wm8505fb_regs.h [new file with mode: 0644]
drivers/video/wmt_ge_rops.c [new file with mode: 0644]
drivers/video/wmt_ge_rops.h [new file with mode: 0644]
include/linux/via-core.h
include/video/sh_mobile_hdmi.h

index 27c1fb4b1e0d71f91dcdd7d528dafa3b3457cb32..954f6e9d8d5ab841c2ea70d9937ab3d1ee730128 100644 (file)
@@ -186,6 +186,14 @@ config FB_SYS_FOPS
        depends on FB
        default n
 
+config FB_WMT_GE_ROPS
+       tristate
+       depends on FB
+       default n
+       ---help---
+         Include functions for accelerated rectangle filling and area
+         copying using WonderMedia Graphics Engine operations.
+
 config FB_DEFERRED_IO
        bool
        depends on FB
@@ -1722,6 +1730,24 @@ config FB_AU1200
          various panels and CRTs by passing in kernel cmd line option
          au1200fb:panel=<name>.
 
+config FB_VT8500
+       bool "VT8500 LCD Driver"
+       depends on (FB = y) && ARM && ARCH_VT8500 && VTWM_VERSION_VT8500
+       select FB_WMT_GE_ROPS
+       select FB_SYS_IMAGEBLIT
+       help
+         This is the framebuffer driver for VIA VT8500 integrated LCD
+         controller.
+
+config FB_WM8505
+       bool "WM8505 frame buffer support"
+       depends on (FB = y) && ARM && ARCH_VT8500 && VTWM_VERSION_WM8505
+       select FB_WMT_GE_ROPS
+       select FB_SYS_IMAGEBLIT
+       help
+         This is the framebuffer driver for WonderMedia WM8505
+         integrated LCD controller.
+
 source "drivers/video/geode/Kconfig"
 
 config FB_HIT
index 485e8ed1318c4bd0a055553f784b6c642ca6cf81..8d916dcb379fda4229b151354b887409ef037164 100644 (file)
@@ -26,6 +26,7 @@ obj-$(CONFIG_FB_SVGALIB)       += svgalib.o
 obj-$(CONFIG_FB_MACMODES)      += macmodes.o
 obj-$(CONFIG_FB_DDC)           += fb_ddc.o
 obj-$(CONFIG_FB_DEFERRED_IO)   += fb_defio.o
+obj-$(CONFIG_FB_WMT_GE_ROPS)   += wmt_ge_rops.o
 
 # Hardware specific drivers go first
 obj-$(CONFIG_FB_AMIGA)            += amifb.o c2p_planar.o
@@ -104,6 +105,8 @@ obj-$(CONFIG_FB_W100)                 += w100fb.o
 obj-$(CONFIG_FB_TMIO)            += tmiofb.o
 obj-$(CONFIG_FB_AU1100)                  += au1100fb.o
 obj-$(CONFIG_FB_AU1200)                  += au1200fb.o
+obj-$(CONFIG_FB_VT8500)                  += vt8500lcdfb.o
+obj-$(CONFIG_FB_WM8505)                  += wm8505fb.o
 obj-$(CONFIG_FB_PMAG_AA)         += pmag-aa-fb.o
 obj-$(CONFIG_FB_PMAG_BA)         += pmag-ba-fb.o
 obj-$(CONFIG_FB_PMAGB_B)         += pmagb-b-fb.o
index 55b3077ff6fff567e2fc3c1831b1255498cb1c07..3b4cf987fb4377dbf33a6e89a23a62e6a71d9e82 100644 (file)
@@ -209,7 +209,8 @@ enum hotplug_state {
 struct sh_hdmi {
        void __iomem *base;
        enum hotplug_state hp_state;    /* hot-plug status */
-       bool preprogrammed_mode;        /* use a pre-programmed VIC or the external mode */
+       u8 preprogrammed_vic;           /* use a pre-programmed VIC or
+                                          the external mode */
        struct clk *hdmi_clk;
        struct device *dev;
        struct fb_info *info;
@@ -342,7 +343,7 @@ static void sh_hdmi_external_video_param(struct sh_hdmi *hdmi)
        hdmi_write(hdmi, var->vsync_len, HDMI_EXTERNAL_V_DURATION);
 
        /* Set bit 0 of HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS here for external mode */
-       if (!hdmi->preprogrammed_mode)
+       if (!hdmi->preprogrammed_vic)
                hdmi_write(hdmi, sync | 1 | (voffset << 4),
                           HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS);
 }
@@ -466,7 +467,18 @@ static void sh_hdmi_audio_config(struct sh_hdmi *hdmi)
  */
 static void sh_hdmi_phy_config(struct sh_hdmi *hdmi)
 {
-       if (hdmi->var.yres > 480) {
+       if (hdmi->var.pixclock < 10000) {
+               /* for 1080p8bit 148MHz */
+               hdmi_write(hdmi, 0x1d, HDMI_SLIPHDMIT_PARAM_SETTINGS_1);
+               hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_2);
+               hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_3);
+               hdmi_write(hdmi, 0x4c, HDMI_SLIPHDMIT_PARAM_SETTINGS_5);
+               hdmi_write(hdmi, 0x1e, HDMI_SLIPHDMIT_PARAM_SETTINGS_6);
+               hdmi_write(hdmi, 0x48, HDMI_SLIPHDMIT_PARAM_SETTINGS_7);
+               hdmi_write(hdmi, 0x0e, HDMI_SLIPHDMIT_PARAM_SETTINGS_8);
+               hdmi_write(hdmi, 0x25, HDMI_SLIPHDMIT_PARAM_SETTINGS_9);
+               hdmi_write(hdmi, 0x04, HDMI_SLIPHDMIT_PARAM_SETTINGS_10);
+       } else if (hdmi->var.pixclock < 30000) {
                /* 720p, 8bit, 74.25MHz. Might need to be adjusted for other formats */
                /*
                 * [1:0]        Speed_A
@@ -565,13 +577,11 @@ static void sh_hdmi_avi_infoframe_setup(struct sh_hdmi *hdmi)
        hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB3);
 
        /*
-        * VIC = 1280 x 720p: ignored if external config is used
-        * Send 2 for 720 x 480p, 16 for 1080p, ignored in external mode
+        * VIC should be ignored if external config is used, so, we could just use 0,
+        * but play safe and use a valid value in any case just in case
         */
-       if (hdmi->var.yres == 1080 && hdmi->var.xres == 1920)
-               vic = 16;
-       else if (hdmi->var.yres == 480 && hdmi->var.xres == 720)
-               vic = 2;
+       if (hdmi->preprogrammed_vic)
+               vic = hdmi->preprogrammed_vic;
        else
                vic = 4;
        hdmi_write(hdmi, vic, HDMI_CTRL_PKT_BUF_ACCESS_PB4);
@@ -685,11 +695,21 @@ static void sh_hdmi_configure(struct sh_hdmi *hdmi)
 }
 
 static unsigned long sh_hdmi_rate_error(struct sh_hdmi *hdmi,
-                                       const struct fb_videomode *mode)
+               const struct fb_videomode *mode,
+               unsigned long *hdmi_rate, unsigned long *parent_rate)
 {
-       long target = PICOS2KHZ(mode->pixclock) * 1000,
-               rate = clk_round_rate(hdmi->hdmi_clk, target);
-       unsigned long rate_error = rate > 0 ? abs(rate - target) : ULONG_MAX;
+       unsigned long target = PICOS2KHZ(mode->pixclock) * 1000, rate_error;
+       struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data;
+
+       *hdmi_rate = clk_round_rate(hdmi->hdmi_clk, target);
+       if ((long)*hdmi_rate < 0)
+               *hdmi_rate = clk_get_rate(hdmi->hdmi_clk);
+
+       rate_error = (long)*hdmi_rate > 0 ? abs(*hdmi_rate - target) : ULONG_MAX;
+       if (rate_error && pdata->clk_optimize_parent)
+               rate_error = pdata->clk_optimize_parent(target, hdmi_rate, parent_rate);
+       else if (clk_get_parent(hdmi->hdmi_clk))
+               *parent_rate = clk_get_rate(clk_get_parent(hdmi->hdmi_clk));
 
        dev_dbg(hdmi->dev, "%u-%u-%u-%u x %u-%u-%u-%u\n",
                mode->left_margin, mode->xres,
@@ -697,14 +717,15 @@ static unsigned long sh_hdmi_rate_error(struct sh_hdmi *hdmi,
                mode->upper_margin, mode->yres,
                mode->lower_margin, mode->vsync_len);
 
-       dev_dbg(hdmi->dev, "\t@%lu(+/-%lu)Hz, e=%lu / 1000, r=%uHz\n", target,
-                rate_error, rate_error ? 10000 / (10 * target / rate_error) : 0,
-                mode->refresh);
+       dev_dbg(hdmi->dev, "\t@%lu(+/-%lu)Hz, e=%lu / 1000, r=%uHz, p=%luHz\n", target,
+               rate_error, rate_error ? 10000 / (10 * target / rate_error) : 0,
+               mode->refresh, *parent_rate);
 
        return rate_error;
 }
 
-static int sh_hdmi_read_edid(struct sh_hdmi *hdmi)
+static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate,
+                            unsigned long *parent_rate)
 {
        struct fb_var_screeninfo tmpvar;
        struct fb_var_screeninfo *var = &tmpvar;
@@ -754,11 +775,14 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi)
        for (i = 0, mode = hdmi->monspec.modedb;
             f_width && f_height && i < hdmi->monspec.modedb_len && !exact_match;
             i++, mode++) {
-               unsigned long rate_error = sh_hdmi_rate_error(hdmi, mode);
+               unsigned long rate_error;
 
                /* No interest in unmatching modes */
                if (f_width != mode->xres || f_height != mode->yres)
                        continue;
+
+               rate_error = sh_hdmi_rate_error(hdmi, mode, hdmi_rate, parent_rate);
+
                if (f_refresh == mode->refresh || (!f_refresh && !rate_error))
                        /*
                         * Exact match if either the refresh rate matches or it
@@ -802,7 +826,7 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi)
 
                if (modelist) {
                        found = &modelist->mode;
-                       found_rate_error = sh_hdmi_rate_error(hdmi, found);
+                       found_rate_error = sh_hdmi_rate_error(hdmi, found, hdmi_rate, parent_rate);
                }
        }
 
@@ -810,16 +834,27 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi)
        if (!found)
                return -ENXIO;
 
-       dev_info(hdmi->dev, "Using %s mode %ux%u@%uHz (%luHz), clock error %luHz\n",
-                modelist ? "default" : "EDID", found->xres, found->yres,
-                found->refresh, PICOS2KHZ(found->pixclock) * 1000, found_rate_error);
-
-       if ((found->xres == 720 && found->yres == 480) ||
-           (found->xres == 1280 && found->yres == 720) ||
-           (found->xres == 1920 && found->yres == 1080))
-               hdmi->preprogrammed_mode = true;
+       if (found->xres == 640 && found->yres == 480 && found->refresh == 60)
+               hdmi->preprogrammed_vic = 1;
+       else if (found->xres == 720 && found->yres == 480 && found->refresh == 60)
+               hdmi->preprogrammed_vic = 2;
+       else if (found->xres == 720 && found->yres == 576 && found->refresh == 50)
+               hdmi->preprogrammed_vic = 17;
+       else if (found->xres == 1280 && found->yres == 720 && found->refresh == 60)
+               hdmi->preprogrammed_vic = 4;
+       else if (found->xres == 1920 && found->yres == 1080 && found->refresh == 24)
+               hdmi->preprogrammed_vic = 32;
+       else if (found->xres == 1920 && found->yres == 1080 && found->refresh == 50)
+               hdmi->preprogrammed_vic = 31;
+       else if (found->xres == 1920 && found->yres == 1080 && found->refresh == 60)
+               hdmi->preprogrammed_vic = 16;
        else
-               hdmi->preprogrammed_mode = false;
+               hdmi->preprogrammed_vic = 0;
+
+       dev_dbg(hdmi->dev, "Using %s %s mode %ux%u@%uHz (%luHz), clock error %luHz\n",
+               modelist ? "default" : "EDID", hdmi->preprogrammed_vic ? "VIC" : "external",
+               found->xres, found->yres, found->refresh,
+               PICOS2KHZ(found->pixclock) * 1000, found_rate_error);
 
        fb_videomode_to_var(&hdmi->var, found);
        sh_hdmi_external_video_param(hdmi);
@@ -972,39 +1007,37 @@ static bool sh_hdmi_must_reconfigure(struct sh_hdmi *hdmi)
 
 /**
  * sh_hdmi_clk_configure() - set HDMI clock frequency and enable the clock
- * @hdmi:      driver context
- * @pixclock:  pixel clock period in picoseconds
- * return:     configured positive rate if successful
- *             0 if couldn't set the rate, but managed to enable the clock
- *             negative error, if couldn't enable the clock
+ * @hdmi:              driver context
+ * @hdmi_rate:         HDMI clock frequency in Hz
+ * @parent_rate:       if != 0 - set parent clock rate for optimal precision
+ * return:             configured positive rate if successful
+ *                     0 if couldn't set the rate, but managed to enable the
+ *                     clock, negative error, if couldn't enable the clock
  */
-static long sh_hdmi_clk_configure(struct sh_hdmi *hdmi, unsigned long pixclock)
+static long sh_hdmi_clk_configure(struct sh_hdmi *hdmi, unsigned long hdmi_rate,
+                                 unsigned long parent_rate)
 {
-       long rate;
        int ret;
 
-       rate = PICOS2KHZ(pixclock) * 1000;
-       rate = clk_round_rate(hdmi->hdmi_clk, rate);
-       if (rate > 0) {
-               ret = clk_set_rate(hdmi->hdmi_clk, rate);
+       if (parent_rate && clk_get_parent(hdmi->hdmi_clk)) {
+               ret = clk_set_rate(clk_get_parent(hdmi->hdmi_clk), parent_rate);
                if (ret < 0) {
-                       dev_warn(hdmi->dev, "Cannot set rate %ld: %d\n", rate, ret);
-                       rate = 0;
+                       dev_warn(hdmi->dev, "Cannot set parent rate %ld: %d\n", parent_rate, ret);
+                       hdmi_rate = clk_round_rate(hdmi->hdmi_clk, hdmi_rate);
                } else {
-                       dev_dbg(hdmi->dev, "HDMI set frequency %lu\n", rate);
+                       dev_dbg(hdmi->dev, "HDMI set parent frequency %lu\n", parent_rate);
                }
-       } else {
-               rate = 0;
-               dev_warn(hdmi->dev, "Cannot get suitable rate: %ld\n", rate);
        }
 
-       ret = clk_enable(hdmi->hdmi_clk);
+       ret = clk_set_rate(hdmi->hdmi_clk, hdmi_rate);
        if (ret < 0) {
-               dev_err(hdmi->dev, "Cannot enable clock: %d\n", ret);
-               return ret;
+               dev_warn(hdmi->dev, "Cannot set rate %ld: %d\n", hdmi_rate, ret);
+               hdmi_rate = 0;
+       } else {
+               dev_dbg(hdmi->dev, "HDMI set frequency %lu\n", hdmi_rate);
        }
 
-       return rate;
+       return hdmi_rate;
 }
 
 /* Hotplug interrupt occurred, read EDID */
@@ -1024,16 +1057,17 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work)
        mutex_lock(&hdmi->mutex);
 
        if (hdmi->hp_state == HDMI_HOTPLUG_EDID_DONE) {
+               unsigned long parent_rate = 0, hdmi_rate;
+
                /* A device has been plugged in */
                pm_runtime_get_sync(hdmi->dev);
 
-               ret = sh_hdmi_read_edid(hdmi);
+               ret = sh_hdmi_read_edid(hdmi, &hdmi_rate, &parent_rate);
                if (ret < 0)
                        goto out;
 
                /* Reconfigure the clock */
-               clk_disable(hdmi->hdmi_clk);
-               ret = sh_hdmi_clk_configure(hdmi, hdmi->var.pixclock);
+               ret = sh_hdmi_clk_configure(hdmi, hdmi_rate, parent_rate);
                if (ret < 0)
                        goto out;
 
@@ -1071,6 +1105,10 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work)
                if (!hdmi->info)
                        goto out;
 
+               hdmi->monspec.modedb_len = 0;
+               fb_destroy_modedb(hdmi->monspec.modedb);
+               hdmi->monspec.modedb = NULL;
+
                acquire_console_sem();
 
                /* HDMI disconnect */
@@ -1078,7 +1116,6 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work)
 
                release_console_sem();
                pm_runtime_put(hdmi->dev);
-               fb_destroy_modedb(hdmi->monspec.modedb);
        }
 
 out:
@@ -1163,13 +1200,22 @@ static int __init sh_hdmi_probe(struct platform_device *pdev)
                goto egetclk;
        }
 
-       /* Some arbitrary relaxed pixclock just to get things started */
-       rate = sh_hdmi_clk_configure(hdmi, 37037);
+       /* An arbitrary relaxed pixclock just to get things started: from standard 480p */
+       rate = clk_round_rate(hdmi->hdmi_clk, PICOS2KHZ(37037));
+       if (rate > 0)
+               rate = sh_hdmi_clk_configure(hdmi, rate, 0);
+
        if (rate < 0) {
                ret = rate;
                goto erate;
        }
 
+       ret = clk_enable(hdmi->hdmi_clk);
+       if (ret < 0) {
+               dev_err(hdmi->dev, "Cannot enable clock: %d\n", ret);
+               goto erate;
+       }
+
        dev_dbg(&pdev->dev, "Enabled HDMI clock at %luHz\n", rate);
 
        if (!request_mem_region(res->start, resource_size(res), dev_name(&pdev->dev))) {
@@ -1187,10 +1233,6 @@ static int __init sh_hdmi_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, hdmi);
 
-       /* Product and revision IDs are 0 in sh-mobile version */
-       dev_info(&pdev->dev, "Detected HDMI controller 0x%x:0x%x\n",
-                hdmi_read(hdmi, HDMI_PRODUCT_ID), hdmi_read(hdmi, HDMI_REVISION_ID));
-
        /* Set up LCDC callbacks */
        board_cfg = &pdata->lcd_chan->board_cfg;
        board_cfg->owner = THIS_MODULE;
@@ -1203,6 +1245,10 @@ static int __init sh_hdmi_probe(struct platform_device *pdev)
        pm_runtime_enable(&pdev->dev);
        pm_runtime_resume(&pdev->dev);
 
+       /* Product and revision IDs are 0 in sh-mobile version */
+       dev_info(&pdev->dev, "Detected HDMI controller 0x%x:0x%x\n",
+                hdmi_read(hdmi, HDMI_PRODUCT_ID), hdmi_read(hdmi, HDMI_REVISION_ID));
+
        ret = request_irq(irq, sh_hdmi_hotplug, 0,
                          dev_name(&pdev->dev), hdmi);
        if (ret < 0) {
index 50963739a40977832e03285b1049be65f4cde1dd..37419ba2c29a0cda8c1e3bffa3934ce3cbda7774 100644 (file)
@@ -54,8 +54,8 @@ static int lcdc_shared_regs[] = {
 };
 #define NR_SHARED_REGS ARRAY_SIZE(lcdc_shared_regs)
 
-#define DEFAULT_XRES 1280
-#define DEFAULT_YRES 1024
+#define MAX_XRES 1920
+#define MAX_YRES 1080
 
 static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = {
        [LDDCKPAT1R] = 0x400,
@@ -115,15 +115,16 @@ static const struct fb_videomode default_720p = {
        .xres = 1280,
        .yres = 720,
 
-       .left_margin = 200,
-       .right_margin = 88,
-       .hsync_len = 48,
+       .left_margin = 220,
+       .right_margin = 110,
+       .hsync_len = 40,
 
        .upper_margin = 20,
        .lower_margin = 5,
        .vsync_len = 5,
 
        .pixclock = 13468,
+       .refresh = 60,
        .sync = FB_SYNC_VERT_HIGH_ACT | FB_SYNC_HOR_HIGH_ACT,
 };
 
@@ -913,22 +914,12 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in
 {
        struct sh_mobile_lcdc_chan *ch = info->par;
 
-       if (var->xres < 160 || var->xres > 1920 ||
-           var->yres < 120 || var->yres > 1080 ||
-           var->left_margin < 32 || var->left_margin > 320 ||
-           var->right_margin < 12 || var->right_margin > 240 ||
-           var->upper_margin < 12 || var->upper_margin > 120 ||
-           var->lower_margin < 1 || var->lower_margin > 64 ||
-           var->hsync_len < 32 || var->hsync_len > 240 ||
-           var->vsync_len < 2 || var->vsync_len > 64 ||
-           var->pixclock < 6000 || var->pixclock > 40000 ||
+       if (var->xres > MAX_XRES || var->yres > MAX_YRES ||
            var->xres * var->yres * (ch->cfg.bpp / 8) * 2 > info->fix.smem_len) {
-               dev_warn(info->dev, "Invalid info: %u %u %u %u %u %u %u %u %u!\n",
-                        var->xres, var->yres,
-                        var->left_margin, var->right_margin,
-                        var->upper_margin, var->lower_margin,
-                        var->hsync_len, var->vsync_len,
-                        var->pixclock);
+               dev_warn(info->dev, "Invalid info: %u-%u-%u-%u x %u-%u-%u-%u @ %lukHz!\n",
+                        var->left_margin, var->xres, var->right_margin, var->hsync_len,
+                        var->upper_margin, var->yres, var->lower_margin, var->vsync_len,
+                        PICOS2KHZ(var->pixclock));
                return -EINVAL;
        }
        return 0;
@@ -1197,6 +1188,7 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
                const struct fb_videomode *mode = cfg->lcd_cfg;
                unsigned long max_size = 0;
                int k;
+               int num_cfg;
 
                ch->info = framebuffer_alloc(0, &pdev->dev);
                if (!ch->info) {
@@ -1224,7 +1216,7 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
                }
 
                if (!mode)
-                       max_size = DEFAULT_XRES * DEFAULT_YRES;
+                       max_size = MAX_XRES * MAX_YRES;
                else if (max_cfg)
                        dev_dbg(&pdev->dev, "Found largest videomode %ux%u\n",
                                max_cfg->xres, max_cfg->yres);
@@ -1232,8 +1224,14 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
                info->fix = sh_mobile_lcdc_fix;
                info->fix.smem_len = max_size * (cfg->bpp / 8) * 2;
 
-               if (!mode)
+               if (!mode) {
                        mode = &default_720p;
+                       num_cfg = 1;
+               } else {
+                       num_cfg = ch->cfg.num_cfg;
+               }
+
+               fb_videomode_to_modelist(mode, num_cfg, &info->modelist);
 
                fb_videomode_to_var(var, mode);
                /* Default Y virtual resolution is 2x panel size */
@@ -1281,10 +1279,6 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
 
        for (i = 0; i < j; i++) {
                struct sh_mobile_lcdc_chan *ch = priv->ch + i;
-               const struct fb_videomode *mode = ch->cfg.lcd_cfg;
-
-               if (!mode)
-                       mode = &default_720p;
 
                info = ch->info;
 
@@ -1297,7 +1291,6 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
                        }
                }
 
-               fb_videomode_to_modelist(mode, ch->cfg.num_cfg, &info->modelist);
                error = register_framebuffer(info);
                if (error < 0)
                        goto err1;
index a3aa917095038d4afa8515a2d79d1184520e8c6e..6723d6910cde7831175df8932f9adbc5c45f9c76 100644 (file)
@@ -15,6 +15,9 @@
 #include <linux/module.h>
 #include <linux/interrupt.h>
 #include <linux/platform_device.h>
+#include <linux/list.h>
+#include <linux/pm.h>
+#include <asm/olpc.h>
 
 /*
  * The default port config.
@@ -28,6 +31,19 @@ static struct via_port_cfg adap_configs[] = {
        { 0, 0, 0, 0 }
 };
 
+/*
+ * The OLPC XO-1.5 puts the camera power and reset lines onto
+ * GPIO 2C.
+ */
+static const struct via_port_cfg olpc_adap_configs[] = {
+       [VIA_PORT_26]   = { VIA_PORT_I2C,  VIA_MODE_I2C, VIASR, 0x26 },
+       [VIA_PORT_31]   = { VIA_PORT_I2C,  VIA_MODE_I2C, VIASR, 0x31 },
+       [VIA_PORT_25]   = { VIA_PORT_GPIO, VIA_MODE_GPIO, VIASR, 0x25 },
+       [VIA_PORT_2C]   = { VIA_PORT_GPIO, VIA_MODE_GPIO, VIASR, 0x2c },
+       [VIA_PORT_3D]   = { VIA_PORT_GPIO, VIA_MODE_GPIO, VIASR, 0x3d },
+       { 0, 0, 0, 0 }
+};
+
 /*
  * We currently only support one viafb device (will there ever be
  * more than one?), so just declare it globally here.
@@ -575,6 +591,78 @@ static void via_teardown_subdevs(void)
                }
 }
 
+/*
+ * Power management functions
+ */
+#ifdef CONFIG_PM
+static LIST_HEAD(viafb_pm_hooks);
+static DEFINE_MUTEX(viafb_pm_hooks_lock);
+
+void viafb_pm_register(struct viafb_pm_hooks *hooks)
+{
+       INIT_LIST_HEAD(&hooks->list);
+
+       mutex_lock(&viafb_pm_hooks_lock);
+       list_add_tail(&hooks->list, &viafb_pm_hooks);
+       mutex_unlock(&viafb_pm_hooks_lock);
+}
+EXPORT_SYMBOL_GPL(viafb_pm_register);
+
+void viafb_pm_unregister(struct viafb_pm_hooks *hooks)
+{
+       mutex_lock(&viafb_pm_hooks_lock);
+       list_del(&hooks->list);
+       mutex_unlock(&viafb_pm_hooks_lock);
+}
+EXPORT_SYMBOL_GPL(viafb_pm_unregister);
+
+static int via_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+       struct viafb_pm_hooks *hooks;
+
+       if (state.event != PM_EVENT_SUSPEND)
+               return 0;
+       /*
+        * "I've occasionally hit a few drivers that caused suspend
+        * failures, and each and every time it was a driver bug, and
+        * the right thing to do was to just ignore the error and suspend
+        * anyway - returning an error code and trying to undo the suspend
+        * is not what anybody ever really wants, even if our model
+        *_allows_ for it."
+        * -- Linus Torvalds, Dec. 7, 2009
+        */
+       mutex_lock(&viafb_pm_hooks_lock);
+       list_for_each_entry_reverse(hooks, &viafb_pm_hooks, list)
+               hooks->suspend(hooks->private);
+       mutex_unlock(&viafb_pm_hooks_lock);
+
+       pci_save_state(pdev);
+       pci_disable_device(pdev);
+       pci_set_power_state(pdev, pci_choose_state(pdev, state));
+       return 0;
+}
+
+static int via_resume(struct pci_dev *pdev)
+{
+       struct viafb_pm_hooks *hooks;
+
+       /* Get the bus side powered up */
+       pci_set_power_state(pdev, PCI_D0);
+       pci_restore_state(pdev);
+       if (pci_enable_device(pdev))
+               return 0;
+
+       pci_set_master(pdev);
+
+       /* Now bring back any subdevs */
+       mutex_lock(&viafb_pm_hooks_lock);
+       list_for_each_entry(hooks, &viafb_pm_hooks, list)
+               hooks->resume(hooks->private);
+       mutex_unlock(&viafb_pm_hooks_lock);
+
+       return 0;
+}
+#endif /* CONFIG_PM */
 
 static int __devinit via_pci_probe(struct pci_dev *pdev,
                const struct pci_device_id *ent)
@@ -584,6 +672,7 @@ static int __devinit via_pci_probe(struct pci_dev *pdev,
        ret = pci_enable_device(pdev);
        if (ret)
                return ret;
+
        /*
         * Global device initialization.
         */
@@ -591,6 +680,9 @@ static int __devinit via_pci_probe(struct pci_dev *pdev,
        global_dev.pdev = pdev;
        global_dev.chip_type = ent->driver_data;
        global_dev.port_cfg = adap_configs;
+       if (machine_is_olpc())
+               global_dev.port_cfg = olpc_adap_configs;
+
        spin_lock_init(&global_dev.reg_lock);
        ret = via_pci_setup_mmio(&global_dev);
        if (ret)
@@ -663,8 +755,8 @@ static struct pci_driver via_driver = {
        .probe          = via_pci_probe,
        .remove         = __devexit_p(via_pci_remove),
 #ifdef CONFIG_PM
-       .suspend        = viafb_suspend,
-       .resume         = viafb_resume,
+       .suspend        = via_suspend,
+       .resume         = via_resume,
 #endif
 };
 
index 39acb37e7a1d2bd333003679b9b322e4a6feb291..c2a0a1cfd3b3fe6c9f2da44e8574fe9a20591be5 100644 (file)
@@ -172,6 +172,28 @@ static void viafb_gpio_disable(struct viafb_gpio *gpio)
        via_write_reg_mask(VIASR, gpio->vg_port_index, 0, 0x02);
 }
 
+#ifdef CONFIG_PM
+
+static int viafb_gpio_suspend(void *private)
+{
+       return 0;
+}
+
+static int viafb_gpio_resume(void *private)
+{
+       int i;
+
+       for (i = 0; i < gpio_config.gpio_chip.ngpio; i += 2)
+               viafb_gpio_enable(gpio_config.active_gpios[i]);
+       return 0;
+}
+
+static struct viafb_pm_hooks viafb_gpio_pm_hooks = {
+       .suspend = viafb_gpio_suspend,
+       .resume = viafb_gpio_resume
+};
+#endif /* CONFIG_PM */
+
 /*
  * Look up a specific gpio and return the number it was assigned.
  */
@@ -236,6 +258,9 @@ static __devinit int viafb_gpio_probe(struct platform_device *platdev)
                printk(KERN_ERR "viafb: failed to add gpios (%d)\n", ret);
                gpio_config.gpio_chip.ngpio = 0;
        }
+#ifdef CONFIG_PM
+       viafb_pm_register(&viafb_gpio_pm_hooks);
+#endif
        return ret;
 }
 
@@ -245,6 +270,10 @@ static int viafb_gpio_remove(struct platform_device *platdev)
        unsigned long flags;
        int ret = 0, i;
 
+#ifdef CONFIG_PM
+       viafb_pm_unregister(&viafb_gpio_pm_hooks);
+#endif
+
        /*
         * Get unregistered.
         */
index d298cfccd6fc24bb842f69a9c8cf9945bdc8c8ae..289edd519527ff948570261db01747be8c26e20f 100644 (file)
@@ -1672,31 +1672,19 @@ static int parse_mode(const char *str, u32 *xres, u32 *yres)
 
 
 #ifdef CONFIG_PM
-int viafb_suspend(struct pci_dev *pdev, pm_message_t state)
+static int viafb_suspend(void *unused)
 {
-       if (state.event == PM_EVENT_SUSPEND) {
-               acquire_console_sem();
-               fb_set_suspend(viafbinfo, 1);
-
-               viafb_sync(viafbinfo);
-
-               pci_save_state(pdev);
-               pci_disable_device(pdev);
-               pci_set_power_state(pdev, pci_choose_state(pdev, state));
-               release_console_sem();
-       }
+       acquire_console_sem();
+       fb_set_suspend(viafbinfo, 1);
+       viafb_sync(viafbinfo);
+       release_console_sem();
 
        return 0;
 }
 
-int viafb_resume(struct pci_dev *pdev)
+static int viafb_resume(void *unused)
 {
        acquire_console_sem();
-       pci_set_power_state(pdev, PCI_D0);
-       pci_restore_state(pdev);
-       if (pci_enable_device(pdev))
-               goto fail;
-       pci_set_master(pdev);
        if (viaparinfo->shared->vdev->engine_mmio)
                viafb_reset_engine(viaparinfo);
        viafb_set_par(viafbinfo);
@@ -1704,11 +1692,15 @@ int viafb_resume(struct pci_dev *pdev)
                viafb_set_par(viafbinfo1);
        fb_set_suspend(viafbinfo, 0);
 
-fail:
        release_console_sem();
        return 0;
 }
 
+static struct viafb_pm_hooks viafb_fb_pm_hooks = {
+       .suspend = viafb_suspend,
+       .resume = viafb_resume
+};
+
 #endif
 
 
@@ -1899,6 +1891,10 @@ int __devinit via_fb_pci_probe(struct viafb_dev *vdev)
 
        viafb_init_proc(viaparinfo->shared);
        viafb_init_dac(IGA2);
+
+#ifdef CONFIG_PM
+       viafb_pm_register(&viafb_fb_pm_hooks);
+#endif
        return 0;
 
 out_fb_unreg:
index 4960e3da6645a8d51e73f50754a71536cc15e327..d66f963e930e3d2791e004b120b6a98b8b5dec92 100644 (file)
@@ -108,6 +108,4 @@ void via_fb_pci_remove(struct pci_dev *pdev);
 /* Temporary */
 int viafb_init(void);
 void viafb_exit(void);
-int viafb_suspend(struct pci_dev *pdev, pm_message_t state);
-int viafb_resume(struct pci_dev *pdev);
 #endif /* __VIAFBDEV_H__ */
diff --git a/drivers/video/vt8500lcdfb.c b/drivers/video/vt8500lcdfb.c
new file mode 100644 (file)
index 0000000..7617f12
--- /dev/null
@@ -0,0 +1,447 @@
+/*
+ *  linux/drivers/video/vt8500lcdfb.c
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Based on skeletonfb.c and pxafb.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/wait.h>
+
+#include <mach/vt8500fb.h>
+
+#include "vt8500lcdfb.h"
+#include "wmt_ge_rops.h"
+
+#define to_vt8500lcd_info(__info) container_of(__info, \
+                                               struct vt8500lcd_info, fb)
+
+static int vt8500lcd_set_par(struct fb_info *info)
+{
+       struct vt8500lcd_info *fbi = to_vt8500lcd_info(info);
+       int reg_bpp = 5; /* 16bpp */
+       int i;
+       unsigned long control0;
+
+       if (!fbi)
+               return -EINVAL;
+
+       if (info->var.bits_per_pixel <= 8) {
+               /* palettized */
+               info->var.red.offset    = 0;
+               info->var.red.length    = info->var.bits_per_pixel;
+               info->var.red.msb_right = 0;
+
+               info->var.green.offset  = 0;
+               info->var.green.length  = info->var.bits_per_pixel;
+               info->var.green.msb_right = 0;
+
+               info->var.blue.offset   = 0;
+               info->var.blue.length   = info->var.bits_per_pixel;
+               info->var.blue.msb_right = 0;
+
+               info->var.transp.offset = 0;
+               info->var.transp.length = 0;
+               info->var.transp.msb_right = 0;
+
+               info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
+               info->fix.line_length = info->var.xres_virtual /
+                                               (8/info->var.bits_per_pixel);
+       } else {
+               /* non-palettized */
+               info->var.transp.offset = 0;
+               info->var.transp.length = 0;
+               info->var.transp.msb_right = 0;
+
+               if (info->var.bits_per_pixel == 16) {
+                       /* RGB565 */
+                       info->var.red.offset = 11;
+                       info->var.red.length = 5;
+                       info->var.red.msb_right = 0;
+                       info->var.green.offset = 5;
+                       info->var.green.length = 6;
+                       info->var.green.msb_right = 0;
+                       info->var.blue.offset = 0;
+                       info->var.blue.length = 5;
+                       info->var.blue.msb_right = 0;
+               } else {
+                       /* Equal depths per channel */
+                       info->var.red.offset = info->var.bits_per_pixel
+                                                       * 2 / 3;
+                       info->var.red.length = info->var.bits_per_pixel / 3;
+                       info->var.red.msb_right = 0;
+                       info->var.green.offset = info->var.bits_per_pixel / 3;
+                       info->var.green.length = info->var.bits_per_pixel / 3;
+                       info->var.green.msb_right = 0;
+                       info->var.blue.offset = 0;
+                       info->var.blue.length = info->var.bits_per_pixel / 3;
+                       info->var.blue.msb_right = 0;
+               }
+
+               info->fix.visual = FB_VISUAL_TRUECOLOR;
+               info->fix.line_length = info->var.bits_per_pixel > 16 ?
+                                       info->var.xres_virtual << 2 :
+                                       info->var.xres_virtual << 1;
+       }
+
+       for (i = 0; i < 8; i++) {
+               if (bpp_values[i] == info->var.bits_per_pixel) {
+                       reg_bpp = i;
+                       continue;
+               }
+       }
+
+       control0 = readl(fbi->regbase) & ~0xf;
+       writel(0, fbi->regbase);
+       while (readl(fbi->regbase + 0x38) & 0x10)
+               /* wait */;
+       writel((((info->var.hsync_len - 1) & 0x3f) << 26)
+               | ((info->var.left_margin & 0xff) << 18)
+               | (((info->var.xres - 1) & 0x3ff) << 8)
+               | (info->var.right_margin & 0xff), fbi->regbase + 0x4);
+       writel((((info->var.vsync_len - 1) & 0x3f) << 26)
+               | ((info->var.upper_margin & 0xff) << 18)
+               | (((info->var.yres - 1) & 0x3ff) << 8)
+               | (info->var.lower_margin & 0xff), fbi->regbase + 0x8);
+       writel((((info->var.yres - 1) & 0x400) << 2)
+               | ((info->var.xres - 1) & 0x400), fbi->regbase + 0x10);
+       writel(0x80000000, fbi->regbase + 0x20);
+       writel(control0 | (reg_bpp << 1) | 0x100, fbi->regbase);
+
+       return 0;
+}
+
+static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
+{
+       chan &= 0xffff;
+       chan >>= 16 - bf->length;
+       return chan << bf->offset;
+}
+
+static int vt8500lcd_setcolreg(unsigned regno, unsigned red, unsigned green,
+                          unsigned blue, unsigned transp,
+                          struct fb_info *info) {
+       struct vt8500lcd_info *fbi = to_vt8500lcd_info(info);
+       int ret = 1;
+       unsigned int val;
+       if (regno >= 256)
+               return -EINVAL;
+
+       if (info->var.grayscale)
+               red = green = blue =
+                       (19595 * red + 38470 * green + 7471 * blue) >> 16;
+
+       switch (fbi->fb.fix.visual) {
+       case FB_VISUAL_TRUECOLOR:
+               if (regno < 16) {
+                       u32 *pal = fbi->fb.pseudo_palette;
+
+                       val  = chan_to_field(red, &fbi->fb.var.red);
+                       val |= chan_to_field(green, &fbi->fb.var.green);
+                       val |= chan_to_field(blue, &fbi->fb.var.blue);
+
+                       pal[regno] = val;
+                       ret = 0;
+               }
+               break;
+
+       case FB_VISUAL_STATIC_PSEUDOCOLOR:
+       case FB_VISUAL_PSEUDOCOLOR:
+               writew((red & 0xf800)
+                     | ((green >> 5) & 0x7e0)
+                     | ((blue >> 11) & 0x1f),
+                      fbi->palette_cpu + sizeof(u16) * regno);
+               break;
+       }
+
+       return ret;
+}
+
+static int vt8500lcd_ioctl(struct fb_info *info, unsigned int cmd,
+                        unsigned long arg)
+{
+       int ret = 0;
+       struct vt8500lcd_info *fbi = to_vt8500lcd_info(info);
+
+       if (cmd == FBIO_WAITFORVSYNC) {
+               /* Unmask End of Frame interrupt */
+               writel(0xffffffff ^ (1 << 3), fbi->regbase + 0x3c);
+               ret = wait_event_interruptible_timeout(fbi->wait,
+                       readl(fbi->regbase + 0x38) & (1 << 3), HZ / 10);
+               /* Mask back to reduce unwanted interrupt traffic */
+               writel(0xffffffff, fbi->regbase + 0x3c);
+               if (ret < 0)
+                       return ret;
+               if (ret == 0)
+                       return -ETIMEDOUT;
+       }
+
+       return ret;
+}
+
+static int vt8500lcd_pan_display(struct fb_var_screeninfo *var,
+                               struct fb_info *info)
+{
+       unsigned pixlen = info->fix.line_length / info->var.xres_virtual;
+       unsigned off = pixlen * var->xoffset
+                     + info->fix.line_length * var->yoffset;
+       struct vt8500lcd_info *fbi = to_vt8500lcd_info(info);
+
+       writel((1 << 31)
+               | (((var->xres_virtual - var->xres) * pixlen / 4) << 20)
+               | (off >> 2), fbi->regbase + 0x20);
+       return 0;
+}
+
+static struct fb_ops vt8500lcd_ops = {
+       .owner          = THIS_MODULE,
+       .fb_set_par     = vt8500lcd_set_par,
+       .fb_setcolreg   = vt8500lcd_setcolreg,
+       .fb_fillrect    = wmt_ge_fillrect,
+       .fb_copyarea    = wmt_ge_copyarea,
+       .fb_imageblit   = sys_imageblit,
+       .fb_sync        = wmt_ge_sync,
+       .fb_ioctl       = vt8500lcd_ioctl,
+       .fb_pan_display = vt8500lcd_pan_display,
+};
+
+static irqreturn_t vt8500lcd_handle_irq(int irq, void *dev_id)
+{
+       struct vt8500lcd_info *fbi = dev_id;
+
+       if (readl(fbi->regbase + 0x38) & (1 << 3))
+               wake_up_interruptible(&fbi->wait);
+
+       writel(0xffffffff, fbi->regbase + 0x38);
+       return IRQ_HANDLED;
+}
+
+static int __devinit vt8500lcd_probe(struct platform_device *pdev)
+{
+       struct vt8500lcd_info *fbi;
+       struct resource *res;
+       struct vt8500fb_platform_data *pdata = pdev->dev.platform_data;
+       void *addr;
+       int irq, ret;
+
+       ret = -ENOMEM;
+       fbi = NULL;
+
+       fbi = kzalloc(sizeof(struct vt8500lcd_info) + sizeof(u32) * 16,
+                                                       GFP_KERNEL);
+       if (!fbi) {
+               dev_err(&pdev->dev, "Failed to initialize framebuffer device\n");
+               ret = -ENOMEM;
+               goto failed;
+       }
+
+       strcpy(fbi->fb.fix.id, "VT8500 LCD");
+
+       fbi->fb.fix.type        = FB_TYPE_PACKED_PIXELS;
+       fbi->fb.fix.xpanstep    = 0;
+       fbi->fb.fix.ypanstep    = 1;
+       fbi->fb.fix.ywrapstep   = 0;
+       fbi->fb.fix.accel       = FB_ACCEL_NONE;
+
+       fbi->fb.var.nonstd      = 0;
+       fbi->fb.var.activate    = FB_ACTIVATE_NOW;
+       fbi->fb.var.height      = -1;
+       fbi->fb.var.width       = -1;
+       fbi->fb.var.vmode       = FB_VMODE_NONINTERLACED;
+
+       fbi->fb.fbops           = &vt8500lcd_ops;
+       fbi->fb.flags           = FBINFO_DEFAULT
+                               | FBINFO_HWACCEL_COPYAREA
+                               | FBINFO_HWACCEL_FILLRECT
+                               | FBINFO_HWACCEL_YPAN
+                               | FBINFO_VIRTFB
+                               | FBINFO_PARTIAL_PAN_OK;
+       fbi->fb.node            = -1;
+
+       addr = fbi;
+       addr = addr + sizeof(struct vt8500lcd_info);
+       fbi->fb.pseudo_palette  = addr;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (res == NULL) {
+               dev_err(&pdev->dev, "no I/O memory resource defined\n");
+               ret = -ENODEV;
+               goto failed_fbi;
+       }
+
+       res = request_mem_region(res->start, resource_size(res), "vt8500lcd");
+       if (res == NULL) {
+               dev_err(&pdev->dev, "failed to request I/O memory\n");
+               ret = -EBUSY;
+               goto failed_fbi;
+       }
+
+       fbi->regbase = ioremap(res->start, resource_size(res));
+       if (fbi->regbase == NULL) {
+               dev_err(&pdev->dev, "failed to map I/O memory\n");
+               ret = -EBUSY;
+               goto failed_free_res;
+       }
+
+       fbi->fb.fix.smem_start  = pdata->video_mem_phys;
+       fbi->fb.fix.smem_len    = pdata->video_mem_len;
+       fbi->fb.screen_base     = pdata->video_mem_virt;
+
+       fbi->palette_size       = PAGE_ALIGN(512);
+       fbi->palette_cpu        = dma_alloc_coherent(&pdev->dev,
+                                                    fbi->palette_size,
+                                                    &fbi->palette_phys,
+                                                    GFP_KERNEL);
+       if (fbi->palette_cpu == NULL) {
+               dev_err(&pdev->dev, "Failed to allocate palette buffer\n");
+               ret = -ENOMEM;
+               goto failed_free_io;
+       }
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               dev_err(&pdev->dev, "no IRQ defined\n");
+               ret = -ENODEV;
+               goto failed_free_palette;
+       }
+
+       ret = request_irq(irq, vt8500lcd_handle_irq, IRQF_DISABLED, "LCD", fbi);
+       if (ret) {
+               dev_err(&pdev->dev, "request_irq failed: %d\n", ret);
+               ret = -EBUSY;
+               goto failed_free_palette;
+       }
+
+       init_waitqueue_head(&fbi->wait);
+
+       if (fb_alloc_cmap(&fbi->fb.cmap, 256, 0) < 0) {
+               dev_err(&pdev->dev, "Failed to allocate color map\n");
+               ret = -ENOMEM;
+               goto failed_free_irq;
+       }
+
+       fb_videomode_to_var(&fbi->fb.var, &pdata->mode);
+       fbi->fb.var.bits_per_pixel      = pdata->bpp;
+       fbi->fb.var.xres_virtual        = pdata->xres_virtual;
+       fbi->fb.var.yres_virtual        = pdata->yres_virtual;
+
+       ret = vt8500lcd_set_par(&fbi->fb);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to set parameters\n");
+               goto failed_free_cmap;
+       }
+
+       writel(fbi->fb.fix.smem_start >> 22, fbi->regbase + 0x1c);
+       writel((fbi->palette_phys & 0xfffffe00) | 1, fbi->regbase + 0x18);
+
+       platform_set_drvdata(pdev, fbi);
+
+       ret = register_framebuffer(&fbi->fb);
+       if (ret < 0) {
+               dev_err(&pdev->dev,
+                       "Failed to register framebuffer device: %d\n", ret);
+               goto failed_free_cmap;
+       }
+
+       /*
+        * Ok, now enable the LCD controller
+        */
+       writel(readl(fbi->regbase) | 1, fbi->regbase);
+
+       return 0;
+
+failed_free_cmap:
+       if (fbi->fb.cmap.len)
+               fb_dealloc_cmap(&fbi->fb.cmap);
+failed_free_irq:
+       free_irq(irq, fbi);
+failed_free_palette:
+       dma_free_coherent(&pdev->dev, fbi->palette_size,
+                         fbi->palette_cpu, fbi->palette_phys);
+failed_free_io:
+       iounmap(fbi->regbase);
+failed_free_res:
+       release_mem_region(res->start, resource_size(res));
+failed_fbi:
+       platform_set_drvdata(pdev, NULL);
+       kfree(fbi);
+failed:
+       return ret;
+}
+
+static int __devexit vt8500lcd_remove(struct platform_device *pdev)
+{
+       struct vt8500lcd_info *fbi = platform_get_drvdata(pdev);
+       struct resource *res;
+       int irq;
+
+       unregister_framebuffer(&fbi->fb);
+
+       writel(0, fbi->regbase);
+
+       if (fbi->fb.cmap.len)
+               fb_dealloc_cmap(&fbi->fb.cmap);
+
+       irq = platform_get_irq(pdev, 0);
+       free_irq(irq, fbi);
+
+       dma_free_coherent(&pdev->dev, fbi->palette_size,
+                         fbi->palette_cpu, fbi->palette_phys);
+
+       iounmap(fbi->regbase);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       release_mem_region(res->start, resource_size(res));
+
+       kfree(fbi);
+
+       return 0;
+}
+
+static struct platform_driver vt8500lcd_driver = {
+       .probe          = vt8500lcd_probe,
+       .remove         = __devexit_p(vt8500lcd_remove),
+       .driver         = {
+               .owner  = THIS_MODULE,
+               .name   = "vt8500-lcd",
+       },
+};
+
+static int __init vt8500lcd_init(void)
+{
+       return platform_driver_register(&vt8500lcd_driver);
+}
+
+static void __exit vt8500lcd_exit(void)
+{
+       platform_driver_unregister(&vt8500lcd_driver);
+}
+
+module_init(vt8500lcd_init);
+module_exit(vt8500lcd_exit);
+
+MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>");
+MODULE_DESCRIPTION("LCD controller driver for VIA VT8500");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/vt8500lcdfb.h b/drivers/video/vt8500lcdfb.h
new file mode 100644 (file)
index 0000000..36ca3ca
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ *  linux/drivers/video/vt8500lcdfb.h
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+struct vt8500lcd_info {
+       struct fb_info          fb;
+       void __iomem            *regbase;
+       void __iomem            *palette_cpu;
+       dma_addr_t              palette_phys;
+       size_t                  palette_size;
+       wait_queue_head_t       wait;
+};
+
+static int bpp_values[] = {
+       1,
+       2,
+       4,
+       8,
+       12,
+       16,
+       18,
+       24,
+};
diff --git a/drivers/video/wm8505fb.c b/drivers/video/wm8505fb.c
new file mode 100644 (file)
index 0000000..e37251b
--- /dev/null
@@ -0,0 +1,422 @@
+/*
+ *  WonderMedia WM8505 Frame Buffer device driver
+ *
+ *  Copyright (C) 2010 Ed Spiridonov <edo.rus@gmail.com>
+ *    Based on vt8500lcdfb.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/wait.h>
+
+#include <mach/vt8500fb.h>
+
+#include "wm8505fb_regs.h"
+#include "wmt_ge_rops.h"
+
+#define DRIVER_NAME "wm8505-fb"
+
+#define to_wm8505fb_info(__info) container_of(__info, \
+                                               struct wm8505fb_info, fb)
+struct wm8505fb_info {
+       struct fb_info          fb;
+       void __iomem            *regbase;
+       unsigned int            contrast;
+};
+
+
+static int wm8505fb_init_hw(struct fb_info *info)
+{
+       struct wm8505fb_info *fbi = to_wm8505fb_info(info);
+
+       int i;
+
+       /* I know the purpose only of few registers, so clear unknown */
+       for (i = 0; i < 0x200; i += 4)
+               writel(0, fbi->regbase + i);
+
+       /* Set frame buffer address */
+       writel(fbi->fb.fix.smem_start, fbi->regbase + WMT_GOVR_FBADDR);
+       writel(fbi->fb.fix.smem_start, fbi->regbase + WMT_GOVR_FBADDR1);
+
+       /* Set in-memory picture format to RGB 32bpp */
+       writel(0x1c,                   fbi->regbase + WMT_GOVR_COLORSPACE);
+       writel(1,                      fbi->regbase + WMT_GOVR_COLORSPACE1);
+
+       /* Virtual buffer size */
+       writel(info->var.xres,         fbi->regbase + WMT_GOVR_XRES);
+       writel(info->var.xres_virtual, fbi->regbase + WMT_GOVR_XRES_VIRTUAL);
+
+       /* black magic ;) */
+       writel(0xf,                    fbi->regbase + WMT_GOVR_FHI);
+       writel(4,                      fbi->regbase + WMT_GOVR_DVO_SET);
+       writel(1,                      fbi->regbase + WMT_GOVR_MIF_ENABLE);
+       writel(1,                      fbi->regbase + WMT_GOVR_REG_UPDATE);
+
+       return 0;
+}
+
+static int wm8505fb_set_timing(struct fb_info *info)
+{
+       struct wm8505fb_info *fbi = to_wm8505fb_info(info);
+
+       int h_start = info->var.left_margin;
+       int h_end = h_start + info->var.xres;
+       int h_all = h_end + info->var.right_margin;
+       int h_sync = info->var.hsync_len;
+
+       int v_start = info->var.upper_margin;
+       int v_end = v_start + info->var.yres;
+       int v_all = v_end + info->var.lower_margin;
+       int v_sync = info->var.vsync_len + 1;
+
+       writel(0, fbi->regbase + WMT_GOVR_TG);
+
+       writel(h_start, fbi->regbase + WMT_GOVR_TIMING_H_START);
+       writel(h_end,   fbi->regbase + WMT_GOVR_TIMING_H_END);
+       writel(h_all,   fbi->regbase + WMT_GOVR_TIMING_H_ALL);
+       writel(h_sync,  fbi->regbase + WMT_GOVR_TIMING_H_SYNC);
+
+       writel(v_start, fbi->regbase + WMT_GOVR_TIMING_V_START);
+       writel(v_end,   fbi->regbase + WMT_GOVR_TIMING_V_END);
+       writel(v_all,   fbi->regbase + WMT_GOVR_TIMING_V_ALL);
+       writel(v_sync,  fbi->regbase + WMT_GOVR_TIMING_V_SYNC);
+
+       writel(1, fbi->regbase + WMT_GOVR_TG);
+
+       return 0;
+}
+
+
+static int wm8505fb_set_par(struct fb_info *info)
+{
+       struct wm8505fb_info *fbi = to_wm8505fb_info(info);
+
+       if (!fbi)
+               return -EINVAL;
+
+       if (info->var.bits_per_pixel == 32) {
+               info->var.red.offset = 16;
+               info->var.red.length = 8;
+               info->var.red.msb_right = 0;
+               info->var.green.offset = 8;
+               info->var.green.length = 8;
+               info->var.green.msb_right = 0;
+               info->var.blue.offset = 0;
+               info->var.blue.length = 8;
+               info->var.blue.msb_right = 0;
+               info->fix.visual = FB_VISUAL_TRUECOLOR;
+               info->fix.line_length = info->var.xres_virtual << 2;
+       }
+
+       wm8505fb_set_timing(info);
+
+       writel(fbi->contrast<<16 | fbi->contrast<<8 | fbi->contrast,
+               fbi->regbase + WMT_GOVR_CONTRAST);
+
+       return 0;
+}
+
+static ssize_t contrast_show(struct device *dev,
+                            struct device_attribute *attr, char *buf)
+{
+       struct fb_info *info = dev_get_drvdata(dev);
+       struct wm8505fb_info *fbi = to_wm8505fb_info(info);
+
+       return sprintf(buf, "%d\n", fbi->contrast);
+}
+
+static ssize_t contrast_store(struct device *dev,
+                             struct device_attribute *attr,
+                             const char *buf, size_t count)
+{
+       struct fb_info *info = dev_get_drvdata(dev);
+       struct wm8505fb_info *fbi = to_wm8505fb_info(info);
+       unsigned long tmp;
+
+       if (strict_strtoul(buf, 10, &tmp) || (tmp > 0xff))
+               return -EINVAL;
+       fbi->contrast = tmp;
+
+       wm8505fb_set_par(info);
+
+       return count;
+}
+
+static DEVICE_ATTR(contrast, 0644, contrast_show, contrast_store);
+
+static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
+{
+       chan &= 0xffff;
+       chan >>= 16 - bf->length;
+       return chan << bf->offset;
+}
+
+static int wm8505fb_setcolreg(unsigned regno, unsigned red, unsigned green,
+                          unsigned blue, unsigned transp,
+                          struct fb_info *info) {
+       struct wm8505fb_info *fbi = to_wm8505fb_info(info);
+       int ret = 1;
+       unsigned int val;
+       if (regno >= 256)
+               return -EINVAL;
+
+       if (info->var.grayscale)
+               red = green = blue =
+                       (19595 * red + 38470 * green + 7471 * blue) >> 16;
+
+       switch (fbi->fb.fix.visual) {
+       case FB_VISUAL_TRUECOLOR:
+               if (regno < 16) {
+                       u32 *pal = info->pseudo_palette;
+
+                       val  = chan_to_field(red, &fbi->fb.var.red);
+                       val |= chan_to_field(green, &fbi->fb.var.green);
+                       val |= chan_to_field(blue, &fbi->fb.var.blue);
+
+                       pal[regno] = val;
+                       ret = 0;
+               }
+               break;
+       }
+
+       return ret;
+}
+
+static int wm8505fb_pan_display(struct fb_var_screeninfo *var,
+                               struct fb_info *info)
+{
+       struct wm8505fb_info *fbi = to_wm8505fb_info(info);
+
+       writel(var->xoffset, fbi->regbase + WMT_GOVR_XPAN);
+       writel(var->yoffset, fbi->regbase + WMT_GOVR_YPAN);
+       return 0;
+}
+
+static int wm8505fb_blank(int blank, struct fb_info *info)
+{
+       struct wm8505fb_info *fbi = to_wm8505fb_info(info);
+
+       switch (blank) {
+       case FB_BLANK_UNBLANK:
+               wm8505fb_set_timing(info);
+               break;
+       default:
+               writel(0,  fbi->regbase + WMT_GOVR_TIMING_V_SYNC);
+               break;
+       }
+
+       return 0;
+}
+
+static struct fb_ops wm8505fb_ops = {
+       .owner          = THIS_MODULE,
+       .fb_set_par     = wm8505fb_set_par,
+       .fb_setcolreg   = wm8505fb_setcolreg,
+       .fb_fillrect    = wmt_ge_fillrect,
+       .fb_copyarea    = wmt_ge_copyarea,
+       .fb_imageblit   = sys_imageblit,
+       .fb_sync        = wmt_ge_sync,
+       .fb_pan_display = wm8505fb_pan_display,
+       .fb_blank       = wm8505fb_blank,
+};
+
+static int __devinit wm8505fb_probe(struct platform_device *pdev)
+{
+       struct wm8505fb_info    *fbi;
+       struct resource         *res;
+       void                    *addr;
+       struct vt8500fb_platform_data *pdata;
+       int ret;
+
+       pdata = pdev->dev.platform_data;
+
+       ret = -ENOMEM;
+       fbi = NULL;
+
+       fbi = kzalloc(sizeof(struct wm8505fb_info) + sizeof(u32) * 16,
+                                                       GFP_KERNEL);
+       if (!fbi) {
+               dev_err(&pdev->dev, "Failed to initialize framebuffer device\n");
+               ret = -ENOMEM;
+               goto failed;
+       }
+
+       strcpy(fbi->fb.fix.id, DRIVER_NAME);
+
+       fbi->fb.fix.type        = FB_TYPE_PACKED_PIXELS;
+       fbi->fb.fix.xpanstep    = 1;
+       fbi->fb.fix.ypanstep    = 1;
+       fbi->fb.fix.ywrapstep   = 0;
+       fbi->fb.fix.accel       = FB_ACCEL_NONE;
+
+       fbi->fb.fbops           = &wm8505fb_ops;
+       fbi->fb.flags           = FBINFO_DEFAULT
+                               | FBINFO_HWACCEL_COPYAREA
+                               | FBINFO_HWACCEL_FILLRECT
+                               | FBINFO_HWACCEL_XPAN
+                               | FBINFO_HWACCEL_YPAN
+                               | FBINFO_VIRTFB
+                               | FBINFO_PARTIAL_PAN_OK;
+       fbi->fb.node            = -1;
+
+       addr = fbi;
+       addr = addr + sizeof(struct wm8505fb_info);
+       fbi->fb.pseudo_palette  = addr;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (res == NULL) {
+               dev_err(&pdev->dev, "no I/O memory resource defined\n");
+               ret = -ENODEV;
+               goto failed_fbi;
+       }
+
+       res = request_mem_region(res->start, resource_size(res), "wm8505fb");
+       if (res == NULL) {
+               dev_err(&pdev->dev, "failed to request I/O memory\n");
+               ret = -EBUSY;
+               goto failed_fbi;
+       }
+
+       fbi->regbase = ioremap(res->start, resource_size(res));
+       if (fbi->regbase == NULL) {
+               dev_err(&pdev->dev, "failed to map I/O memory\n");
+               ret = -EBUSY;
+               goto failed_free_res;
+       }
+
+       fb_videomode_to_var(&fbi->fb.var, &pdata->mode);
+
+       fbi->fb.var.nonstd              = 0;
+       fbi->fb.var.activate            = FB_ACTIVATE_NOW;
+
+       fbi->fb.var.height              = -1;
+       fbi->fb.var.width               = -1;
+       fbi->fb.var.xres_virtual        = pdata->xres_virtual;
+       fbi->fb.var.yres_virtual        = pdata->yres_virtual;
+       fbi->fb.var.bits_per_pixel      = pdata->bpp;
+
+       fbi->fb.fix.smem_start  = pdata->video_mem_phys;
+       fbi->fb.fix.smem_len    = pdata->video_mem_len;
+       fbi->fb.screen_base     = pdata->video_mem_virt;
+       fbi->fb.screen_size     = pdata->video_mem_len;
+
+       if (fb_alloc_cmap(&fbi->fb.cmap, 256, 0) < 0) {
+               dev_err(&pdev->dev, "Failed to allocate color map\n");
+               ret = -ENOMEM;
+               goto failed_free_io;
+       }
+
+       wm8505fb_init_hw(&fbi->fb);
+
+       fbi->contrast = 0x80;
+       ret = wm8505fb_set_par(&fbi->fb);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to set parameters\n");
+               goto failed_free_cmap;
+       }
+
+       platform_set_drvdata(pdev, fbi);
+
+       ret = register_framebuffer(&fbi->fb);
+       if (ret < 0) {
+               dev_err(&pdev->dev,
+                       "Failed to register framebuffer device: %d\n", ret);
+               goto failed_free_cmap;
+       }
+
+       ret = device_create_file(&pdev->dev, &dev_attr_contrast);
+       if (ret < 0) {
+               printk(KERN_WARNING "fb%d: failed to register attributes (%d)\n",
+                       fbi->fb.node, ret);
+       }
+
+       printk(KERN_INFO "fb%d: %s frame buffer at 0x%lx-0x%lx\n",
+              fbi->fb.node, fbi->fb.fix.id, fbi->fb.fix.smem_start,
+              fbi->fb.fix.smem_start + fbi->fb.fix.smem_len - 1);
+
+       return 0;
+
+failed_free_cmap:
+       if (fbi->fb.cmap.len)
+               fb_dealloc_cmap(&fbi->fb.cmap);
+failed_free_io:
+       iounmap(fbi->regbase);
+failed_free_res:
+       release_mem_region(res->start, resource_size(res));
+failed_fbi:
+       platform_set_drvdata(pdev, NULL);
+       kfree(fbi);
+failed:
+       return ret;
+}
+
+static int __devexit wm8505fb_remove(struct platform_device *pdev)
+{
+       struct wm8505fb_info *fbi = platform_get_drvdata(pdev);
+       struct resource *res;
+
+       device_remove_file(&pdev->dev, &dev_attr_contrast);
+
+       unregister_framebuffer(&fbi->fb);
+
+       writel(0, fbi->regbase);
+
+       if (fbi->fb.cmap.len)
+               fb_dealloc_cmap(&fbi->fb.cmap);
+
+       iounmap(fbi->regbase);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       release_mem_region(res->start, resource_size(res));
+
+       kfree(fbi);
+
+       return 0;
+}
+
+static struct platform_driver wm8505fb_driver = {
+       .probe          = wm8505fb_probe,
+       .remove         = __devexit_p(wm8505fb_remove),
+       .driver         = {
+               .owner  = THIS_MODULE,
+               .name   = DRIVER_NAME,
+       },
+};
+
+static int __init wm8505fb_init(void)
+{
+       return platform_driver_register(&wm8505fb_driver);
+}
+
+static void __exit wm8505fb_exit(void)
+{
+       platform_driver_unregister(&wm8505fb_driver);
+}
+
+module_init(wm8505fb_init);
+module_exit(wm8505fb_exit);
+
+MODULE_AUTHOR("Ed Spiridonov <edo.rus@gmail.com>");
+MODULE_DESCRIPTION("Framebuffer driver for WMT WM8505");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/wm8505fb_regs.h b/drivers/video/wm8505fb_regs.h
new file mode 100644 (file)
index 0000000..4dd4166
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ *  GOVR registers list for WM8505 chips
+ *
+ *  Copyright (C) 2010 Ed Spiridonov <edo.rus@gmail.com>
+ *   Based on VIA/WonderMedia wm8510-govrh-reg.h
+ *   http://github.com/projectgus/kernel_wm8505/blob/wm8505_2.6.29/
+ *         drivers/video/wmt/register/wm8510/wm8510-govrh-reg.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _WM8505FB_REGS_H
+#define _WM8505FB_REGS_H
+
+/*
+ * Color space select register, default value 0x1c
+ *   BIT0 GOVRH_DVO_YUV2RGB_ENABLE
+ *   BIT1 GOVRH_VGA_YUV2RGB_ENABLE
+ *   BIT2 GOVRH_RGB_MODE
+ *   BIT3 GOVRH_DAC_CLKINV
+ *   BIT4 GOVRH_BLANK_ZERO
+ */
+#define WMT_GOVR_COLORSPACE    0x1e4
+/*
+ * Another colorspace select register, default value 1
+ *   BIT0 GOVRH_DVO_RGB
+ *   BIT1 GOVRH_DVO_YUV422
+ */
+#define WMT_GOVR_COLORSPACE1    0x30
+
+#define WMT_GOVR_CONTRAST      0x1b8
+#define WMT_GOVR_BRGHTNESS     0x1bc /* incompatible with RGB? */
+
+/* Framubeffer address */
+#define WMT_GOVR_FBADDR                 0x90
+#define WMT_GOVR_FBADDR1        0x94 /* UV offset in YUV mode */
+
+/* Offset of visible window */
+#define WMT_GOVR_XPAN           0xa4
+#define WMT_GOVR_YPAN           0xa0
+
+#define WMT_GOVR_XRES           0x98
+#define WMT_GOVR_XRES_VIRTUAL   0x9c
+
+#define WMT_GOVR_MIF_ENABLE     0x80
+#define WMT_GOVR_FHI            0xa8
+#define WMT_GOVR_REG_UPDATE     0xe4
+
+/*
+ *   BIT0 GOVRH_DVO_OUTWIDTH
+ *   BIT1 GOVRH_DVO_SYNC_POLAR
+ *   BIT2 GOVRH_DVO_ENABLE
+ */
+#define WMT_GOVR_DVO_SET       0x148
+
+/* Timing generator? */
+#define WMT_GOVR_TG            0x100
+
+/* Timings */
+#define WMT_GOVR_TIMING_H_ALL  0x108
+#define WMT_GOVR_TIMING_V_ALL  0x10c
+#define WMT_GOVR_TIMING_V_START        0x110
+#define WMT_GOVR_TIMING_V_END  0x114
+#define WMT_GOVR_TIMING_H_START        0x118
+#define WMT_GOVR_TIMING_H_END  0x11c
+#define WMT_GOVR_TIMING_V_SYNC 0x128
+#define WMT_GOVR_TIMING_H_SYNC 0x12c
+
+#endif /* _WM8505FB_REGS_H */
diff --git a/drivers/video/wmt_ge_rops.c b/drivers/video/wmt_ge_rops.c
new file mode 100644 (file)
index 0000000..f31883f
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ *  linux/drivers/video/wmt_ge_rops.c
+ *
+ *  Accelerators for raster operations using WonderMedia Graphics Engine
+ *
+ *  Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/fb.h>
+#include <linux/platform_device.h>
+#include "fb_draw.h"
+
+#define GE_COMMAND_OFF         0x00
+#define GE_DEPTH_OFF           0x04
+#define GE_HIGHCOLOR_OFF       0x08
+#define GE_ROPCODE_OFF         0x14
+#define GE_FIRE_OFF            0x18
+#define GE_SRCBASE_OFF         0x20
+#define GE_SRCDISPW_OFF                0x24
+#define GE_SRCDISPH_OFF                0x28
+#define GE_SRCAREAX_OFF                0x2c
+#define GE_SRCAREAY_OFF                0x30
+#define GE_SRCAREAW_OFF                0x34
+#define GE_SRCAREAH_OFF                0x38
+#define GE_DESTBASE_OFF                0x3c
+#define GE_DESTDISPW_OFF       0x40
+#define GE_DESTDISPH_OFF       0x44
+#define GE_DESTAREAX_OFF       0x48
+#define GE_DESTAREAY_OFF       0x4c
+#define GE_DESTAREAW_OFF       0x50
+#define GE_DESTAREAH_OFF       0x54
+#define GE_PAT0C_OFF           0x88    /* Pattern 0 color */
+#define GE_ENABLE_OFF          0xec
+#define GE_INTEN_OFF           0xf0
+#define GE_STATUS_OFF          0xf8
+
+static void __iomem *regbase;
+
+void wmt_ge_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
+{
+       unsigned long fg, pat;
+
+       if (p->state != FBINFO_STATE_RUNNING)
+               return;
+
+       if (p->fix.visual == FB_VISUAL_TRUECOLOR ||
+           p->fix.visual == FB_VISUAL_DIRECTCOLOR)
+               fg = ((u32 *) (p->pseudo_palette))[rect->color];
+       else
+               fg = rect->color;
+
+       pat = pixel_to_pat(p->var.bits_per_pixel, fg);
+
+       if (p->fbops->fb_sync)
+               p->fbops->fb_sync(p);
+
+       writel(p->var.bits_per_pixel == 32 ? 3 :
+             (p->var.bits_per_pixel == 8 ? 0 : 1), regbase + GE_DEPTH_OFF);
+       writel(p->var.bits_per_pixel == 15 ? 1 : 0, regbase + GE_HIGHCOLOR_OFF);
+       writel(p->fix.smem_start, regbase + GE_DESTBASE_OFF);
+       writel(p->var.xres_virtual - 1, regbase + GE_DESTDISPW_OFF);
+       writel(p->var.yres_virtual - 1, regbase + GE_DESTDISPH_OFF);
+       writel(rect->dx, regbase + GE_DESTAREAX_OFF);
+       writel(rect->dy, regbase + GE_DESTAREAY_OFF);
+       writel(rect->width - 1, regbase + GE_DESTAREAW_OFF);
+       writel(rect->height - 1, regbase + GE_DESTAREAH_OFF);
+
+       writel(pat, regbase + GE_PAT0C_OFF);
+       writel(1, regbase + GE_COMMAND_OFF);
+       writel(rect->rop == ROP_XOR ? 0x5a : 0xf0, regbase + GE_ROPCODE_OFF);
+       writel(1, regbase + GE_FIRE_OFF);
+}
+EXPORT_SYMBOL_GPL(wmt_ge_fillrect);
+
+void wmt_ge_copyarea(struct fb_info *p, const struct fb_copyarea *area)
+{
+       if (p->state != FBINFO_STATE_RUNNING)
+               return;
+
+       if (p->fbops->fb_sync)
+               p->fbops->fb_sync(p);
+
+       writel(p->var.bits_per_pixel > 16 ? 3 :
+             (p->var.bits_per_pixel > 8 ? 1 : 0), regbase + GE_DEPTH_OFF);
+
+       writel(p->fix.smem_start, regbase + GE_SRCBASE_OFF);
+       writel(p->var.xres_virtual - 1, regbase + GE_SRCDISPW_OFF);
+       writel(p->var.yres_virtual - 1, regbase + GE_SRCDISPH_OFF);
+       writel(area->sx, regbase + GE_SRCAREAX_OFF);
+       writel(area->sy, regbase + GE_SRCAREAY_OFF);
+       writel(area->width - 1, regbase + GE_SRCAREAW_OFF);
+       writel(area->height - 1, regbase + GE_SRCAREAH_OFF);
+
+       writel(p->fix.smem_start, regbase + GE_DESTBASE_OFF);
+       writel(p->var.xres_virtual - 1, regbase + GE_DESTDISPW_OFF);
+       writel(p->var.yres_virtual - 1, regbase + GE_DESTDISPH_OFF);
+       writel(area->dx, regbase + GE_DESTAREAX_OFF);
+       writel(area->dy, regbase + GE_DESTAREAY_OFF);
+       writel(area->width - 1, regbase + GE_DESTAREAW_OFF);
+       writel(area->height - 1, regbase + GE_DESTAREAH_OFF);
+
+       writel(0xcc, regbase + GE_ROPCODE_OFF);
+       writel(1, regbase + GE_COMMAND_OFF);
+       writel(1, regbase + GE_FIRE_OFF);
+}
+EXPORT_SYMBOL_GPL(wmt_ge_copyarea);
+
+int wmt_ge_sync(struct fb_info *p)
+{
+       int loops = 5000000;
+       while ((readl(regbase + GE_STATUS_OFF) & 4) && --loops)
+               cpu_relax();
+       return loops > 0 ? 0 : -EBUSY;
+}
+EXPORT_SYMBOL_GPL(wmt_ge_sync);
+
+static int __devinit wmt_ge_rops_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       int ret;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (res == NULL) {
+               dev_err(&pdev->dev, "no I/O memory resource defined\n");
+               ret = -ENODEV;
+               goto error;
+       }
+
+       /* Only one ROP engine is presently supported. */
+       if (unlikely(regbase)) {
+               WARN_ON(1);
+               return -EBUSY;
+       }
+
+       regbase = ioremap(res->start, resource_size(res));
+       if (regbase == NULL) {
+               dev_err(&pdev->dev, "failed to map I/O memory\n");
+               ret = -EBUSY;
+               goto error;
+       }
+
+       writel(1, regbase + GE_ENABLE_OFF);
+       printk(KERN_INFO "Enabled support for WMT GE raster acceleration\n");
+
+       return 0;
+
+error:
+       return ret;
+}
+
+static int __devexit wmt_ge_rops_remove(struct platform_device *pdev)
+{
+       iounmap(regbase);
+       return 0;
+}
+
+static struct platform_driver wmt_ge_rops_driver = {
+       .probe          = wmt_ge_rops_probe,
+       .remove         = __devexit_p(wmt_ge_rops_remove),
+       .driver         = {
+               .owner  = THIS_MODULE,
+               .name   = "wmt_ge_rops",
+       },
+};
+
+static int __init wmt_ge_rops_init(void)
+{
+       return platform_driver_register(&wmt_ge_rops_driver);
+}
+
+static void __exit wmt_ge_rops_exit(void)
+{
+       platform_driver_unregister(&wmt_ge_rops_driver);
+}
+
+module_init(wmt_ge_rops_init);
+module_exit(wmt_ge_rops_exit);
+
+MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com");
+MODULE_DESCRIPTION("Accelerators for raster operations using "
+                  "WonderMedia Graphics Engine");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/wmt_ge_rops.h b/drivers/video/wmt_ge_rops.h
new file mode 100644 (file)
index 0000000..8738075
--- /dev/null
@@ -0,0 +1,5 @@
+extern void wmt_ge_fillrect(struct fb_info *info,
+                           const struct fb_fillrect *rect);
+extern void wmt_ge_copyarea(struct fb_info *info,
+                           const struct fb_copyarea *area);
+extern int wmt_ge_sync(struct fb_info *info);
index 38bffd8ccca58963f29ab33a77beb52b454f87eb..9c21cdf3e3b386e3090d1d4d57fe7860c6bb7be5 100644 (file)
@@ -59,6 +59,21 @@ struct via_port_cfg {
        u8                      ioport_index;
 };
 
+/*
+ * Allow subdevs to register suspend/resume hooks.
+ */
+#ifdef CONFIG_PM
+struct viafb_pm_hooks {
+       struct list_head list;
+       int (*suspend)(void *private);
+       int (*resume)(void *private);
+       void *private;
+};
+
+void viafb_pm_register(struct viafb_pm_hooks *hooks);
+void viafb_pm_unregister(struct viafb_pm_hooks *hooks);
+#endif /* CONFIG_PM */
+
 /*
  * This is the global viafb "device" containing stuff needed by
  * all subdevs.
index 1e1aa54ab2e45bad0c1d6841572ba84ff0e912a3..b56932927d0a5e9ae92f7d43f8dd67c00c5991de 100644 (file)
@@ -13,6 +13,7 @@
 
 struct sh_mobile_lcdc_chan_cfg;
 struct device;
+struct clk;
 
 /*
  * flags format
@@ -33,6 +34,8 @@ struct sh_mobile_hdmi_info {
        struct sh_mobile_lcdc_chan_cfg  *lcd_chan;
        struct device                   *lcd_dev;
        unsigned int                     flags;
+       long (*clk_optimize_parent)(unsigned long target, unsigned long *best_freq,
+                                   unsigned long *parent_freq);
 };
 
 #endif