s3c-fb: CPUFREQ frequency scaling support
[sfrench/cifs-2.6.git] / drivers / video / s3c2410fb.c
index b0b4513ba53784014c6bf0c6c60fc576d7f054f7..7da0027e2409f498ac88a175c78e1fb9f0f4b30e 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/interrupt.h>
 #include <linux/platform_device.h>
 #include <linux/clk.h>
+#include <linux/cpufreq.h>
 
 #include <asm/io.h>
 #include <asm/div64.h>
@@ -89,7 +90,7 @@ static void s3c2410fb_set_lcdaddr(struct fb_info *info)
 static unsigned int s3c2410fb_calc_pixclk(struct s3c2410fb_info *fbi,
                                          unsigned long pixclk)
 {
-       unsigned long clk = clk_get_rate(fbi->clk);
+       unsigned long clk = fbi->clk_rate;
        unsigned long long div;
 
        /* pixclk is in picoseconds, our clock is in Hz
@@ -758,6 +759,57 @@ static irqreturn_t s3c2410fb_irq(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
+#ifdef CONFIG_CPU_FREQ
+
+static int s3c2410fb_cpufreq_transition(struct notifier_block *nb,
+                                       unsigned long val, void *data)
+{
+       struct cpufreq_freqs *freqs = data;
+       struct s3c2410fb_info *info;
+       struct fb_info *fbinfo;
+       long delta_f;
+
+       info = container_of(nb, struct s3c2410fb_info, freq_transition);
+       fbinfo = platform_get_drvdata(to_platform_device(info->dev));
+
+       /* work out change, <0 for speed-up */
+       delta_f = info->clk_rate - clk_get_rate(info->clk);
+
+       if ((val == CPUFREQ_POSTCHANGE && delta_f > 0) ||
+           (val == CPUFREQ_PRECHANGE && delta_f < 0)) {
+               info->clk_rate = clk_get_rate(info->clk);
+               s3c2410fb_activate_var(fbinfo);
+       }
+
+       return 0;
+}
+
+static inline int s3c2410fb_cpufreq_register(struct s3c2410fb_info *info)
+{
+       info->freq_transition.notifier_call = s3c2410fb_cpufreq_transition;
+
+       return cpufreq_register_notifier(&info->freq_transition,
+                                        CPUFREQ_TRANSITION_NOTIFIER);
+}
+
+static inline void s3c2410fb_cpufreq_deregister(struct s3c2410fb_info *info)
+{
+       cpufreq_unregister_notifier(&info->freq_transition,
+                                   CPUFREQ_TRANSITION_NOTIFIER);
+}
+
+#else
+static inline int s3c2410fb_cpufreq_register(struct s3c2410fb_info *info)
+{
+       return 0;
+}
+
+static inline void s3c2410fb_cpufreq_deregister(struct s3c2410fb_info *info)
+{
+}
+#endif
+
+
 static char driver_name[] = "s3c2410fb";
 
 static int __init s3c24xxfb_probe(struct platform_device *pdev,
@@ -875,6 +927,8 @@ static int __init s3c24xxfb_probe(struct platform_device *pdev,
 
        msleep(1);
 
+       info->clk_rate = clk_get_rate(info->clk);
+
        /* find maximum required memory size for display */
        for (i = 0; i < mach_info->num_displays; i++) {
                unsigned long smem_len = mach_info->displays[i].xres;
@@ -904,11 +958,17 @@ static int __init s3c24xxfb_probe(struct platform_device *pdev,
 
        s3c2410fb_check_var(&fbinfo->var, fbinfo);
 
+       ret = s3c2410fb_cpufreq_register(info);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to register cpufreq\n");
+               goto free_video_memory;
+       }
+
        ret = register_framebuffer(fbinfo);
        if (ret < 0) {
                printk(KERN_ERR "Failed to register framebuffer device: %d\n",
                        ret);
-               goto free_video_memory;
+               goto free_cpufreq;
        }
 
        /* create device files */
@@ -922,6 +982,8 @@ static int __init s3c24xxfb_probe(struct platform_device *pdev,
 
        return 0;
 
+ free_cpufreq:
+       s3c2410fb_cpufreq_deregister(info);
 free_video_memory:
        s3c2410fb_unmap_video_memory(fbinfo);
 release_clock:
@@ -961,6 +1023,7 @@ static int s3c2410fb_remove(struct platform_device *pdev)
        int irq;
 
        unregister_framebuffer(fbinfo);
+       s3c2410fb_cpufreq_deregister(info);
 
        s3c2410fb_lcd_enable(info, 0);
        msleep(1);