Commit 0dac6ecd authored by Ben Dooks's avatar Ben Dooks Committed by Linus Torvalds

s3c-fb: CPUFREQ frequency scaling support

Add support for CPU frequency scaling in the S3C24XX video driver.
Signed-off-by: default avatarSimtec Linux Team <linux@simtec.co.uk>
Signed-off-by: default avatarBen Dooks <ben@simtec.co.uk>
Cc: Dave Jones <davej@codemonkey.org.uk>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent ddc518d9
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/cpufreq.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/div64.h> #include <asm/div64.h>
...@@ -89,7 +90,7 @@ static void s3c2410fb_set_lcdaddr(struct fb_info *info) ...@@ -89,7 +90,7 @@ static void s3c2410fb_set_lcdaddr(struct fb_info *info)
static unsigned int s3c2410fb_calc_pixclk(struct s3c2410fb_info *fbi, static unsigned int s3c2410fb_calc_pixclk(struct s3c2410fb_info *fbi,
unsigned long pixclk) unsigned long pixclk)
{ {
unsigned long clk = clk_get_rate(fbi->clk); unsigned long clk = fbi->clk_rate;
unsigned long long div; unsigned long long div;
/* pixclk is in picoseconds, our clock is in Hz /* pixclk is in picoseconds, our clock is in Hz
...@@ -758,6 +759,57 @@ static irqreturn_t s3c2410fb_irq(int irq, void *dev_id) ...@@ -758,6 +759,57 @@ static irqreturn_t s3c2410fb_irq(int irq, void *dev_id)
return IRQ_HANDLED; 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 char driver_name[] = "s3c2410fb";
static int __init s3c24xxfb_probe(struct platform_device *pdev, static int __init s3c24xxfb_probe(struct platform_device *pdev,
...@@ -875,6 +927,8 @@ static int __init s3c24xxfb_probe(struct platform_device *pdev, ...@@ -875,6 +927,8 @@ static int __init s3c24xxfb_probe(struct platform_device *pdev,
msleep(1); msleep(1);
info->clk_rate = clk_get_rate(info->clk);
/* find maximum required memory size for display */ /* find maximum required memory size for display */
for (i = 0; i < mach_info->num_displays; i++) { for (i = 0; i < mach_info->num_displays; i++) {
unsigned long smem_len = mach_info->displays[i].xres; unsigned long smem_len = mach_info->displays[i].xres;
...@@ -904,11 +958,17 @@ static int __init s3c24xxfb_probe(struct platform_device *pdev, ...@@ -904,11 +958,17 @@ static int __init s3c24xxfb_probe(struct platform_device *pdev,
s3c2410fb_check_var(&fbinfo->var, fbinfo); 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); ret = register_framebuffer(fbinfo);
if (ret < 0) { if (ret < 0) {
printk(KERN_ERR "Failed to register framebuffer device: %d\n", printk(KERN_ERR "Failed to register framebuffer device: %d\n",
ret); ret);
goto free_video_memory; goto free_cpufreq;
} }
/* create device files */ /* create device files */
...@@ -922,6 +982,8 @@ static int __init s3c24xxfb_probe(struct platform_device *pdev, ...@@ -922,6 +982,8 @@ static int __init s3c24xxfb_probe(struct platform_device *pdev,
return 0; return 0;
free_cpufreq:
s3c2410fb_cpufreq_deregister(info);
free_video_memory: free_video_memory:
s3c2410fb_unmap_video_memory(fbinfo); s3c2410fb_unmap_video_memory(fbinfo);
release_clock: release_clock:
...@@ -961,6 +1023,7 @@ static int s3c2410fb_remove(struct platform_device *pdev) ...@@ -961,6 +1023,7 @@ static int s3c2410fb_remove(struct platform_device *pdev)
int irq; int irq;
unregister_framebuffer(fbinfo); unregister_framebuffer(fbinfo);
s3c2410fb_cpufreq_deregister(info);
s3c2410fb_lcd_enable(info, 0); s3c2410fb_lcd_enable(info, 0);
msleep(1); msleep(1);
......
...@@ -29,8 +29,13 @@ struct s3c2410fb_info { ...@@ -29,8 +29,13 @@ struct s3c2410fb_info {
enum s3c_drv_type drv_type; enum s3c_drv_type drv_type;
struct s3c2410fb_hw regs; struct s3c2410fb_hw regs;
unsigned long clk_rate;
unsigned int palette_ready; unsigned int palette_ready;
#ifdef CONFIG_CPU_FREQ
struct notifier_block freq_transition;
#endif
/* keep these registers in case we need to re-write palette */ /* keep these registers in case we need to re-write palette */
u32 palette_buffer[256]; u32 palette_buffer[256];
u32 pseudo_pal[16]; u32 pseudo_pal[16];
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment