aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/atmel_lcdfb.c
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 /drivers/video/atmel_lcdfb.c
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>
Diffstat (limited to 'drivers/video/atmel_lcdfb.c')
-rw-r--r--drivers/video/atmel_lcdfb.c115
1 files changed, 111 insertions, 4 deletions
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,