This is the cpufreq notifier for the S3C2410 framebuffer driver.

Signed-off-by: Cesar Eduardo Barros <[EMAIL PROTECTED]>
---
 drivers/video/Kconfig     |    2 +-
 drivers/video/s3c2410fb.c |  174 ++++++++++++++++++++++++++++++++++++++++++++-
 drivers/video/s3c2410fb.h |    7 ++
 3 files changed, 179 insertions(+), 4 deletions(-)

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 5b3dbcf..94ba455 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -1784,7 +1784,7 @@ config FB_W100
 
 config FB_S3C2410
        tristate "S3C2410 LCD framebuffer support"
-       depends on FB && ARCH_S3C2410
+       depends on FB && ARCH_S3C2410 && (CPU_FREQ=n || CPU_FREQ_S3C2410)
        select FB_CFB_FILLRECT
        select FB_CFB_COPYAREA
        select FB_CFB_IMAGEBLIT
diff --git a/drivers/video/s3c2410fb.c b/drivers/video/s3c2410fb.c
index b3c31d9..f63a68b 100644
--- a/drivers/video/s3c2410fb.c
+++ b/drivers/video/s3c2410fb.c
@@ -84,6 +84,8 @@
 #include <linux/interrupt.h>
 #include <linux/platform_device.h>
 #include <linux/clk.h>
+#include <linux/cpufreq.h>
+#include <linux/console.h>
 
 #include <asm/io.h>
 #include <asm/div64.h>
@@ -92,6 +94,7 @@
 #include <asm/arch/regs-lcd.h>
 #include <asm/arch/regs-gpio.h>
 #include <asm/arch/fb.h>
+#include <asm/arch/s3c2410-cpufreq.h>
 
 #ifdef CONFIG_PM
 #include <linux/pm.h>
@@ -141,10 +144,10 @@ static void s3c2410fb_set_lcdaddr(struct fb_info *info)
  *
  * calculate divisor for clk->pixclk
  */
-static unsigned int s3c2410fb_calc_pixclk(struct s3c2410fb_info *fbi,
-                                         unsigned long pixclk)
+static unsigned int s3c2410fb_calc_pixclk_clk(struct s3c2410fb_info *fbi,
+                                             unsigned long pixclk,
+                                             unsigned long clk)
 {
-       unsigned long clk = clk_get_rate(fbi->clk);
        unsigned long long div;
 
        /* pixclk is in picoseconds, our clock is in Hz
@@ -160,6 +163,13 @@ static unsigned int s3c2410fb_calc_pixclk(struct 
s3c2410fb_info *fbi,
        return div;
 }
 
+static unsigned int s3c2410fb_calc_pixclk(struct s3c2410fb_info *fbi,
+                                         unsigned long pixclk)
+{
+       unsigned long clk = clk_get_rate(fbi->clk);
+       return s3c2410fb_calc_pixclk_clk(fbi, pixclk, clk);
+}
+
 /*
  *     s3c2410fb_check_var():
  *     Get the video params out of 'var'. If a value doesn't fit, round it up,
@@ -463,6 +473,8 @@ static void s3c2410fb_activate_var(struct fb_info *info)
 
        fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID,
        writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1);
+
+       s3c2410_cpufreq_schedule_update_policy();
 }
 
 /*
@@ -777,6 +789,139 @@ static irqreturn_t s3c2410fb_irq(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
+#ifdef CONFIG_CPU_FREQ
+
+#define S3C2410_LCDCON1_CLKVAL_MASK S3C2410_LCDCON1_CLKVAL((1<<10) - 1)
+
+static void s3c2410fb_cpufreq_update_clkval(struct fb_info *info,
+               unsigned long clk)
+{
+       /* Based on s3c2410fb_activate_var. */
+       struct s3c2410fb_info *fbi = info->par;
+       void __iomem *regs = fbi->io;
+       int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT;
+       struct fb_var_screeninfo *var = &info->var;
+       int clkdiv = s3c2410fb_calc_pixclk_clk(fbi, var->pixclock, clk) / 2;
+
+       if (type == S3C2410_LCDCON1_TFT) {
+               --clkdiv;
+               if (clkdiv < 0)
+                       clkdiv = 0;
+       } else {
+               if (clkdiv < 2)
+                       clkdiv = 2;
+       }
+
+       fbi->regs.lcdcon1 &=  ~S3C2410_LCDCON1_CLKVAL_MASK;
+       fbi->regs.lcdcon1 |=  S3C2410_LCDCON1_CLKVAL(clkdiv);
+
+       writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1);
+}
+
+static bool s3c2410fb_cpufreq_validate_clkval(struct fb_info *info,
+               unsigned long clk)
+{
+       /* Based on s3c2410fb_activate_var. */
+       struct s3c2410fb_info *fbi = info->par;
+       int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT;
+       struct fb_var_screeninfo *var = &info->var;
+       int clkdiv = s3c2410fb_calc_pixclk_clk(fbi, var->pixclock, clk) / 2;
+
+       if (type == S3C2410_LCDCON1_TFT) {
+               --clkdiv;
+               if (clkdiv < 0)
+                       return false;
+       } else {
+               if (clkdiv < 2)
+                       return false;
+       }
+
+       return true;
+}
+
+static int s3c2410fb_cpufreq_transition_notifier(struct notifier_block *nb,
+               unsigned long val, void *data)
+{
+       struct s3c2410fb_info *fbi =
+               container_of(nb, struct s3c2410fb_info, cpufreq_transition_nb);
+       struct fb_info *info = dev_get_drvdata(fbi->dev);
+       struct cpufreq_freqs *freqs = data;
+       const struct s3c2410_cpufreq_freqs *target_freqs =
+               s3c2410_cpufreq_target_freqs(freqs);
+       unsigned long clk;
+
+       /*
+        * The comment for fb_ops implies the correct locking here
+        * is console_sem.
+        */
+
+       switch (val) {
+       case CPUFREQ_PRECHANGE:
+               if (target_freqs)
+                       if (target_freqs->new.hclk > target_freqs->old.hclk) {
+                               acquire_console_sem();
+                               s3c2410fb_cpufreq_update_clkval(info,
+                                               target_freqs->new.hclk);
+                               release_console_sem();
+                       }
+               break;
+       case CPUFREQ_POSTCHANGE:
+               if (target_freqs) {
+                       if (target_freqs->old.hclk > target_freqs->new.hclk) {
+                               acquire_console_sem();
+                               s3c2410fb_cpufreq_update_clkval(info,
+                                               target_freqs->new.hclk);
+                               release_console_sem();
+                       }
+                       break;
+               }
+               /* fall through */
+       case CPUFREQ_RESUMECHANGE:
+       case CPUFREQ_SUSPENDCHANGE:
+               /* target_freqs is not valid */
+               clk = clk_get_rate(fbi->clk);
+               acquire_console_sem();
+               s3c2410fb_cpufreq_update_clkval(info, clk);
+               release_console_sem();
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static bool s3c2410fb_cpufreq_policy_adjust_cb(void *data,
+               const struct s3c2410_cpufreq_clocks *clocks)
+{
+       struct fb_info *info = data;
+
+       return s3c2410fb_cpufreq_validate_clkval(info, clocks->hclk);
+}
+
+static int s3c2410fb_cpufreq_policy_notifier(struct notifier_block *nb,
+               unsigned long val, void *data)
+{
+       struct s3c2410fb_info *fbi =
+               container_of(nb, struct s3c2410fb_info, cpufreq_policy_nb);
+       struct fb_info *info = dev_get_drvdata(fbi->dev);
+       struct cpufreq_policy *policy = data;
+
+       switch (val)
+       {
+       case CPUFREQ_ADJUST:
+               s3c2410_cpufreq_adjust_table(policy,
+                               s3c2410fb_cpufreq_policy_adjust_cb, info);
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+#endif
+
 static char driver_name[] = "s3c2410fb";
 
 static int __init s3c2410fb_probe(struct platform_device *pdev)
@@ -920,6 +1065,20 @@ static int __init s3c2410fb_probe(struct platform_device 
*pdev)
                goto free_video_memory;
        }
 
+#ifdef CONFIG_CPU_FREQ
+       info->cpufreq_transition_nb.notifier_call
+               = s3c2410fb_cpufreq_transition_notifier;
+       cpufreq_register_notifier(&info->cpufreq_transition_nb,
+               CPUFREQ_TRANSITION_NOTIFIER);
+
+       info->cpufreq_policy_nb.notifier_call
+               = s3c2410fb_cpufreq_policy_notifier;
+       cpufreq_register_notifier(&info->cpufreq_policy_nb,
+               CPUFREQ_POLICY_NOTIFIER);
+
+       s3c2410_cpufreq_update_policy();
+#endif
+
        /* create device files */
        device_create_file(&pdev->dev, &dev_attr_debug);
 
@@ -971,11 +1130,20 @@ static int s3c2410fb_remove(struct platform_device *pdev)
        struct s3c2410fb_info *info = fbinfo->par;
        int irq;
 
+#ifdef CONFIG_CPU_FREQ
+       cpufreq_unregister_notifier(&info->cpufreq_transition_nb,
+               CPUFREQ_TRANSITION_NOTIFIER);
+       cpufreq_unregister_notifier(&info->cpufreq_policy_nb,
+               CPUFREQ_POLICY_NOTIFIER);
+#endif
+
        unregister_framebuffer(fbinfo);
 
        s3c2410fb_stop_lcd(info);
        msleep(1);
 
+       s3c2410_cpufreq_update_policy();
+
        s3c2410fb_unmap_video_memory(fbinfo);
 
        if (info->clk) {
diff --git a/drivers/video/s3c2410fb.h b/drivers/video/s3c2410fb.h
index 6ce5dc2..ade71cb 100644
--- a/drivers/video/s3c2410fb.h
+++ b/drivers/video/s3c2410fb.h
@@ -25,6 +25,8 @@
 #ifndef __S3C2410FB_H
 #define __S3C2410FB_H
 
+#include <linux/notifier.h>
+
 struct s3c2410fb_info {
        struct device           *dev;
        struct clk              *clk;
@@ -39,6 +41,11 @@ struct s3c2410fb_info {
        /* keep these registers in case we need to re-write palette */
        u32                     palette_buffer[256];
        u32                     pseudo_pal[16];
+
+#ifdef CONFIG_CPU_FREQ
+       struct notifier_block   cpufreq_transition_nb;
+       struct notifier_block   cpufreq_policy_nb;
+#endif
 };
 
 #define PALETTE_BUFF_CLEAR (0x80000000)        /* entry is clear/invalid */
-- 
1.5.4


Reply via email to