blob: 3c026b036c86c6f65c600022f24c9291d017639e [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Backlight Driver for Sharp Corgi
3 *
4 * Copyright (c) 2004-2005 Richard Purdie
5 *
6 * Based on Sharp's 2.4 Backlight Driver
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 *
12 */
13
14#include <linux/module.h>
15#include <linux/kernel.h>
16#include <linux/init.h>
17#include <linux/device.h>
18#include <linux/spinlock.h>
19#include <linux/fb.h>
20#include <linux/backlight.h>
21
22#include <asm/arch-pxa/corgi.h>
23#include <asm/hardware/scoop.h>
24
25#define CORGI_MAX_INTENSITY 0x3e
26#define CORGI_DEFAULT_INTENSITY 0x1f
27#define CORGI_LIMIT_MASK 0x0b
28
29static int corgibl_powermode = FB_BLANK_UNBLANK;
30static int current_intensity = 0;
31static int corgibl_limit = 0;
32static spinlock_t bl_lock = SPIN_LOCK_UNLOCKED;
33
34static void corgibl_send_intensity(int intensity)
35{
36 unsigned long flags;
37 void (*corgi_kick_batt)(void);
38
39 if (corgibl_powermode != FB_BLANK_UNBLANK) {
40 intensity = 0;
41 } else {
42 if (corgibl_limit)
43 intensity &= CORGI_LIMIT_MASK;
44 }
45
46 /* Skip 0x20 as it will blank the display */
47 if (intensity >= 0x20)
48 intensity++;
49
50 spin_lock_irqsave(&bl_lock, flags);
51 /* Bits 0-4 are accessed via the SSP interface */
52 corgi_ssp_blduty_set(intensity & 0x1f);
53 /* Bit 5 is via SCOOP */
54 if (intensity & 0x0020)
55 set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_BACKLIGHT_CONT);
56 else
57 reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_BACKLIGHT_CONT);
58 spin_unlock_irqrestore(&bl_lock, flags);
59}
60
61static void corgibl_blank(int blank)
62{
63 switch(blank) {
64
65 case FB_BLANK_NORMAL:
66 case FB_BLANK_VSYNC_SUSPEND:
67 case FB_BLANK_HSYNC_SUSPEND:
68 case FB_BLANK_POWERDOWN:
69 if (corgibl_powermode == FB_BLANK_UNBLANK) {
70 corgibl_send_intensity(0);
71 corgibl_powermode = blank;
72 }
73 break;
74 case FB_BLANK_UNBLANK:
75 if (corgibl_powermode != FB_BLANK_UNBLANK) {
76 corgibl_powermode = blank;
77 corgibl_send_intensity(current_intensity);
78 }
79 break;
80 }
81}
82
83#ifdef CONFIG_PM
84static int corgibl_suspend(struct device *dev, u32 state, u32 level)
85{
86 if (level == SUSPEND_POWER_DOWN)
87 corgibl_blank(FB_BLANK_POWERDOWN);
88 return 0;
89}
90
91static int corgibl_resume(struct device *dev, u32 level)
92{
93 if (level == RESUME_POWER_ON)
94 corgibl_blank(FB_BLANK_UNBLANK);
95 return 0;
96}
97#else
98#define corgibl_suspend NULL
99#define corgibl_resume NULL
100#endif
101
102
103static int corgibl_set_power(struct backlight_device *bd, int state)
104{
105 corgibl_blank(state);
106 return 0;
107}
108
109static int corgibl_get_power(struct backlight_device *bd)
110{
111 return corgibl_powermode;
112}
113
114static int corgibl_set_intensity(struct backlight_device *bd, int intensity)
115{
116 if (intensity > CORGI_MAX_INTENSITY)
117 intensity = CORGI_MAX_INTENSITY;
118 corgibl_send_intensity(intensity);
119 current_intensity=intensity;
120 return 0;
121}
122
123static int corgibl_get_intensity(struct backlight_device *bd)
124{
125 return current_intensity;
126}
127
128/*
129 * Called when the battery is low to limit the backlight intensity.
130 * If limit==0 clear any limit, otherwise limit the intensity
131 */
132void corgibl_limit_intensity(int limit)
133{
134 corgibl_limit = (limit ? 1 : 0);
135 corgibl_send_intensity(current_intensity);
136}
137EXPORT_SYMBOL(corgibl_limit_intensity);
138
139
140static struct backlight_properties corgibl_data = {
141 .owner = THIS_MODULE,
142 .get_power = corgibl_get_power,
143 .set_power = corgibl_set_power,
144 .max_brightness = CORGI_MAX_INTENSITY,
145 .get_brightness = corgibl_get_intensity,
146 .set_brightness = corgibl_set_intensity,
147};
148
149static struct backlight_device *corgi_backlight_device;
150
151static int __init corgibl_probe(struct device *dev)
152{
153 corgi_backlight_device = backlight_device_register ("corgi-bl",
154 NULL, &corgibl_data);
155 if (IS_ERR (corgi_backlight_device))
156 return PTR_ERR (corgi_backlight_device);
157
158 corgibl_set_intensity(NULL, CORGI_DEFAULT_INTENSITY);
159
160 printk("Corgi Backlight Driver Initialized.\n");
161 return 0;
162}
163
164static int corgibl_remove(struct device *dev)
165{
166 backlight_device_unregister(corgi_backlight_device);
167
168 corgibl_set_intensity(NULL, 0);
169
170 printk("Corgi Backlight Driver Unloaded\n");
171 return 0;
172}
173
174static struct device_driver corgibl_driver = {
175 .name = "corgi-bl",
176 .bus = &platform_bus_type,
177 .probe = corgibl_probe,
178 .remove = corgibl_remove,
179 .suspend = corgibl_suspend,
180 .resume = corgibl_resume,
181};
182
183static int __init corgibl_init(void)
184{
185 return driver_register(&corgibl_driver);
186}
187
188static void __exit corgibl_exit(void)
189{
190 driver_unregister(&corgibl_driver);
191}
192
193module_init(corgibl_init);
194module_exit(corgibl_exit);
195
196MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>");
197MODULE_DESCRIPTION("Corgi Backlight Driver");
198MODULE_LICENSE("GPLv2");