aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Brownell <dbrownell@users.sourceforge.net>2008-02-06 04:39:26 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2008-02-06 13:41:16 -0500
commita9a84c37d1ee50db8f3752b117caf2b48dcd4f8a (patch)
treeadac878234cbe372624c5d33e7d70322972392d7
parentb1230ee50a9903a987feaad767fb71e2fd173894 (diff)
atmel_lcdfb: backlight control
On the sam9 EK boards, the LCD backlight is hooked up to a PWM output from the LCD controller. It's controlled by "contrast" registers though. This patch lets boards declare that they have that kind of backlight control. The driver can then export this control, letting screenblank and other operations actually take effect ... reducing the typically substantial power drain from the backlight. Note that it's not fully cooked - doesn't force backlight off during system suspend - the "power" and "blank" events may not be done right This should be easily added in the future. [nicolas.ferre@atmel.com: remove unneeded inline and rename functions] Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com> Cc: Andrew Victor <linux@maxim.org.za> Cc: Russell King <rmk@arm.linux.org.uk> Cc: "Antonino A. Daplas" <adaplas@pol.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--arch/arm/mach-at91/board-sam9261ek.c1
-rw-r--r--arch/arm/mach-at91/board-sam9263ek.c1
-rw-r--r--drivers/video/atmel_lcdfb.c115
-rw-r--r--drivers/video/backlight/Kconfig13
-rw-r--r--include/video/atmel_lcdc.h11
5 files changed, 135 insertions, 6 deletions
diff --git a/arch/arm/mach-at91/board-sam9261ek.c b/arch/arm/mach-at91/board-sam9261ek.c
index aa29ea58ca09..0ce38dfa6ebe 100644
--- a/arch/arm/mach-at91/board-sam9261ek.c
+++ b/arch/arm/mach-at91/board-sam9261ek.c
@@ -383,6 +383,7 @@ static void at91_lcdc_tft_power_control(int on)
383} 383}
384 384
385static struct atmel_lcdfb_info __initdata ek_lcdc_data = { 385static struct atmel_lcdfb_info __initdata ek_lcdc_data = {
386 .lcdcon_is_backlight = true,
386 .default_bpp = 16, 387 .default_bpp = 16,
387 .default_dmacon = ATMEL_LCDC_DMAEN, 388 .default_dmacon = ATMEL_LCDC_DMAEN,
388 .default_lcdcon2 = AT91SAM9261_DEFAULT_TFT_LCDCON2, 389 .default_lcdcon2 = AT91SAM9261_DEFAULT_TFT_LCDCON2,
diff --git a/arch/arm/mach-at91/board-sam9263ek.c b/arch/arm/mach-at91/board-sam9263ek.c
index f09347a86e71..38313abef657 100644
--- a/arch/arm/mach-at91/board-sam9263ek.c
+++ b/arch/arm/mach-at91/board-sam9263ek.c
@@ -253,6 +253,7 @@ static void at91_lcdc_power_control(int on)
253 253
254/* Driver datas */ 254/* Driver datas */
255static struct atmel_lcdfb_info __initdata ek_lcdc_data = { 255static struct atmel_lcdfb_info __initdata ek_lcdc_data = {
256 .lcdcon_is_backlight = true,
256 .default_bpp = 16, 257 .default_bpp = 16,
257 .default_dmacon = ATMEL_LCDC_DMAEN, 258 .default_dmacon = ATMEL_LCDC_DMAEN,
258 .default_lcdcon2 = AT91SAM9263_DEFAULT_LCDCON2, 259 .default_lcdcon2 = AT91SAM9263_DEFAULT_LCDCON2,
diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c
index 5d22ea532e42..fc65c02306dd 100644
--- a/drivers/video/atmel_lcdfb.c
+++ b/drivers/video/atmel_lcdfb.c
@@ -16,6 +16,7 @@
16#include <linux/fb.h> 16#include <linux/fb.h>
17#include <linux/init.h> 17#include <linux/init.h>
18#include <linux/delay.h> 18#include <linux/delay.h>
19#include <linux/backlight.h>
19 20
20#include <asm/arch/board.h> 21#include <asm/arch/board.h>
21#include <asm/arch/cpu.h> 22#include <asm/arch/cpu.h>
@@ -69,6 +70,107 @@ static void atmel_lcdfb_update_dma2d(struct atmel_lcdfb_info *sinfo,
69} 70}
70#endif 71#endif
71 72
73static const u32 contrast_ctr = ATMEL_LCDC_PS_DIV8
74 | ATMEL_LCDC_POL_POSITIVE
75 | ATMEL_LCDC_ENA_PWMENABLE;
76
77#ifdef CONFIG_BACKLIGHT_ATMEL_LCDC
78
79/* some bl->props field just changed */
80static int atmel_bl_update_status(struct backlight_device *bl)
81{
82 struct atmel_lcdfb_info *sinfo = bl_get_data(bl);
83 int power = sinfo->bl_power;
84 int brightness = bl->props.brightness;
85
86 /* REVISIT there may be a meaningful difference between
87 * fb_blank and power ... there seem to be some cases
88 * this doesn't handle correctly.
89 */
90 if (bl->props.fb_blank != sinfo->bl_power)
91 power = bl->props.fb_blank;
92 else if (bl->props.power != sinfo->bl_power)
93 power = bl->props.power;
94
95 if (brightness < 0 && power == FB_BLANK_UNBLANK)
96 brightness = lcdc_readl(sinfo, ATMEL_LCDC_CONTRAST_VAL);
97 else if (power != FB_BLANK_UNBLANK)
98 brightness = 0;
99
100 lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_VAL, brightness);
101 lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_CTR,
102 brightness ? contrast_ctr : 0);
103
104 bl->props.fb_blank = bl->props.power = sinfo->bl_power = power;
105
106 return 0;
107}
108
109static int atmel_bl_get_brightness(struct backlight_device *bl)
110{
111 struct atmel_lcdfb_info *sinfo = bl_get_data(bl);
112
113 return lcdc_readl(sinfo, ATMEL_LCDC_CONTRAST_VAL);
114}
115
116static struct backlight_ops atmel_lcdc_bl_ops = {
117 .update_status = atmel_bl_update_status,
118 .get_brightness = atmel_bl_get_brightness,
119};
120
121static void init_backlight(struct atmel_lcdfb_info *sinfo)
122{
123 struct backlight_device *bl;
124
125 sinfo->bl_power = FB_BLANK_UNBLANK;
126
127 if (sinfo->backlight)
128 return;
129
130 bl = backlight_device_register("backlight", &sinfo->pdev->dev,
131 sinfo, &atmel_lcdc_bl_ops);
132 if (IS_ERR(sinfo->backlight)) {
133 dev_err(&sinfo->pdev->dev, "error %ld on backlight register\n",
134 PTR_ERR(bl));
135 return;
136 }
137 sinfo->backlight = bl;
138
139 bl->props.power = FB_BLANK_UNBLANK;
140 bl->props.fb_blank = FB_BLANK_UNBLANK;
141 bl->props.max_brightness = 0xff;
142 bl->props.brightness = atmel_bl_get_brightness(bl);
143}
144
145static void exit_backlight(struct atmel_lcdfb_info *sinfo)
146{
147 if (sinfo->backlight)
148 backlight_device_unregister(sinfo->backlight);
149}
150
151#else
152
153static void init_backlight(struct atmel_lcdfb_info *sinfo)
154{
155 dev_warn(&sinfo->pdev->dev, "backlight control is not available\n");
156}
157
158static void exit_backlight(struct atmel_lcdfb_info *sinfo)
159{
160}
161
162#endif
163
164static void init_contrast(struct atmel_lcdfb_info *sinfo)
165{
166 /* have some default contrast/backlight settings */
167 lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_CTR, contrast_ctr);
168 lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_VAL, ATMEL_LCDC_CVAL_DEFAULT);
169
170 if (sinfo->lcdcon_is_backlight)
171 init_backlight(sinfo);
172}
173
72 174
73static struct fb_fix_screeninfo atmel_lcdfb_fix __initdata = { 175static struct fb_fix_screeninfo atmel_lcdfb_fix __initdata = {
74 .type = FB_TYPE_PACKED_PIXELS, 176 .type = FB_TYPE_PACKED_PIXELS,
@@ -390,10 +492,6 @@ static int atmel_lcdfb_set_par(struct fb_info *info)
390 /* Disable all interrupts */ 492 /* Disable all interrupts */
391 lcdc_writel(sinfo, ATMEL_LCDC_IDR, ~0UL); 493 lcdc_writel(sinfo, ATMEL_LCDC_IDR, ~0UL);
392 494
393 /* Set contrast */
394 value = ATMEL_LCDC_PS_DIV8 | ATMEL_LCDC_POL_POSITIVE | ATMEL_LCDC_ENA_PWMENABLE;
395 lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_CTR, value);
396 lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_VAL, ATMEL_LCDC_CVAL_DEFAULT);
397 /* ...wait for DMA engine to become idle... */ 495 /* ...wait for DMA engine to become idle... */
398 while (lcdc_readl(sinfo, ATMEL_LCDC_DMACON) & ATMEL_LCDC_DMABUSY) 496 while (lcdc_readl(sinfo, ATMEL_LCDC_DMACON) & ATMEL_LCDC_DMABUSY)
399 msleep(10); 497 msleep(10);
@@ -597,6 +695,7 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev)
597 sinfo->default_monspecs = pdata_sinfo->default_monspecs; 695 sinfo->default_monspecs = pdata_sinfo->default_monspecs;
598 sinfo->atmel_lcdfb_power_control = pdata_sinfo->atmel_lcdfb_power_control; 696 sinfo->atmel_lcdfb_power_control = pdata_sinfo->atmel_lcdfb_power_control;
599 sinfo->guard_time = pdata_sinfo->guard_time; 697 sinfo->guard_time = pdata_sinfo->guard_time;
698 sinfo->lcdcon_is_backlight = pdata_sinfo->lcdcon_is_backlight;
600 } else { 699 } else {
601 dev_err(dev, "cannot get default configuration\n"); 700 dev_err(dev, "cannot get default configuration\n");
602 goto free_info; 701 goto free_info;
@@ -690,6 +789,9 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev)
690 goto release_mem; 789 goto release_mem;
691 } 790 }
692 791
792 /* Initialize PWM for contrast or backlight ("off") */
793 init_contrast(sinfo);
794
693 /* interrupt */ 795 /* interrupt */
694 ret = request_irq(sinfo->irq_base, atmel_lcdfb_interrupt, 0, pdev->name, info); 796 ret = request_irq(sinfo->irq_base, atmel_lcdfb_interrupt, 0, pdev->name, info);
695 if (ret) { 797 if (ret) {
@@ -741,6 +843,7 @@ free_cmap:
741unregister_irqs: 843unregister_irqs:
742 free_irq(sinfo->irq_base, info); 844 free_irq(sinfo->irq_base, info);
743unmap_mmio: 845unmap_mmio:
846 exit_backlight(sinfo);
744 iounmap(sinfo->mmio); 847 iounmap(sinfo->mmio);
745release_mem: 848release_mem:
746 release_mem_region(info->fix.mmio_start, info->fix.mmio_len); 849 release_mem_region(info->fix.mmio_start, info->fix.mmio_len);
@@ -775,6 +878,7 @@ static int __exit atmel_lcdfb_remove(struct platform_device *pdev)
775 if (!sinfo) 878 if (!sinfo)
776 return 0; 879 return 0;
777 880
881 exit_backlight(sinfo);
778 if (sinfo->atmel_lcdfb_power_control) 882 if (sinfo->atmel_lcdfb_power_control)
779 sinfo->atmel_lcdfb_power_control(0); 883 sinfo->atmel_lcdfb_power_control(0);
780 unregister_framebuffer(info); 884 unregister_framebuffer(info);
@@ -801,6 +905,9 @@ static int __exit atmel_lcdfb_remove(struct platform_device *pdev)
801 905
802static struct platform_driver atmel_lcdfb_driver = { 906static struct platform_driver atmel_lcdfb_driver = {
803 .remove = __exit_p(atmel_lcdfb_remove), 907 .remove = __exit_p(atmel_lcdfb_remove),
908
909// FIXME need suspend, resume
910
804 .driver = { 911 .driver = {
805 .name = "atmel_lcdfb", 912 .name = "atmel_lcdfb",
806 .owner = THIS_MODULE, 913 .owner = THIS_MODULE,
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
index 9609a6c676be..924e2551044a 100644
--- a/drivers/video/backlight/Kconfig
+++ b/drivers/video/backlight/Kconfig
@@ -50,6 +50,19 @@ config BACKLIGHT_CLASS_DEVICE
50 To have support for your specific LCD panel you will have to 50 To have support for your specific LCD panel you will have to
51 select the proper drivers which depend on this option. 51 select the proper drivers which depend on this option.
52 52
53config BACKLIGHT_ATMEL_LCDC
54 bool "Atmel LCDC Contrast-as-Backlight control"
55 depends on BACKLIGHT_CLASS_DEVICE && FB_ATMEL
56 default y if MACH_SAM9261EK || MACH_SAM9263EK
57 help
58 This provides a backlight control internal to the Atmel LCDC
59 driver. If the LCD "contrast control" on your board is wired
60 so it controls the backlight brightness, select this option to
61 export this as a PWM-based backlight control.
62
63 If in doubt, it's safe to enable this option; it doesn't kick
64 in unless the board's description says it's wired that way.
65
53config BACKLIGHT_CORGI 66config BACKLIGHT_CORGI
54 tristate "Generic (aka Sharp Corgi) Backlight Driver" 67 tristate "Generic (aka Sharp Corgi) Backlight Driver"
55 depends on BACKLIGHT_CLASS_DEVICE 68 depends on BACKLIGHT_CLASS_DEVICE
diff --git a/include/video/atmel_lcdc.h b/include/video/atmel_lcdc.h
index 76095e70935b..336c20db87f8 100644
--- a/include/video/atmel_lcdc.h
+++ b/include/video/atmel_lcdc.h
@@ -22,7 +22,7 @@
22#ifndef __ATMEL_LCDC_H__ 22#ifndef __ATMEL_LCDC_H__
23#define __ATMEL_LCDC_H__ 23#define __ATMEL_LCDC_H__
24 24
25 /* LCD Controller info data structure */ 25 /* LCD Controller info data structure, stored in device platform_data */
26struct atmel_lcdfb_info { 26struct atmel_lcdfb_info {
27 spinlock_t lock; 27 spinlock_t lock;
28 struct fb_info *info; 28 struct fb_info *info;
@@ -33,7 +33,14 @@ struct atmel_lcdfb_info {
33 struct platform_device *pdev; 33 struct platform_device *pdev;
34 struct clk *bus_clk; 34 struct clk *bus_clk;
35 struct clk *lcdc_clk; 35 struct clk *lcdc_clk;
36 unsigned int default_bpp; 36
37#ifdef CONFIG_BACKLIGHT_ATMEL_LCDC
38 struct backlight_device *backlight;
39 u8 bl_power;
40#endif
41 bool lcdcon_is_backlight;
42
43 u8 default_bpp;
37 unsigned int default_lcdcon2; 44 unsigned int default_lcdcon2;
38 unsigned int default_dmacon; 45 unsigned int default_dmacon;
39 void (*atmel_lcdfb_power_control)(int on); 46 void (*atmel_lcdfb_power_control)(int on);