diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-09-26 13:49:42 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-09-26 13:49:42 -0400 |
commit | d910fc786014ac3fb72f837c329c112e0c7a9aea (patch) | |
tree | 32f6c6eb4fdcf60fc0384f94502862cb24b8ab38 /drivers/video | |
parent | 1d1764c39815db55e10b2d78732db4d6dd9d6039 (diff) | |
parent | a7998cecf5073e0755feeb7fd50b2bdc08dea6bd (diff) |
Merge branch 'for-linus' of git://git.o-hand.com/linux-rpurdie-backlight
* 'for-linus' of git://git.o-hand.com/linux-rpurdie-backlight:
backlight: new driver for ADP5520/ADP5501 MFD PMICs
backlight: extend event support to also support poll()
backlight/eeepc-laptop: Update the backlight state when we change brightness
backlight/acpi: Update the backlight state when we change brightness
backlight: Allow drivers to update the core, and generate events on changes
backlight: switch to da903x driver to dev_pm_ops
backlight: Add support for the Avionic Design Xanthos backlight device.
backlight: spi driver for LMS283GF05 LCD
backlight: move hp680-bl's probe function to .devinit.text
backlight: Add support for new Apple machines.
backlight: mbp_nvidia_bl: add support for MacBookAir 1,1
backlight: Add WM831x backlight driver
Trivial conflicts due to '#ifdef CONFIG_PM' differences in
drivers/video/backlight/da903x_bl.c
Diffstat (limited to 'drivers/video')
-rw-r--r-- | drivers/video/backlight/Kconfig | 33 | ||||
-rw-r--r-- | drivers/video/backlight/Makefile | 4 | ||||
-rw-r--r-- | drivers/video/backlight/adp5520_bl.c | 377 | ||||
-rw-r--r-- | drivers/video/backlight/adx_bl.c | 178 | ||||
-rw-r--r-- | drivers/video/backlight/backlight.c | 42 | ||||
-rw-r--r-- | drivers/video/backlight/hp680_bl.c | 2 | ||||
-rw-r--r-- | drivers/video/backlight/lms283gf05.c | 242 | ||||
-rw-r--r-- | drivers/video/backlight/mbp_nvidia_bl.c | 36 | ||||
-rw-r--r-- | drivers/video/backlight/wm831x_bl.c | 250 |
9 files changed, 1163 insertions, 1 deletions
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 90861cd93165..09bfa9662e4d 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig | |||
@@ -31,6 +31,13 @@ config LCD_CORGI | |||
31 | Say y here to support the LCD panels usually found on SHARP | 31 | Say y here to support the LCD panels usually found on SHARP |
32 | corgi (C7x0) and spitz (Cxx00) models. | 32 | corgi (C7x0) and spitz (Cxx00) models. |
33 | 33 | ||
34 | config LCD_LMS283GF05 | ||
35 | tristate "Samsung LMS283GF05 LCD" | ||
36 | depends on LCD_CLASS_DEVICE && SPI_MASTER && GENERIC_GPIO | ||
37 | help | ||
38 | SPI driver for Samsung LMS283GF05. This provides basic support | ||
39 | for powering the LCD up/down through a sysfs interface. | ||
40 | |||
34 | config LCD_LTV350QV | 41 | config LCD_LTV350QV |
35 | tristate "Samsung LTV350QV LCD Panel" | 42 | tristate "Samsung LTV350QV LCD Panel" |
36 | depends on LCD_CLASS_DEVICE && SPI_MASTER | 43 | depends on LCD_CLASS_DEVICE && SPI_MASTER |
@@ -229,3 +236,29 @@ config BACKLIGHT_SAHARA | |||
229 | help | 236 | help |
230 | If you have a Tabletkiosk Sahara Touch-iT, say y to enable the | 237 | If you have a Tabletkiosk Sahara Touch-iT, say y to enable the |
231 | backlight driver. | 238 | backlight driver. |
239 | |||
240 | config BACKLIGHT_WM831X | ||
241 | tristate "WM831x PMIC Backlight Driver" | ||
242 | depends on BACKLIGHT_CLASS_DEVICE && MFD_WM831X | ||
243 | help | ||
244 | If you have a backlight driven by the ISINK and DCDC of a | ||
245 | WM831x PMIC say y to enable the backlight driver for it. | ||
246 | |||
247 | config BACKLIGHT_ADX | ||
248 | tristate "Avionic Design Xanthos Backlight Driver" | ||
249 | depends on BACKLIGHT_CLASS_DEVICE && ARCH_PXA_ADX | ||
250 | default y | ||
251 | help | ||
252 | Say Y to enable the backlight driver on Avionic Design Xanthos-based | ||
253 | boards. | ||
254 | |||
255 | config BACKLIGHT_ADP5520 | ||
256 | tristate "Backlight Driver for ADP5520/ADP5501 using WLED" | ||
257 | depends on BACKLIGHT_CLASS_DEVICE && PMIC_ADP5520 | ||
258 | help | ||
259 | If you have a LCD backlight connected to the BST/BL_SNK output of | ||
260 | ADP5520 or ADP5501, say Y here to enable this driver. | ||
261 | |||
262 | To compile this driver as a module, choose M here: the module will | ||
263 | be called adp5520_bl. | ||
264 | |||
diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index 4eb178c1d684..9a405548874c 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile | |||
@@ -3,6 +3,7 @@ | |||
3 | obj-$(CONFIG_LCD_CLASS_DEVICE) += lcd.o | 3 | obj-$(CONFIG_LCD_CLASS_DEVICE) += lcd.o |
4 | obj-$(CONFIG_LCD_CORGI) += corgi_lcd.o | 4 | obj-$(CONFIG_LCD_CORGI) += corgi_lcd.o |
5 | obj-$(CONFIG_LCD_HP700) += jornada720_lcd.o | 5 | obj-$(CONFIG_LCD_HP700) += jornada720_lcd.o |
6 | obj-$(CONFIG_LCD_LMS283GF05) += lms283gf05.o | ||
6 | obj-$(CONFIG_LCD_LTV350QV) += ltv350qv.o | 7 | obj-$(CONFIG_LCD_LTV350QV) += ltv350qv.o |
7 | obj-$(CONFIG_LCD_ILI9320) += ili9320.o | 8 | obj-$(CONFIG_LCD_ILI9320) += ili9320.o |
8 | obj-$(CONFIG_LCD_PLATFORM) += platform_lcd.o | 9 | obj-$(CONFIG_LCD_PLATFORM) += platform_lcd.o |
@@ -24,4 +25,7 @@ obj-$(CONFIG_BACKLIGHT_DA903X) += da903x_bl.o | |||
24 | obj-$(CONFIG_BACKLIGHT_MBP_NVIDIA) += mbp_nvidia_bl.o | 25 | obj-$(CONFIG_BACKLIGHT_MBP_NVIDIA) += mbp_nvidia_bl.o |
25 | obj-$(CONFIG_BACKLIGHT_TOSA) += tosa_bl.o | 26 | obj-$(CONFIG_BACKLIGHT_TOSA) += tosa_bl.o |
26 | obj-$(CONFIG_BACKLIGHT_SAHARA) += kb3886_bl.o | 27 | obj-$(CONFIG_BACKLIGHT_SAHARA) += kb3886_bl.o |
28 | obj-$(CONFIG_BACKLIGHT_WM831X) += wm831x_bl.o | ||
29 | obj-$(CONFIG_BACKLIGHT_ADX) += adx_bl.o | ||
30 | obj-$(CONFIG_BACKLIGHT_ADP5520) += adp5520_bl.o | ||
27 | 31 | ||
diff --git a/drivers/video/backlight/adp5520_bl.c b/drivers/video/backlight/adp5520_bl.c new file mode 100644 index 000000000000..ad05da5ba3c7 --- /dev/null +++ b/drivers/video/backlight/adp5520_bl.c | |||
@@ -0,0 +1,377 @@ | |||
1 | /* | ||
2 | * Backlight driver for Analog Devices ADP5520/ADP5501 MFD PMICs | ||
3 | * | ||
4 | * Copyright 2009 Analog Devices Inc. | ||
5 | * | ||
6 | * Licensed under the GPL-2 or later. | ||
7 | */ | ||
8 | |||
9 | #include <linux/kernel.h> | ||
10 | #include <linux/init.h> | ||
11 | #include <linux/platform_device.h> | ||
12 | #include <linux/fb.h> | ||
13 | #include <linux/backlight.h> | ||
14 | #include <linux/mfd/adp5520.h> | ||
15 | |||
16 | struct adp5520_bl { | ||
17 | struct device *master; | ||
18 | struct adp5520_backlight_platfrom_data *pdata; | ||
19 | struct mutex lock; | ||
20 | unsigned long cached_daylight_max; | ||
21 | int id; | ||
22 | int current_brightness; | ||
23 | }; | ||
24 | |||
25 | static int adp5520_bl_set(struct backlight_device *bl, int brightness) | ||
26 | { | ||
27 | struct adp5520_bl *data = bl_get_data(bl); | ||
28 | struct device *master = data->master; | ||
29 | int ret = 0; | ||
30 | |||
31 | if (data->pdata->en_ambl_sens) { | ||
32 | if ((brightness > 0) && (brightness < ADP5020_MAX_BRIGHTNESS)) { | ||
33 | /* Disable Ambient Light auto adjust */ | ||
34 | ret |= adp5520_clr_bits(master, BL_CONTROL, | ||
35 | BL_AUTO_ADJ); | ||
36 | ret |= adp5520_write(master, DAYLIGHT_MAX, brightness); | ||
37 | } else { | ||
38 | /* | ||
39 | * MAX_BRIGHTNESS -> Enable Ambient Light auto adjust | ||
40 | * restore daylight l3 sysfs brightness | ||
41 | */ | ||
42 | ret |= adp5520_write(master, DAYLIGHT_MAX, | ||
43 | data->cached_daylight_max); | ||
44 | ret |= adp5520_set_bits(master, BL_CONTROL, | ||
45 | BL_AUTO_ADJ); | ||
46 | } | ||
47 | } else { | ||
48 | ret |= adp5520_write(master, DAYLIGHT_MAX, brightness); | ||
49 | } | ||
50 | |||
51 | if (data->current_brightness && brightness == 0) | ||
52 | ret |= adp5520_set_bits(master, | ||
53 | MODE_STATUS, DIM_EN); | ||
54 | else if (data->current_brightness == 0 && brightness) | ||
55 | ret |= adp5520_clr_bits(master, | ||
56 | MODE_STATUS, DIM_EN); | ||
57 | |||
58 | if (!ret) | ||
59 | data->current_brightness = brightness; | ||
60 | |||
61 | return ret; | ||
62 | } | ||
63 | |||
64 | static int adp5520_bl_update_status(struct backlight_device *bl) | ||
65 | { | ||
66 | int brightness = bl->props.brightness; | ||
67 | if (bl->props.power != FB_BLANK_UNBLANK) | ||
68 | brightness = 0; | ||
69 | |||
70 | if (bl->props.fb_blank != FB_BLANK_UNBLANK) | ||
71 | brightness = 0; | ||
72 | |||
73 | return adp5520_bl_set(bl, brightness); | ||
74 | } | ||
75 | |||
76 | static int adp5520_bl_get_brightness(struct backlight_device *bl) | ||
77 | { | ||
78 | struct adp5520_bl *data = bl_get_data(bl); | ||
79 | int error; | ||
80 | uint8_t reg_val; | ||
81 | |||
82 | error = adp5520_read(data->master, BL_VALUE, ®_val); | ||
83 | |||
84 | return error ? data->current_brightness : reg_val; | ||
85 | } | ||
86 | |||
87 | static struct backlight_ops adp5520_bl_ops = { | ||
88 | .update_status = adp5520_bl_update_status, | ||
89 | .get_brightness = adp5520_bl_get_brightness, | ||
90 | }; | ||
91 | |||
92 | static int adp5520_bl_setup(struct backlight_device *bl) | ||
93 | { | ||
94 | struct adp5520_bl *data = bl_get_data(bl); | ||
95 | struct device *master = data->master; | ||
96 | struct adp5520_backlight_platfrom_data *pdata = data->pdata; | ||
97 | int ret = 0; | ||
98 | |||
99 | ret |= adp5520_write(master, DAYLIGHT_MAX, pdata->l1_daylight_max); | ||
100 | ret |= adp5520_write(master, DAYLIGHT_DIM, pdata->l1_daylight_dim); | ||
101 | |||
102 | if (pdata->en_ambl_sens) { | ||
103 | data->cached_daylight_max = pdata->l1_daylight_max; | ||
104 | ret |= adp5520_write(master, OFFICE_MAX, pdata->l2_office_max); | ||
105 | ret |= adp5520_write(master, OFFICE_DIM, pdata->l2_office_dim); | ||
106 | ret |= adp5520_write(master, DARK_MAX, pdata->l3_dark_max); | ||
107 | ret |= adp5520_write(master, DARK_DIM, pdata->l3_dark_dim); | ||
108 | ret |= adp5520_write(master, L2_TRIP, pdata->l2_trip); | ||
109 | ret |= adp5520_write(master, L2_HYS, pdata->l2_hyst); | ||
110 | ret |= adp5520_write(master, L3_TRIP, pdata->l3_trip); | ||
111 | ret |= adp5520_write(master, L3_HYS, pdata->l3_hyst); | ||
112 | ret |= adp5520_write(master, ALS_CMPR_CFG, | ||
113 | ALS_CMPR_CFG_VAL(pdata->abml_filt, L3_EN)); | ||
114 | } | ||
115 | |||
116 | ret |= adp5520_write(master, BL_CONTROL, | ||
117 | BL_CTRL_VAL(pdata->fade_led_law, pdata->en_ambl_sens)); | ||
118 | |||
119 | ret |= adp5520_write(master, BL_FADE, FADE_VAL(pdata->fade_in, | ||
120 | pdata->fade_out)); | ||
121 | |||
122 | ret |= adp5520_set_bits(master, MODE_STATUS, BL_EN | DIM_EN); | ||
123 | |||
124 | return ret; | ||
125 | } | ||
126 | |||
127 | static ssize_t adp5520_show(struct device *dev, char *buf, int reg) | ||
128 | { | ||
129 | struct adp5520_bl *data = dev_get_drvdata(dev); | ||
130 | int error; | ||
131 | uint8_t reg_val; | ||
132 | |||
133 | mutex_lock(&data->lock); | ||
134 | error = adp5520_read(data->master, reg, ®_val); | ||
135 | mutex_unlock(&data->lock); | ||
136 | |||
137 | return sprintf(buf, "%u\n", reg_val); | ||
138 | } | ||
139 | |||
140 | static ssize_t adp5520_store(struct device *dev, const char *buf, | ||
141 | size_t count, int reg) | ||
142 | { | ||
143 | struct adp5520_bl *data = dev_get_drvdata(dev); | ||
144 | unsigned long val; | ||
145 | int ret; | ||
146 | |||
147 | ret = strict_strtoul(buf, 10, &val); | ||
148 | if (ret) | ||
149 | return ret; | ||
150 | |||
151 | mutex_lock(&data->lock); | ||
152 | adp5520_write(data->master, reg, val); | ||
153 | mutex_unlock(&data->lock); | ||
154 | |||
155 | return count; | ||
156 | } | ||
157 | |||
158 | static ssize_t adp5520_bl_dark_max_show(struct device *dev, | ||
159 | struct device_attribute *attr, char *buf) | ||
160 | { | ||
161 | return adp5520_show(dev, buf, DARK_MAX); | ||
162 | } | ||
163 | |||
164 | static ssize_t adp5520_bl_dark_max_store(struct device *dev, | ||
165 | struct device_attribute *attr, const char *buf, size_t count) | ||
166 | { | ||
167 | return adp5520_store(dev, buf, count, DARK_MAX); | ||
168 | } | ||
169 | static DEVICE_ATTR(dark_max, 0664, adp5520_bl_dark_max_show, | ||
170 | adp5520_bl_dark_max_store); | ||
171 | |||
172 | static ssize_t adp5520_bl_office_max_show(struct device *dev, | ||
173 | struct device_attribute *attr, char *buf) | ||
174 | { | ||
175 | return adp5520_show(dev, buf, OFFICE_MAX); | ||
176 | } | ||
177 | |||
178 | static ssize_t adp5520_bl_office_max_store(struct device *dev, | ||
179 | struct device_attribute *attr, const char *buf, size_t count) | ||
180 | { | ||
181 | return adp5520_store(dev, buf, count, OFFICE_MAX); | ||
182 | } | ||
183 | static DEVICE_ATTR(office_max, 0664, adp5520_bl_office_max_show, | ||
184 | adp5520_bl_office_max_store); | ||
185 | |||
186 | static ssize_t adp5520_bl_daylight_max_show(struct device *dev, | ||
187 | struct device_attribute *attr, char *buf) | ||
188 | { | ||
189 | return adp5520_show(dev, buf, DAYLIGHT_MAX); | ||
190 | } | ||
191 | |||
192 | static ssize_t adp5520_bl_daylight_max_store(struct device *dev, | ||
193 | struct device_attribute *attr, const char *buf, size_t count) | ||
194 | { | ||
195 | struct adp5520_bl *data = dev_get_drvdata(dev); | ||
196 | |||
197 | strict_strtoul(buf, 10, &data->cached_daylight_max); | ||
198 | return adp5520_store(dev, buf, count, DAYLIGHT_MAX); | ||
199 | } | ||
200 | static DEVICE_ATTR(daylight_max, 0664, adp5520_bl_daylight_max_show, | ||
201 | adp5520_bl_daylight_max_store); | ||
202 | |||
203 | static ssize_t adp5520_bl_dark_dim_show(struct device *dev, | ||
204 | struct device_attribute *attr, char *buf) | ||
205 | { | ||
206 | return adp5520_show(dev, buf, DARK_DIM); | ||
207 | } | ||
208 | |||
209 | static ssize_t adp5520_bl_dark_dim_store(struct device *dev, | ||
210 | struct device_attribute *attr, | ||
211 | const char *buf, size_t count) | ||
212 | { | ||
213 | return adp5520_store(dev, buf, count, DARK_DIM); | ||
214 | } | ||
215 | static DEVICE_ATTR(dark_dim, 0664, adp5520_bl_dark_dim_show, | ||
216 | adp5520_bl_dark_dim_store); | ||
217 | |||
218 | static ssize_t adp5520_bl_office_dim_show(struct device *dev, | ||
219 | struct device_attribute *attr, char *buf) | ||
220 | { | ||
221 | return adp5520_show(dev, buf, OFFICE_DIM); | ||
222 | } | ||
223 | |||
224 | static ssize_t adp5520_bl_office_dim_store(struct device *dev, | ||
225 | struct device_attribute *attr, | ||
226 | const char *buf, size_t count) | ||
227 | { | ||
228 | return adp5520_store(dev, buf, count, OFFICE_DIM); | ||
229 | } | ||
230 | static DEVICE_ATTR(office_dim, 0664, adp5520_bl_office_dim_show, | ||
231 | adp5520_bl_office_dim_store); | ||
232 | |||
233 | static ssize_t adp5520_bl_daylight_dim_show(struct device *dev, | ||
234 | struct device_attribute *attr, char *buf) | ||
235 | { | ||
236 | return adp5520_show(dev, buf, DAYLIGHT_DIM); | ||
237 | } | ||
238 | |||
239 | static ssize_t adp5520_bl_daylight_dim_store(struct device *dev, | ||
240 | struct device_attribute *attr, | ||
241 | const char *buf, size_t count) | ||
242 | { | ||
243 | return adp5520_store(dev, buf, count, DAYLIGHT_DIM); | ||
244 | } | ||
245 | static DEVICE_ATTR(daylight_dim, 0664, adp5520_bl_daylight_dim_show, | ||
246 | adp5520_bl_daylight_dim_store); | ||
247 | |||
248 | static struct attribute *adp5520_bl_attributes[] = { | ||
249 | &dev_attr_dark_max.attr, | ||
250 | &dev_attr_dark_dim.attr, | ||
251 | &dev_attr_office_max.attr, | ||
252 | &dev_attr_office_dim.attr, | ||
253 | &dev_attr_daylight_max.attr, | ||
254 | &dev_attr_daylight_dim.attr, | ||
255 | NULL | ||
256 | }; | ||
257 | |||
258 | static const struct attribute_group adp5520_bl_attr_group = { | ||
259 | .attrs = adp5520_bl_attributes, | ||
260 | }; | ||
261 | |||
262 | static int __devinit adp5520_bl_probe(struct platform_device *pdev) | ||
263 | { | ||
264 | struct backlight_device *bl; | ||
265 | struct adp5520_bl *data; | ||
266 | int ret = 0; | ||
267 | |||
268 | data = kzalloc(sizeof(*data), GFP_KERNEL); | ||
269 | if (data == NULL) | ||
270 | return -ENOMEM; | ||
271 | |||
272 | data->master = pdev->dev.parent; | ||
273 | data->pdata = pdev->dev.platform_data; | ||
274 | |||
275 | if (data->pdata == NULL) { | ||
276 | dev_err(&pdev->dev, "missing platform data\n"); | ||
277 | kfree(data); | ||
278 | return -ENODEV; | ||
279 | } | ||
280 | |||
281 | data->id = pdev->id; | ||
282 | data->current_brightness = 0; | ||
283 | |||
284 | mutex_init(&data->lock); | ||
285 | |||
286 | bl = backlight_device_register(pdev->name, data->master, | ||
287 | data, &adp5520_bl_ops); | ||
288 | if (IS_ERR(bl)) { | ||
289 | dev_err(&pdev->dev, "failed to register backlight\n"); | ||
290 | kfree(data); | ||
291 | return PTR_ERR(bl); | ||
292 | } | ||
293 | |||
294 | bl->props.max_brightness = | ||
295 | bl->props.brightness = ADP5020_MAX_BRIGHTNESS; | ||
296 | |||
297 | if (data->pdata->en_ambl_sens) | ||
298 | ret = sysfs_create_group(&bl->dev.kobj, | ||
299 | &adp5520_bl_attr_group); | ||
300 | |||
301 | if (ret) { | ||
302 | dev_err(&pdev->dev, "failed to register sysfs\n"); | ||
303 | backlight_device_unregister(bl); | ||
304 | kfree(data); | ||
305 | } | ||
306 | |||
307 | platform_set_drvdata(pdev, bl); | ||
308 | ret |= adp5520_bl_setup(bl); | ||
309 | backlight_update_status(bl); | ||
310 | |||
311 | return ret; | ||
312 | } | ||
313 | |||
314 | static int __devexit adp5520_bl_remove(struct platform_device *pdev) | ||
315 | { | ||
316 | struct backlight_device *bl = platform_get_drvdata(pdev); | ||
317 | struct adp5520_bl *data = bl_get_data(bl); | ||
318 | |||
319 | adp5520_clr_bits(data->master, MODE_STATUS, BL_EN); | ||
320 | |||
321 | if (data->pdata->en_ambl_sens) | ||
322 | sysfs_remove_group(&bl->dev.kobj, | ||
323 | &adp5520_bl_attr_group); | ||
324 | |||
325 | backlight_device_unregister(bl); | ||
326 | kfree(data); | ||
327 | |||
328 | return 0; | ||
329 | } | ||
330 | |||
331 | #ifdef CONFIG_PM | ||
332 | static int adp5520_bl_suspend(struct platform_device *pdev, | ||
333 | pm_message_t state) | ||
334 | { | ||
335 | struct backlight_device *bl = platform_get_drvdata(pdev); | ||
336 | return adp5520_bl_set(bl, 0); | ||
337 | } | ||
338 | |||
339 | static int adp5520_bl_resume(struct platform_device *pdev) | ||
340 | { | ||
341 | struct backlight_device *bl = platform_get_drvdata(pdev); | ||
342 | |||
343 | backlight_update_status(bl); | ||
344 | return 0; | ||
345 | } | ||
346 | #else | ||
347 | #define adp5520_bl_suspend NULL | ||
348 | #define adp5520_bl_resume NULL | ||
349 | #endif | ||
350 | |||
351 | static struct platform_driver adp5520_bl_driver = { | ||
352 | .driver = { | ||
353 | .name = "adp5520-backlight", | ||
354 | .owner = THIS_MODULE, | ||
355 | }, | ||
356 | .probe = adp5520_bl_probe, | ||
357 | .remove = __devexit_p(adp5520_bl_remove), | ||
358 | .suspend = adp5520_bl_suspend, | ||
359 | .resume = adp5520_bl_resume, | ||
360 | }; | ||
361 | |||
362 | static int __init adp5520_bl_init(void) | ||
363 | { | ||
364 | return platform_driver_register(&adp5520_bl_driver); | ||
365 | } | ||
366 | module_init(adp5520_bl_init); | ||
367 | |||
368 | static void __exit adp5520_bl_exit(void) | ||
369 | { | ||
370 | platform_driver_unregister(&adp5520_bl_driver); | ||
371 | } | ||
372 | module_exit(adp5520_bl_exit); | ||
373 | |||
374 | MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); | ||
375 | MODULE_DESCRIPTION("ADP5520(01) Backlight Driver"); | ||
376 | MODULE_LICENSE("GPL"); | ||
377 | MODULE_ALIAS("platform:adp5520-backlight"); | ||
diff --git a/drivers/video/backlight/adx_bl.c b/drivers/video/backlight/adx_bl.c new file mode 100644 index 000000000000..2c3bdfc620b7 --- /dev/null +++ b/drivers/video/backlight/adx_bl.c | |||
@@ -0,0 +1,178 @@ | |||
1 | /* | ||
2 | * linux/drivers/video/backlight/adx.c | ||
3 | * | ||
4 | * Copyright (C) 2009 Avionic Design GmbH | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * Written by Thierry Reding <thierry.reding@avionic-design.de> | ||
11 | */ | ||
12 | |||
13 | #include <linux/backlight.h> | ||
14 | #include <linux/fb.h> | ||
15 | #include <linux/io.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/platform_device.h> | ||
18 | |||
19 | /* register definitions */ | ||
20 | #define ADX_BACKLIGHT_CONTROL 0x00 | ||
21 | #define ADX_BACKLIGHT_CONTROL_ENABLE (1 << 0) | ||
22 | #define ADX_BACKLIGHT_BRIGHTNESS 0x08 | ||
23 | #define ADX_BACKLIGHT_STATUS 0x10 | ||
24 | #define ADX_BACKLIGHT_ERROR 0x18 | ||
25 | |||
26 | struct adxbl { | ||
27 | void __iomem *base; | ||
28 | }; | ||
29 | |||
30 | static int adx_backlight_update_status(struct backlight_device *bldev) | ||
31 | { | ||
32 | struct adxbl *bl = bl_get_data(bldev); | ||
33 | u32 value; | ||
34 | |||
35 | value = bldev->props.brightness; | ||
36 | writel(value, bl->base + ADX_BACKLIGHT_BRIGHTNESS); | ||
37 | |||
38 | value = readl(bl->base + ADX_BACKLIGHT_CONTROL); | ||
39 | |||
40 | if (bldev->props.state & BL_CORE_FBBLANK) | ||
41 | value &= ~ADX_BACKLIGHT_CONTROL_ENABLE; | ||
42 | else | ||
43 | value |= ADX_BACKLIGHT_CONTROL_ENABLE; | ||
44 | |||
45 | writel(value, bl->base + ADX_BACKLIGHT_CONTROL); | ||
46 | |||
47 | return 0; | ||
48 | } | ||
49 | |||
50 | static int adx_backlight_get_brightness(struct backlight_device *bldev) | ||
51 | { | ||
52 | struct adxbl *bl = bl_get_data(bldev); | ||
53 | u32 brightness; | ||
54 | |||
55 | brightness = readl(bl->base + ADX_BACKLIGHT_BRIGHTNESS); | ||
56 | return brightness & 0xff; | ||
57 | } | ||
58 | |||
59 | static int adx_backlight_check_fb(struct fb_info *fb) | ||
60 | { | ||
61 | return 1; | ||
62 | } | ||
63 | |||
64 | static struct backlight_ops adx_backlight_ops = { | ||
65 | .options = 0, | ||
66 | .update_status = adx_backlight_update_status, | ||
67 | .get_brightness = adx_backlight_get_brightness, | ||
68 | .check_fb = adx_backlight_check_fb, | ||
69 | }; | ||
70 | |||
71 | static int __devinit adx_backlight_probe(struct platform_device *pdev) | ||
72 | { | ||
73 | struct backlight_device *bldev; | ||
74 | struct resource *res; | ||
75 | struct adxbl *bl; | ||
76 | int ret = 0; | ||
77 | |||
78 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
79 | if (!res) { | ||
80 | ret = -ENXIO; | ||
81 | goto out; | ||
82 | } | ||
83 | |||
84 | res = devm_request_mem_region(&pdev->dev, res->start, | ||
85 | resource_size(res), res->name); | ||
86 | if (!res) { | ||
87 | ret = -ENXIO; | ||
88 | goto out; | ||
89 | } | ||
90 | |||
91 | bl = devm_kzalloc(&pdev->dev, sizeof(*bl), GFP_KERNEL); | ||
92 | if (!bl) { | ||
93 | ret = -ENOMEM; | ||
94 | goto out; | ||
95 | } | ||
96 | |||
97 | bl->base = devm_ioremap_nocache(&pdev->dev, res->start, | ||
98 | resource_size(res)); | ||
99 | if (!bl->base) { | ||
100 | ret = -ENXIO; | ||
101 | goto out; | ||
102 | } | ||
103 | |||
104 | bldev = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, bl, | ||
105 | &adx_backlight_ops); | ||
106 | if (!bldev) { | ||
107 | ret = -ENOMEM; | ||
108 | goto out; | ||
109 | } | ||
110 | |||
111 | bldev->props.max_brightness = 0xff; | ||
112 | bldev->props.brightness = 0xff; | ||
113 | bldev->props.power = FB_BLANK_UNBLANK; | ||
114 | |||
115 | platform_set_drvdata(pdev, bldev); | ||
116 | |||
117 | out: | ||
118 | return ret; | ||
119 | } | ||
120 | |||
121 | static int __devexit adx_backlight_remove(struct platform_device *pdev) | ||
122 | { | ||
123 | struct backlight_device *bldev; | ||
124 | int ret = 0; | ||
125 | |||
126 | bldev = platform_get_drvdata(pdev); | ||
127 | bldev->props.power = FB_BLANK_UNBLANK; | ||
128 | bldev->props.brightness = 0xff; | ||
129 | backlight_update_status(bldev); | ||
130 | backlight_device_unregister(bldev); | ||
131 | platform_set_drvdata(pdev, NULL); | ||
132 | |||
133 | return ret; | ||
134 | } | ||
135 | |||
136 | #ifdef CONFIG_PM | ||
137 | static int adx_backlight_suspend(struct platform_device *pdev, | ||
138 | pm_message_t state) | ||
139 | { | ||
140 | return 0; | ||
141 | } | ||
142 | |||
143 | static int adx_backlight_resume(struct platform_device *pdev) | ||
144 | { | ||
145 | return 0; | ||
146 | } | ||
147 | #else | ||
148 | #define adx_backlight_suspend NULL | ||
149 | #define adx_backlight_resume NULL | ||
150 | #endif | ||
151 | |||
152 | static struct platform_driver adx_backlight_driver = { | ||
153 | .probe = adx_backlight_probe, | ||
154 | .remove = __devexit_p(adx_backlight_remove), | ||
155 | .suspend = adx_backlight_suspend, | ||
156 | .resume = adx_backlight_resume, | ||
157 | .driver = { | ||
158 | .name = "adx-backlight", | ||
159 | .owner = THIS_MODULE, | ||
160 | }, | ||
161 | }; | ||
162 | |||
163 | static int __init adx_backlight_init(void) | ||
164 | { | ||
165 | return platform_driver_register(&adx_backlight_driver); | ||
166 | } | ||
167 | |||
168 | static void __exit adx_backlight_exit(void) | ||
169 | { | ||
170 | platform_driver_unregister(&adx_backlight_driver); | ||
171 | } | ||
172 | |||
173 | module_init(adx_backlight_init); | ||
174 | module_exit(adx_backlight_exit); | ||
175 | |||
176 | MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>"); | ||
177 | MODULE_DESCRIPTION("Avionic Design Xanthos Backlight Driver"); | ||
178 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c index 157057c79ca3..6615ac7fa60a 100644 --- a/drivers/video/backlight/backlight.c +++ b/drivers/video/backlight/backlight.c | |||
@@ -73,6 +73,27 @@ static inline void backlight_unregister_fb(struct backlight_device *bd) | |||
73 | } | 73 | } |
74 | #endif /* CONFIG_FB */ | 74 | #endif /* CONFIG_FB */ |
75 | 75 | ||
76 | static void backlight_generate_event(struct backlight_device *bd, | ||
77 | enum backlight_update_reason reason) | ||
78 | { | ||
79 | char *envp[2]; | ||
80 | |||
81 | switch (reason) { | ||
82 | case BACKLIGHT_UPDATE_SYSFS: | ||
83 | envp[0] = "SOURCE=sysfs"; | ||
84 | break; | ||
85 | case BACKLIGHT_UPDATE_HOTKEY: | ||
86 | envp[0] = "SOURCE=hotkey"; | ||
87 | break; | ||
88 | default: | ||
89 | envp[0] = "SOURCE=unknown"; | ||
90 | break; | ||
91 | } | ||
92 | envp[1] = NULL; | ||
93 | kobject_uevent_env(&bd->dev.kobj, KOBJ_CHANGE, envp); | ||
94 | sysfs_notify(&bd->dev.kobj, NULL, "actual_brightness"); | ||
95 | } | ||
96 | |||
76 | static ssize_t backlight_show_power(struct device *dev, | 97 | static ssize_t backlight_show_power(struct device *dev, |
77 | struct device_attribute *attr,char *buf) | 98 | struct device_attribute *attr,char *buf) |
78 | { | 99 | { |
@@ -142,6 +163,8 @@ static ssize_t backlight_store_brightness(struct device *dev, | |||
142 | } | 163 | } |
143 | mutex_unlock(&bd->ops_lock); | 164 | mutex_unlock(&bd->ops_lock); |
144 | 165 | ||
166 | backlight_generate_event(bd, BACKLIGHT_UPDATE_SYSFS); | ||
167 | |||
145 | return rc; | 168 | return rc; |
146 | } | 169 | } |
147 | 170 | ||
@@ -214,6 +237,25 @@ static struct device_attribute bl_device_attributes[] = { | |||
214 | }; | 237 | }; |
215 | 238 | ||
216 | /** | 239 | /** |
240 | * backlight_force_update - tell the backlight subsystem that hardware state | ||
241 | * has changed | ||
242 | * @bd: the backlight device to update | ||
243 | * | ||
244 | * Updates the internal state of the backlight in response to a hardware event, | ||
245 | * and generate a uevent to notify userspace | ||
246 | */ | ||
247 | void backlight_force_update(struct backlight_device *bd, | ||
248 | enum backlight_update_reason reason) | ||
249 | { | ||
250 | mutex_lock(&bd->ops_lock); | ||
251 | if (bd->ops && bd->ops->get_brightness) | ||
252 | bd->props.brightness = bd->ops->get_brightness(bd); | ||
253 | mutex_unlock(&bd->ops_lock); | ||
254 | backlight_generate_event(bd, reason); | ||
255 | } | ||
256 | EXPORT_SYMBOL(backlight_force_update); | ||
257 | |||
258 | /** | ||
217 | * backlight_device_register - create and register a new object of | 259 | * backlight_device_register - create and register a new object of |
218 | * backlight_device class. | 260 | * backlight_device class. |
219 | * @name: the name of the new object(must be the same as the name of the | 261 | * @name: the name of the new object(must be the same as the name of the |
diff --git a/drivers/video/backlight/hp680_bl.c b/drivers/video/backlight/hp680_bl.c index 5be55a20d8c7..7fb4eefff80d 100644 --- a/drivers/video/backlight/hp680_bl.c +++ b/drivers/video/backlight/hp680_bl.c | |||
@@ -103,7 +103,7 @@ static struct backlight_ops hp680bl_ops = { | |||
103 | .update_status = hp680bl_set_intensity, | 103 | .update_status = hp680bl_set_intensity, |
104 | }; | 104 | }; |
105 | 105 | ||
106 | static int __init hp680bl_probe(struct platform_device *pdev) | 106 | static int __devinit hp680bl_probe(struct platform_device *pdev) |
107 | { | 107 | { |
108 | struct backlight_device *bd; | 108 | struct backlight_device *bd; |
109 | 109 | ||
diff --git a/drivers/video/backlight/lms283gf05.c b/drivers/video/backlight/lms283gf05.c new file mode 100644 index 000000000000..447b542a20ca --- /dev/null +++ b/drivers/video/backlight/lms283gf05.c | |||
@@ -0,0 +1,242 @@ | |||
1 | /* | ||
2 | * lms283gf05.c -- support for Samsung LMS283GF05 LCD | ||
3 | * | ||
4 | * Copyright (c) 2009 Marek Vasut <marek.vasut@gmail.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #include <linux/device.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/delay.h> | ||
14 | #include <linux/gpio.h> | ||
15 | #include <linux/lcd.h> | ||
16 | |||
17 | #include <linux/spi/spi.h> | ||
18 | #include <linux/spi/lms283gf05.h> | ||
19 | |||
20 | struct lms283gf05_state { | ||
21 | struct spi_device *spi; | ||
22 | struct lcd_device *ld; | ||
23 | }; | ||
24 | |||
25 | struct lms283gf05_seq { | ||
26 | unsigned char reg; | ||
27 | unsigned short value; | ||
28 | unsigned char delay; | ||
29 | }; | ||
30 | |||
31 | /* Magic sequences supplied by manufacturer, for details refer to datasheet */ | ||
32 | static struct lms283gf05_seq disp_initseq[] = { | ||
33 | /* REG, VALUE, DELAY */ | ||
34 | { 0x07, 0x0000, 0 }, | ||
35 | { 0x13, 0x0000, 10 }, | ||
36 | |||
37 | { 0x11, 0x3004, 0 }, | ||
38 | { 0x14, 0x200F, 0 }, | ||
39 | { 0x10, 0x1a20, 0 }, | ||
40 | { 0x13, 0x0040, 50 }, | ||
41 | |||
42 | { 0x13, 0x0060, 0 }, | ||
43 | { 0x13, 0x0070, 200 }, | ||
44 | |||
45 | { 0x01, 0x0127, 0 }, | ||
46 | { 0x02, 0x0700, 0 }, | ||
47 | { 0x03, 0x1030, 0 }, | ||
48 | { 0x08, 0x0208, 0 }, | ||
49 | { 0x0B, 0x0620, 0 }, | ||
50 | { 0x0C, 0x0110, 0 }, | ||
51 | { 0x30, 0x0120, 0 }, | ||
52 | { 0x31, 0x0127, 0 }, | ||
53 | { 0x32, 0x0000, 0 }, | ||
54 | { 0x33, 0x0503, 0 }, | ||
55 | { 0x34, 0x0727, 0 }, | ||
56 | { 0x35, 0x0124, 0 }, | ||
57 | { 0x36, 0x0706, 0 }, | ||
58 | { 0x37, 0x0701, 0 }, | ||
59 | { 0x38, 0x0F00, 0 }, | ||
60 | { 0x39, 0x0F00, 0 }, | ||
61 | { 0x40, 0x0000, 0 }, | ||
62 | { 0x41, 0x0000, 0 }, | ||
63 | { 0x42, 0x013f, 0 }, | ||
64 | { 0x43, 0x0000, 0 }, | ||
65 | { 0x44, 0x013f, 0 }, | ||
66 | { 0x45, 0x0000, 0 }, | ||
67 | { 0x46, 0xef00, 0 }, | ||
68 | { 0x47, 0x013f, 0 }, | ||
69 | { 0x48, 0x0000, 0 }, | ||
70 | { 0x07, 0x0015, 30 }, | ||
71 | |||
72 | { 0x07, 0x0017, 0 }, | ||
73 | |||
74 | { 0x20, 0x0000, 0 }, | ||
75 | { 0x21, 0x0000, 0 }, | ||
76 | { 0x22, 0x0000, 0 } | ||
77 | }; | ||
78 | |||
79 | static struct lms283gf05_seq disp_pdwnseq[] = { | ||
80 | { 0x07, 0x0016, 30 }, | ||
81 | |||
82 | { 0x07, 0x0004, 0 }, | ||
83 | { 0x10, 0x0220, 20 }, | ||
84 | |||
85 | { 0x13, 0x0060, 50 }, | ||
86 | |||
87 | { 0x13, 0x0040, 50 }, | ||
88 | |||
89 | { 0x13, 0x0000, 0 }, | ||
90 | { 0x10, 0x0000, 0 } | ||
91 | }; | ||
92 | |||
93 | |||
94 | static void lms283gf05_reset(unsigned long gpio, bool inverted) | ||
95 | { | ||
96 | gpio_set_value(gpio, !inverted); | ||
97 | mdelay(100); | ||
98 | gpio_set_value(gpio, inverted); | ||
99 | mdelay(20); | ||
100 | gpio_set_value(gpio, !inverted); | ||
101 | mdelay(20); | ||
102 | } | ||
103 | |||
104 | static void lms283gf05_toggle(struct spi_device *spi, | ||
105 | struct lms283gf05_seq *seq, int sz) | ||
106 | { | ||
107 | char buf[3]; | ||
108 | int i; | ||
109 | |||
110 | for (i = 0; i < sz; i++) { | ||
111 | buf[0] = 0x74; | ||
112 | buf[1] = 0x00; | ||
113 | buf[2] = seq[i].reg; | ||
114 | spi_write(spi, buf, 3); | ||
115 | |||
116 | buf[0] = 0x76; | ||
117 | buf[1] = seq[i].value >> 8; | ||
118 | buf[2] = seq[i].value & 0xff; | ||
119 | spi_write(spi, buf, 3); | ||
120 | |||
121 | mdelay(seq[i].delay); | ||
122 | } | ||
123 | } | ||
124 | |||
125 | static int lms283gf05_power_set(struct lcd_device *ld, int power) | ||
126 | { | ||
127 | struct lms283gf05_state *st = lcd_get_data(ld); | ||
128 | struct spi_device *spi = st->spi; | ||
129 | struct lms283gf05_pdata *pdata = spi->dev.platform_data; | ||
130 | |||
131 | if (power) { | ||
132 | if (pdata) | ||
133 | lms283gf05_reset(pdata->reset_gpio, | ||
134 | pdata->reset_inverted); | ||
135 | lms283gf05_toggle(spi, disp_initseq, ARRAY_SIZE(disp_initseq)); | ||
136 | } else { | ||
137 | lms283gf05_toggle(spi, disp_pdwnseq, ARRAY_SIZE(disp_pdwnseq)); | ||
138 | if (pdata) | ||
139 | gpio_set_value(pdata->reset_gpio, | ||
140 | pdata->reset_inverted); | ||
141 | } | ||
142 | |||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | static struct lcd_ops lms_ops = { | ||
147 | .set_power = lms283gf05_power_set, | ||
148 | .get_power = NULL, | ||
149 | }; | ||
150 | |||
151 | static int __devinit lms283gf05_probe(struct spi_device *spi) | ||
152 | { | ||
153 | struct lms283gf05_state *st; | ||
154 | struct lms283gf05_pdata *pdata = spi->dev.platform_data; | ||
155 | struct lcd_device *ld; | ||
156 | int ret = 0; | ||
157 | |||
158 | if (pdata != NULL) { | ||
159 | ret = gpio_request(pdata->reset_gpio, "LMS285GF05 RESET"); | ||
160 | if (ret) | ||
161 | return ret; | ||
162 | |||
163 | ret = gpio_direction_output(pdata->reset_gpio, | ||
164 | !pdata->reset_inverted); | ||
165 | if (ret) | ||
166 | goto err; | ||
167 | } | ||
168 | |||
169 | st = kzalloc(sizeof(struct lms283gf05_state), GFP_KERNEL); | ||
170 | if (st == NULL) { | ||
171 | dev_err(&spi->dev, "No memory for device state\n"); | ||
172 | ret = -ENOMEM; | ||
173 | goto err; | ||
174 | } | ||
175 | |||
176 | ld = lcd_device_register("lms283gf05", &spi->dev, st, &lms_ops); | ||
177 | if (IS_ERR(ld)) { | ||
178 | ret = PTR_ERR(ld); | ||
179 | goto err2; | ||
180 | } | ||
181 | |||
182 | st->spi = spi; | ||
183 | st->ld = ld; | ||
184 | |||
185 | dev_set_drvdata(&spi->dev, st); | ||
186 | |||
187 | /* kick in the LCD */ | ||
188 | if (pdata) | ||
189 | lms283gf05_reset(pdata->reset_gpio, pdata->reset_inverted); | ||
190 | lms283gf05_toggle(spi, disp_initseq, ARRAY_SIZE(disp_initseq)); | ||
191 | |||
192 | return 0; | ||
193 | |||
194 | err2: | ||
195 | kfree(st); | ||
196 | err: | ||
197 | if (pdata != NULL) | ||
198 | gpio_free(pdata->reset_gpio); | ||
199 | |||
200 | return ret; | ||
201 | } | ||
202 | |||
203 | static int __devexit lms283gf05_remove(struct spi_device *spi) | ||
204 | { | ||
205 | struct lms283gf05_state *st = dev_get_drvdata(&spi->dev); | ||
206 | struct lms283gf05_pdata *pdata = st->spi->dev.platform_data; | ||
207 | |||
208 | lcd_device_unregister(st->ld); | ||
209 | |||
210 | if (pdata != NULL) | ||
211 | gpio_free(pdata->reset_gpio); | ||
212 | |||
213 | kfree(st); | ||
214 | |||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | static struct spi_driver lms283gf05_driver = { | ||
219 | .driver = { | ||
220 | .name = "lms283gf05", | ||
221 | .owner = THIS_MODULE, | ||
222 | }, | ||
223 | .probe = lms283gf05_probe, | ||
224 | .remove = __devexit_p(lms283gf05_remove), | ||
225 | }; | ||
226 | |||
227 | static __init int lms283gf05_init(void) | ||
228 | { | ||
229 | return spi_register_driver(&lms283gf05_driver); | ||
230 | } | ||
231 | |||
232 | static __exit void lms283gf05_exit(void) | ||
233 | { | ||
234 | spi_unregister_driver(&lms283gf05_driver); | ||
235 | } | ||
236 | |||
237 | module_init(lms283gf05_init); | ||
238 | module_exit(lms283gf05_exit); | ||
239 | |||
240 | MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); | ||
241 | MODULE_DESCRIPTION("LCD283GF05 LCD"); | ||
242 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/video/backlight/mbp_nvidia_bl.c b/drivers/video/backlight/mbp_nvidia_bl.c index 3bb4c0a50c62..9edb8d7c295f 100644 --- a/drivers/video/backlight/mbp_nvidia_bl.c +++ b/drivers/video/backlight/mbp_nvidia_bl.c | |||
@@ -166,6 +166,15 @@ static const struct dmi_system_id __initdata mbp_device_table[] = { | |||
166 | }, | 166 | }, |
167 | { | 167 | { |
168 | .callback = mbp_dmi_match, | 168 | .callback = mbp_dmi_match, |
169 | .ident = "MacBookAir 1,1", | ||
170 | .matches = { | ||
171 | DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), | ||
172 | DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir1,1"), | ||
173 | }, | ||
174 | .driver_data = (void *)&intel_chipset_data, | ||
175 | }, | ||
176 | { | ||
177 | .callback = mbp_dmi_match, | ||
169 | .ident = "MacBook 5,1", | 178 | .ident = "MacBook 5,1", |
170 | .matches = { | 179 | .matches = { |
171 | DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), | 180 | DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), |
@@ -175,6 +184,15 @@ static const struct dmi_system_id __initdata mbp_device_table[] = { | |||
175 | }, | 184 | }, |
176 | { | 185 | { |
177 | .callback = mbp_dmi_match, | 186 | .callback = mbp_dmi_match, |
187 | .ident = "MacBook 5,2", | ||
188 | .matches = { | ||
189 | DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), | ||
190 | DMI_MATCH(DMI_PRODUCT_NAME, "MacBook5,2"), | ||
191 | }, | ||
192 | .driver_data = (void *)&nvidia_chipset_data, | ||
193 | }, | ||
194 | { | ||
195 | .callback = mbp_dmi_match, | ||
178 | .ident = "MacBookAir 2,1", | 196 | .ident = "MacBookAir 2,1", |
179 | .matches = { | 197 | .matches = { |
180 | DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), | 198 | DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), |
@@ -191,6 +209,24 @@ static const struct dmi_system_id __initdata mbp_device_table[] = { | |||
191 | }, | 209 | }, |
192 | .driver_data = (void *)&nvidia_chipset_data, | 210 | .driver_data = (void *)&nvidia_chipset_data, |
193 | }, | 211 | }, |
212 | { | ||
213 | .callback = mbp_dmi_match, | ||
214 | .ident = "MacBookPro 5,2", | ||
215 | .matches = { | ||
216 | DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), | ||
217 | DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5,2"), | ||
218 | }, | ||
219 | .driver_data = (void *)&nvidia_chipset_data, | ||
220 | }, | ||
221 | { | ||
222 | .callback = mbp_dmi_match, | ||
223 | .ident = "MacBookPro 5,5", | ||
224 | .matches = { | ||
225 | DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), | ||
226 | DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5,5"), | ||
227 | }, | ||
228 | .driver_data = (void *)&nvidia_chipset_data, | ||
229 | }, | ||
194 | { } | 230 | { } |
195 | }; | 231 | }; |
196 | 232 | ||
diff --git a/drivers/video/backlight/wm831x_bl.c b/drivers/video/backlight/wm831x_bl.c new file mode 100644 index 000000000000..467bdb7efb23 --- /dev/null +++ b/drivers/video/backlight/wm831x_bl.c | |||
@@ -0,0 +1,250 @@ | |||
1 | /* | ||
2 | * Backlight driver for Wolfson Microelectronics WM831x PMICs | ||
3 | * | ||
4 | * Copyright 2009 Wolfson Microelectonics plc | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/platform_device.h> | ||
14 | #include <linux/fb.h> | ||
15 | #include <linux/backlight.h> | ||
16 | |||
17 | #include <linux/mfd/wm831x/core.h> | ||
18 | #include <linux/mfd/wm831x/pdata.h> | ||
19 | #include <linux/mfd/wm831x/regulator.h> | ||
20 | |||
21 | struct wm831x_backlight_data { | ||
22 | struct wm831x *wm831x; | ||
23 | int isink_reg; | ||
24 | int current_brightness; | ||
25 | }; | ||
26 | |||
27 | static int wm831x_backlight_set(struct backlight_device *bl, int brightness) | ||
28 | { | ||
29 | struct wm831x_backlight_data *data = bl_get_data(bl); | ||
30 | struct wm831x *wm831x = data->wm831x; | ||
31 | int power_up = !data->current_brightness && brightness; | ||
32 | int power_down = data->current_brightness && !brightness; | ||
33 | int ret; | ||
34 | |||
35 | if (power_up) { | ||
36 | /* Enable the ISINK */ | ||
37 | ret = wm831x_set_bits(wm831x, data->isink_reg, | ||
38 | WM831X_CS1_ENA, WM831X_CS1_ENA); | ||
39 | if (ret < 0) | ||
40 | goto err; | ||
41 | |||
42 | /* Enable the DC-DC */ | ||
43 | ret = wm831x_set_bits(wm831x, WM831X_DCDC_ENABLE, | ||
44 | WM831X_DC4_ENA, WM831X_DC4_ENA); | ||
45 | if (ret < 0) | ||
46 | goto err; | ||
47 | } | ||
48 | |||
49 | if (power_down) { | ||
50 | /* DCDC first */ | ||
51 | ret = wm831x_set_bits(wm831x, WM831X_DCDC_ENABLE, | ||
52 | WM831X_DC4_ENA, 0); | ||
53 | if (ret < 0) | ||
54 | goto err; | ||
55 | |||
56 | /* ISINK */ | ||
57 | ret = wm831x_set_bits(wm831x, data->isink_reg, | ||
58 | WM831X_CS1_DRIVE | WM831X_CS1_ENA, 0); | ||
59 | if (ret < 0) | ||
60 | goto err; | ||
61 | } | ||
62 | |||
63 | /* Set the new brightness */ | ||
64 | ret = wm831x_set_bits(wm831x, data->isink_reg, | ||
65 | WM831X_CS1_ISEL_MASK, brightness); | ||
66 | if (ret < 0) | ||
67 | goto err; | ||
68 | |||
69 | if (power_up) { | ||
70 | /* Drive current through the ISINK */ | ||
71 | ret = wm831x_set_bits(wm831x, data->isink_reg, | ||
72 | WM831X_CS1_DRIVE, WM831X_CS1_DRIVE); | ||
73 | if (ret < 0) | ||
74 | return ret; | ||
75 | } | ||
76 | |||
77 | data->current_brightness = brightness; | ||
78 | |||
79 | return 0; | ||
80 | |||
81 | err: | ||
82 | /* If we were in the middle of a power transition always shut down | ||
83 | * for safety. | ||
84 | */ | ||
85 | if (power_up || power_down) { | ||
86 | wm831x_set_bits(wm831x, WM831X_DCDC_ENABLE, WM831X_DC4_ENA, 0); | ||
87 | wm831x_set_bits(wm831x, data->isink_reg, WM831X_CS1_ENA, 0); | ||
88 | } | ||
89 | |||
90 | return ret; | ||
91 | } | ||
92 | |||
93 | static int wm831x_backlight_update_status(struct backlight_device *bl) | ||
94 | { | ||
95 | int brightness = bl->props.brightness; | ||
96 | |||
97 | if (bl->props.power != FB_BLANK_UNBLANK) | ||
98 | brightness = 0; | ||
99 | |||
100 | if (bl->props.fb_blank != FB_BLANK_UNBLANK) | ||
101 | brightness = 0; | ||
102 | |||
103 | if (bl->props.state & BL_CORE_SUSPENDED) | ||
104 | brightness = 0; | ||
105 | |||
106 | return wm831x_backlight_set(bl, brightness); | ||
107 | } | ||
108 | |||
109 | static int wm831x_backlight_get_brightness(struct backlight_device *bl) | ||
110 | { | ||
111 | struct wm831x_backlight_data *data = bl_get_data(bl); | ||
112 | return data->current_brightness; | ||
113 | } | ||
114 | |||
115 | static struct backlight_ops wm831x_backlight_ops = { | ||
116 | .options = BL_CORE_SUSPENDRESUME, | ||
117 | .update_status = wm831x_backlight_update_status, | ||
118 | .get_brightness = wm831x_backlight_get_brightness, | ||
119 | }; | ||
120 | |||
121 | static int wm831x_backlight_probe(struct platform_device *pdev) | ||
122 | { | ||
123 | struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); | ||
124 | struct wm831x_pdata *wm831x_pdata; | ||
125 | struct wm831x_backlight_pdata *pdata; | ||
126 | struct wm831x_backlight_data *data; | ||
127 | struct backlight_device *bl; | ||
128 | int ret, i, max_isel, isink_reg, dcdc_cfg; | ||
129 | |||
130 | /* We need platform data */ | ||
131 | if (pdev->dev.parent->platform_data) { | ||
132 | wm831x_pdata = pdev->dev.parent->platform_data; | ||
133 | pdata = wm831x_pdata->backlight; | ||
134 | } else { | ||
135 | pdata = NULL; | ||
136 | } | ||
137 | |||
138 | if (!pdata) { | ||
139 | dev_err(&pdev->dev, "No platform data supplied\n"); | ||
140 | return -EINVAL; | ||
141 | } | ||
142 | |||
143 | /* Figure out the maximum current we can use */ | ||
144 | for (i = 0; i < WM831X_ISINK_MAX_ISEL; i++) { | ||
145 | if (wm831x_isinkv_values[i] > pdata->max_uA) | ||
146 | break; | ||
147 | } | ||
148 | |||
149 | if (i == 0) { | ||
150 | dev_err(&pdev->dev, "Invalid max_uA: %duA\n", pdata->max_uA); | ||
151 | return -EINVAL; | ||
152 | } | ||
153 | max_isel = i - 1; | ||
154 | |||
155 | if (pdata->max_uA != wm831x_isinkv_values[max_isel]) | ||
156 | dev_warn(&pdev->dev, | ||
157 | "Maximum current is %duA not %duA as requested\n", | ||
158 | wm831x_isinkv_values[max_isel], pdata->max_uA); | ||
159 | |||
160 | switch (pdata->isink) { | ||
161 | case 1: | ||
162 | isink_reg = WM831X_CURRENT_SINK_1; | ||
163 | dcdc_cfg = 0; | ||
164 | break; | ||
165 | case 2: | ||
166 | isink_reg = WM831X_CURRENT_SINK_2; | ||
167 | dcdc_cfg = WM831X_DC4_FBSRC; | ||
168 | break; | ||
169 | default: | ||
170 | dev_err(&pdev->dev, "Invalid ISINK %d\n", pdata->isink); | ||
171 | return -EINVAL; | ||
172 | } | ||
173 | |||
174 | /* Configure the ISINK to use for feedback */ | ||
175 | ret = wm831x_reg_unlock(wm831x); | ||
176 | if (ret < 0) | ||
177 | return ret; | ||
178 | |||
179 | ret = wm831x_set_bits(wm831x, WM831X_DC4_CONTROL, WM831X_DC4_FBSRC, | ||
180 | dcdc_cfg); | ||
181 | |||
182 | wm831x_reg_lock(wm831x); | ||
183 | if (ret < 0) | ||
184 | return ret; | ||
185 | |||
186 | data = kzalloc(sizeof(*data), GFP_KERNEL); | ||
187 | if (data == NULL) | ||
188 | return -ENOMEM; | ||
189 | |||
190 | data->wm831x = wm831x; | ||
191 | data->current_brightness = 0; | ||
192 | data->isink_reg = isink_reg; | ||
193 | |||
194 | bl = backlight_device_register("wm831x", &pdev->dev, | ||
195 | data, &wm831x_backlight_ops); | ||
196 | if (IS_ERR(bl)) { | ||
197 | dev_err(&pdev->dev, "failed to register backlight\n"); | ||
198 | kfree(data); | ||
199 | return PTR_ERR(bl); | ||
200 | } | ||
201 | |||
202 | bl->props.max_brightness = max_isel; | ||
203 | bl->props.brightness = max_isel; | ||
204 | |||
205 | platform_set_drvdata(pdev, bl); | ||
206 | |||
207 | /* Disable the DCDC if it was started so we can bootstrap */ | ||
208 | wm831x_set_bits(wm831x, WM831X_DCDC_ENABLE, WM831X_DC4_ENA, 0); | ||
209 | |||
210 | |||
211 | backlight_update_status(bl); | ||
212 | |||
213 | return 0; | ||
214 | } | ||
215 | |||
216 | static int wm831x_backlight_remove(struct platform_device *pdev) | ||
217 | { | ||
218 | struct backlight_device *bl = platform_get_drvdata(pdev); | ||
219 | struct wm831x_backlight_data *data = bl_get_data(bl); | ||
220 | |||
221 | backlight_device_unregister(bl); | ||
222 | kfree(data); | ||
223 | return 0; | ||
224 | } | ||
225 | |||
226 | static struct platform_driver wm831x_backlight_driver = { | ||
227 | .driver = { | ||
228 | .name = "wm831x-backlight", | ||
229 | .owner = THIS_MODULE, | ||
230 | }, | ||
231 | .probe = wm831x_backlight_probe, | ||
232 | .remove = wm831x_backlight_remove, | ||
233 | }; | ||
234 | |||
235 | static int __init wm831x_backlight_init(void) | ||
236 | { | ||
237 | return platform_driver_register(&wm831x_backlight_driver); | ||
238 | } | ||
239 | module_init(wm831x_backlight_init); | ||
240 | |||
241 | static void __exit wm831x_backlight_exit(void) | ||
242 | { | ||
243 | platform_driver_unregister(&wm831x_backlight_driver); | ||
244 | } | ||
245 | module_exit(wm831x_backlight_exit); | ||
246 | |||
247 | MODULE_DESCRIPTION("Backlight Driver for WM831x PMICs"); | ||
248 | MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com"); | ||
249 | MODULE_LICENSE("GPL"); | ||
250 | MODULE_ALIAS("platform:wm831x-backlight"); | ||