aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@suse.de>2009-12-03 19:19:47 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2009-12-11 15:23:22 -0500
commitd189164a2426a5169117cfe154186c2f999ada87 (patch)
treedd8d99ce029c55620cc26f47128881efee828214 /drivers/staging
parent5beef3c9bf7f967a4a70ddb0108fd3e459fed133 (diff)
Staging: add Samsung Laptop driver
This is a drive for the Samsung N128 laptop to control the wireless LED and backlight. Many thanks to Joey Lee for his help in testing and finding all of my bugs in the development of this driver, it has been invaluable. Cc: Joey Lee <jlee@novell.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/staging')
-rw-r--r--drivers/staging/Kconfig2
-rw-r--r--drivers/staging/Makefile1
-rw-r--r--drivers/staging/samsung-laptop/Kconfig10
-rw-r--r--drivers/staging/samsung-laptop/Makefile1
-rw-r--r--drivers/staging/samsung-laptop/samsung-laptop.c582
5 files changed, 596 insertions, 0 deletions
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index d30fa451e6f..a44ac2e3b8e 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -131,6 +131,8 @@ source "drivers/staging/wlags49_h25/Kconfig"
131 131
132source "drivers/staging/batman-adv/Kconfig" 132source "drivers/staging/batman-adv/Kconfig"
133 133
134source "drivers/staging/samsung-laptop/Kconfig"
135
134source "drivers/staging/strip/Kconfig" 136source "drivers/staging/strip/Kconfig"
135 137
136source "drivers/staging/arlan/Kconfig" 138source "drivers/staging/arlan/Kconfig"
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 910e55dd791..069864f4391 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -47,6 +47,7 @@ obj-$(CONFIG_RAMZSWAP) += ramzswap/
47obj-$(CONFIG_WLAGS49_H2) += wlags49_h2/ 47obj-$(CONFIG_WLAGS49_H2) += wlags49_h2/
48obj-$(CONFIG_WLAGS49_H25) += wlags49_h25/ 48obj-$(CONFIG_WLAGS49_H25) += wlags49_h25/
49obj-$(CONFIG_BATMAN_ADV) += batman-adv/ 49obj-$(CONFIG_BATMAN_ADV) += batman-adv/
50obj-$(CONFIG_SAMSUNG_LAPTOP) += samsung-laptop/
50obj-$(CONFIG_STRIP) += strip/ 51obj-$(CONFIG_STRIP) += strip/
51obj-$(CONFIG_ARLAN) += arlan/ 52obj-$(CONFIG_ARLAN) += arlan/
52obj-$(CONFIG_WAVELAN) += wavelan/ 53obj-$(CONFIG_WAVELAN) += wavelan/
diff --git a/drivers/staging/samsung-laptop/Kconfig b/drivers/staging/samsung-laptop/Kconfig
new file mode 100644
index 00000000000..f27c60864c2
--- /dev/null
+++ b/drivers/staging/samsung-laptop/Kconfig
@@ -0,0 +1,10 @@
1config SAMSUNG_LAPTOP
2 tristate "Samsung Laptop driver"
3 default n
4 depends on RFKILL && BACKLIGHT_CLASS_DEVICE && X86
5 help
6 This module implements a driver for the N128 Samsung Laptop
7 providing control over the Wireless LED and the LCD backlight
8
9 To compile this driver as a module, choose
10 M here: the module will be called samsung-laptop.
diff --git a/drivers/staging/samsung-laptop/Makefile b/drivers/staging/samsung-laptop/Makefile
new file mode 100644
index 00000000000..3c6f4204521
--- /dev/null
+++ b/drivers/staging/samsung-laptop/Makefile
@@ -0,0 +1 @@
obj-$(CONFIG_SAMSUNG_LAPTOP) += samsung-laptop.o
diff --git a/drivers/staging/samsung-laptop/samsung-laptop.c b/drivers/staging/samsung-laptop/samsung-laptop.c
new file mode 100644
index 00000000000..253b44754aa
--- /dev/null
+++ b/drivers/staging/samsung-laptop/samsung-laptop.c
@@ -0,0 +1,582 @@
1/*
2 * Samsung N130 Laptop driver
3 *
4 * Copyright (C) 2009 Greg Kroah-Hartman (gregkh@suse.de)
5 * Copyright (C) 2009 Novell Inc.
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published by
9 * the Free Software Foundation.
10 *
11 */
12#include <linux/version.h>
13#include <linux/kernel.h>
14#include <linux/init.h>
15#include <linux/module.h>
16#include <linux/delay.h>
17#include <linux/pci.h>
18#include <linux/backlight.h>
19#include <linux/fb.h>
20#include <linux/dmi.h>
21#include <linux/platform_device.h>
22#include <linux/rfkill.h>
23
24/*
25 * This driver is needed because a number of Samsung laptops do not hook
26 * their control settings through ACPI. So we have to poke around in the
27 * BIOS to do things like brightness values, and "special" key controls.
28 */
29
30/*
31 * We have 0 - 8 as valid brightness levels. The specs say that level 0 should
32 * be reserved by the BIOS (which really doesn't make much sense), we tell
33 * userspace that the value is 0 - 7 and then just tell the hardware 1 - 8
34 */
35#define MAX_BRIGHT 0x07
36
37/* Brightness is 0 - 8, as described above. Value 0 is for the BIOS to use */
38#define GET_BRIGHTNESS 0x00
39#define SET_BRIGHTNESS 0x01
40
41/* first byte:
42 * 0x00 - wireless is off
43 * 0x01 - wireless is on
44 * second byte:
45 * 0x02 - 3G is off
46 * 0x03 - 3G is on
47 * TODO, verify 3G is correct, that doesn't seem right...
48 */
49#define GET_WIRELESS_BUTTON 0x02
50#define SET_WIRELESS_BUTTON 0x03
51
52/* 0 is off, 1 is on */
53#define GET_BACKLIGHT 0x04
54#define SET_BACKLIGHT 0x05
55
56/*
57 * 0x80 or 0x00 - no action
58 * 0x81 - recovery key pressed
59 */
60#define GET_RECOVERY_METHOD 0x06
61#define SET_RECOVERY_METHOD 0x07
62
63/* 0 is low, 1 is high */
64#define GET_PERFORMANCE_LEVEL 0x08
65#define SET_PERFORMANCE_LEVEL 0x09
66
67/*
68 * Tell the BIOS that Linux is running on this machine.
69 * 81 is on, 80 is off
70 */
71#define SET_LINUX 0x0a
72
73
74#define MAIN_FUNCTION 0x4c49
75
76#define SABI_HEADER_PORT 0x00
77#define SABI_HEADER_RE_MEM 0x02
78#define SABI_HEADER_IFACEFUNC 0x03
79#define SABI_HEADER_EN_MEM 0x04
80#define SABI_HEADER_DATA_OFFSET 0x05
81#define SABI_HEADER_DATA_SEGMENT 0x07
82
83#define SABI_IFACE_MAIN 0x00
84#define SABI_IFACE_SUB 0x02
85#define SABI_IFACE_COMPLETE 0x04
86#define SABI_IFACE_DATA 0x05
87
88/* Structure to get data back to the calling function */
89struct sabi_retval {
90 u8 retval[20];
91};
92
93static void __iomem *sabi;
94static void __iomem *sabi_iface;
95static void __iomem *f0000_segment;
96static struct backlight_device *backlight_device;
97static struct mutex sabi_mutex;
98static struct platform_device *sdev;
99static struct rfkill *rfk;
100
101static int force;
102module_param(force, bool, 0);
103MODULE_PARM_DESC(force, "Disable the DMI check and forces the driver to be loaded");
104
105static int debug;
106module_param(debug, bool, S_IRUGO | S_IWUSR);
107MODULE_PARM_DESC(debug, "Debug enabled or not");
108
109static int sabi_get_command(u8 command, struct sabi_retval *sretval)
110{
111 int retval = 0;
112 u16 port = readw(sabi + SABI_HEADER_PORT);
113
114 mutex_lock(&sabi_mutex);
115
116 /* enable memory to be able to write to it */
117 outb(readb(sabi + SABI_HEADER_EN_MEM), port);
118
119 /* write out the command */
120 writew(MAIN_FUNCTION, sabi_iface + SABI_IFACE_MAIN);
121 writew(command, sabi_iface + SABI_IFACE_SUB);
122 writeb(0, sabi_iface + SABI_IFACE_COMPLETE);
123 outb(readb(sabi + SABI_HEADER_IFACEFUNC), port);
124
125 /* write protect memory to make it safe */
126 outb(readb(sabi + SABI_HEADER_RE_MEM), port);
127
128 /* see if the command actually succeeded */
129 if (readb(sabi_iface + SABI_IFACE_COMPLETE) == 0xaa &&
130 readb(sabi_iface + SABI_IFACE_DATA) != 0xff) {
131 /*
132 * It did!
133 * Save off the data into a structure so the caller use it.
134 * Right now we only care about the first 4 bytes,
135 * I suppose there are commands that need more, but I don't
136 * know about them.
137 */
138 sretval->retval[0] = readb(sabi_iface + SABI_IFACE_DATA);
139 sretval->retval[1] = readb(sabi_iface + SABI_IFACE_DATA + 1);
140 sretval->retval[2] = readb(sabi_iface + SABI_IFACE_DATA + 2);
141 sretval->retval[3] = readb(sabi_iface + SABI_IFACE_DATA + 3);
142 goto exit;
143 }
144
145 /* Something bad happened, so report it and error out */
146 printk(KERN_WARNING "SABI command 0x%02x failed with completion flag 0x%02x and output 0x%02x\n",
147 command, readb(sabi_iface + SABI_IFACE_COMPLETE),
148 readb(sabi_iface + SABI_IFACE_DATA));
149 retval = -EINVAL;
150exit:
151 mutex_unlock(&sabi_mutex);
152 return retval;
153
154}
155
156static int sabi_set_command(u8 command, u8 data)
157{
158 int retval = 0;
159 u16 port = readw(sabi + SABI_HEADER_PORT);
160
161 mutex_lock(&sabi_mutex);
162
163 /* enable memory to be able to write to it */
164 outb(readb(sabi + SABI_HEADER_EN_MEM), port);
165
166 /* write out the command */
167 writew(MAIN_FUNCTION, sabi_iface + SABI_IFACE_MAIN);
168 writew(command, sabi_iface + SABI_IFACE_SUB);
169 writeb(0, sabi_iface + SABI_IFACE_COMPLETE);
170 writeb(data, sabi_iface + SABI_IFACE_DATA);
171 outb(readb(sabi + SABI_HEADER_IFACEFUNC), port);
172
173 /* write protect memory to make it safe */
174 outb(readb(sabi + SABI_HEADER_RE_MEM), port);
175
176 /* see if the command actually succeeded */
177 if (readb(sabi_iface + SABI_IFACE_COMPLETE) == 0xaa &&
178 readb(sabi_iface + SABI_IFACE_DATA) != 0xff) {
179 /* it did! */
180 goto exit;
181 }
182
183 /* Something bad happened, so report it and error out */
184 printk(KERN_WARNING "SABI command 0x%02x failed with completion flag 0x%02x and output 0x%02x\n",
185 command, readb(sabi_iface + SABI_IFACE_COMPLETE),
186 readb(sabi_iface + SABI_IFACE_DATA));
187 retval = -EINVAL;
188exit:
189 mutex_unlock(&sabi_mutex);
190 return retval;
191}
192
193static void test_backlight(void)
194{
195 struct sabi_retval sretval;
196
197 sabi_get_command(GET_BACKLIGHT, &sretval);
198 printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]);
199
200 sabi_set_command(SET_BACKLIGHT, 0);
201 printk(KERN_DEBUG "backlight should be off\n");
202
203 sabi_get_command(GET_BACKLIGHT, &sretval);
204 printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]);
205
206 msleep(1000);
207
208 sabi_set_command(SET_BACKLIGHT, 1);
209 printk(KERN_DEBUG "backlight should be on\n");
210
211 sabi_get_command(GET_BACKLIGHT, &sretval);
212 printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]);
213}
214
215static void test_wireless(void)
216{
217 struct sabi_retval sretval;
218
219 sabi_get_command(GET_WIRELESS_BUTTON, &sretval);
220 printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]);
221
222 sabi_set_command(SET_WIRELESS_BUTTON, 0);
223 printk(KERN_DEBUG "wireless led should be off\n");
224
225 sabi_get_command(GET_WIRELESS_BUTTON, &sretval);
226 printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]);
227
228 msleep(1000);
229
230 sabi_set_command(SET_WIRELESS_BUTTON, 1);
231 printk(KERN_DEBUG "wireless led should be on\n");
232
233 sabi_get_command(GET_WIRELESS_BUTTON, &sretval);
234 printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]);
235}
236
237static u8 read_brightness(void)
238{
239 struct sabi_retval sretval;
240 int user_brightness = 0;
241 int retval;
242
243 retval = sabi_get_command(GET_BRIGHTNESS, &sretval);
244 if (!retval)
245 user_brightness = sretval.retval[0];
246 if (user_brightness != 0)
247 --user_brightness;
248 return user_brightness;
249}
250
251static void set_brightness(u8 user_brightness)
252{
253 sabi_set_command(SET_BRIGHTNESS, user_brightness + 1);
254}
255
256static int get_brightness(struct backlight_device *bd)
257{
258 return (int)read_brightness();
259}
260
261static int update_status(struct backlight_device *bd)
262{
263 set_brightness(bd->props.brightness);
264
265 if (bd->props.power == FB_BLANK_UNBLANK)
266 sabi_set_command(SET_BACKLIGHT, 1);
267 else
268 sabi_set_command(SET_BACKLIGHT, 0);
269 return 0;
270}
271
272static struct backlight_ops backlight_ops = {
273 .get_brightness = get_brightness,
274 .update_status = update_status,
275};
276
277#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31)
278static int rfkill_set(void *data, bool blocked)
279{
280 /* Do something with blocked...*/
281 /*
282 * blocked == false is on
283 * blocked == true is off
284 */
285 if (blocked)
286 sabi_set_command(SET_WIRELESS_BUTTON, 0);
287 else
288 sabi_set_command(SET_WIRELESS_BUTTON, 1);
289
290 return 0;
291}
292
293static struct rfkill_ops rfkill_ops = {
294 .set_block = rfkill_set,
295};
296
297static int init_wireless(struct platform_device *sdev)
298{
299 int retval;
300
301 rfk = rfkill_alloc("samsung-wifi", &sdev->dev, RFKILL_TYPE_WLAN,
302 &rfkill_ops, NULL);
303 if (!rfk)
304 return -ENOMEM;
305
306 retval = rfkill_register(rfk);
307 if (retval) {
308 rfkill_destroy(rfk);
309 return -ENODEV;
310 }
311
312 return 0;
313}
314
315static void destroy_wireless(void)
316{
317 rfkill_unregister(rfk);
318 rfkill_destroy(rfk);
319}
320
321#else
322
323static int rfkill_set(void *data, enum rfkill_state state)
324{
325 if (state == RFKILL_STATE_UNBLOCKED)
326 sabi_set_command(SET_WIRELESS_BUTTON, 1);
327 else
328 sabi_set_command(SET_WIRELESS_BUTTON, 0);
329
330 return 0;
331}
332
333static int init_wireless(struct platform_device *sdev)
334{
335 int retval;
336
337 rfk = rfkill_allocate(&sdev->dev, RFKILL_TYPE_WLAN);
338 if (!rfk)
339 return -ENOMEM;
340 rfk->toggle_radio = rfkill_set;
341 rfk->name = "samsung-wifi";
342
343 retval = rfkill_register(rfk);
344 if (retval) {
345 rfkill_free(rfk);
346 return -ENODEV;
347 }
348
349 return 0;
350}
351
352static void destroy_wireless(void)
353{
354 rfkill_unregister(rfk);
355}
356
357#endif
358
359static ssize_t get_silent_state(struct device *dev,
360 struct device_attribute *attr, char *buf)
361{
362 struct sabi_retval sretval;
363 int retval;
364
365 /* Read the state */
366 retval = sabi_get_command(GET_PERFORMANCE_LEVEL, &sretval);
367 if (retval)
368 return retval;
369
370 /* The logic is backwards, yeah, lots of fun... */
371 if (sretval.retval[0] == 0)
372 retval = 1;
373 else
374 retval = 0;
375 return sprintf(buf, "%d\n", retval);
376}
377
378static ssize_t set_silent_state(struct device *dev,
379 struct device_attribute *attr, const char *buf,
380 size_t count)
381{
382 char value;
383
384 if (count >= 1) {
385 value = buf[0];
386 if ((value == '0') || (value == 'n') || (value == 'N')) {
387 /* Turn speed up */
388 sabi_set_command(SET_PERFORMANCE_LEVEL, 0x01);
389 } else if ((value == '1') || (value == 'y') || (value == 'Y')) {
390 /* Turn speed down */
391 sabi_set_command(SET_PERFORMANCE_LEVEL, 0x00);
392 } else {
393 return -EINVAL;
394 }
395 }
396 return count;
397}
398static DEVICE_ATTR(silent, S_IWUGO | S_IRUGO,
399 get_silent_state, set_silent_state);
400
401
402static int __init dmi_check_cb(const struct dmi_system_id *id)
403{
404 printk(KERN_INFO KBUILD_MODNAME ": found laptop model '%s'\n",
405 id->ident);
406 return 0;
407}
408
409static struct dmi_system_id __initdata samsung_dmi_table[] = {
410 {
411 .ident = "N128",
412 .matches = {
413 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
414 DMI_MATCH(DMI_PRODUCT_NAME, "N128"),
415 DMI_MATCH(DMI_BOARD_NAME, "N128"),
416 },
417 .callback = dmi_check_cb,
418 },
419 {
420 .ident = "N130",
421 .matches = {
422 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
423 DMI_MATCH(DMI_PRODUCT_NAME, "N130"),
424 DMI_MATCH(DMI_BOARD_NAME, "N130"),
425 },
426 .callback = dmi_check_cb,
427 },
428 { },
429};
430MODULE_DEVICE_TABLE(dmi, samsung_dmi_table);
431
432static int __init samsung_init(void)
433{
434 struct sabi_retval sretval;
435 const char *testStr = "SECLINUX";
436 void __iomem *memcheck;
437 unsigned int ifaceP;
438 int pStr;
439 int loca;
440 int retval;
441
442 mutex_init(&sabi_mutex);
443
444 if (!force && !dmi_check_system(samsung_dmi_table))
445 return -ENODEV;
446
447 f0000_segment = ioremap(0xf0000, 0xffff);
448 if (!f0000_segment) {
449 printk(KERN_ERR "Can't map the segment at 0xf0000\n");
450 return -EINVAL;
451 }
452
453 /* Try to find the signature "SECLINUX" in memory to find the header */
454 pStr = 0;
455 memcheck = f0000_segment;
456 for (loca = 0; loca < 0xffff; loca++) {
457 char temp = readb(memcheck + loca);
458
459 if (temp == testStr[pStr]) {
460 if (pStr == strlen(testStr)-1)
461 break;
462 ++pStr;
463 } else {
464 pStr = 0;
465 }
466 }
467 if (loca == 0xffff) {
468 printk(KERN_ERR "This computer does not support SABI\n");
469 goto error_no_signature;
470 }
471
472 /* point to the SMI port Number */
473 loca += 1;
474 sabi = (memcheck + loca);
475
476 if (debug) {
477 printk(KERN_DEBUG "This computer supports SABI==%x\n",
478 loca + 0xf0000 - 6);
479 printk(KERN_DEBUG "SABI header:\n");
480 printk(KERN_DEBUG " SMI Port Number = 0x%04x\n",
481 readw(sabi + SABI_HEADER_PORT));
482 printk(KERN_DEBUG " SMI Interface Function = 0x%02x\n",
483 readb(sabi + SABI_HEADER_IFACEFUNC));
484 printk(KERN_DEBUG " SMI enable memory buffer = 0x%02x\n",
485 readb(sabi + SABI_HEADER_EN_MEM));
486 printk(KERN_DEBUG " SMI restore memory buffer = 0x%02x\n",
487 readb(sabi + SABI_HEADER_RE_MEM));
488 printk(KERN_DEBUG " SABI data offset = 0x%04x\n",
489 readw(sabi + SABI_HEADER_DATA_OFFSET));
490 printk(KERN_DEBUG " SABI data segment = 0x%04x\n",
491 readw(sabi + SABI_HEADER_DATA_SEGMENT));
492 }
493
494 /* Get a pointer to the SABI Interface */
495 ifaceP = (readw(sabi + SABI_HEADER_DATA_SEGMENT) & 0x0ffff) << 4;
496 ifaceP += readw(sabi + SABI_HEADER_DATA_OFFSET) & 0x0ffff;
497 sabi_iface = ioremap(ifaceP, 16);
498 if (!sabi_iface) {
499 printk(KERN_ERR "Can't remap %x\n", ifaceP);
500 goto exit;
501 }
502 if (debug) {
503 printk(KERN_DEBUG "ifaceP = 0x%08x\n", ifaceP);
504 printk(KERN_DEBUG "sabi_iface = %p\n", sabi_iface);
505
506 test_backlight();
507 test_wireless();
508
509 retval = sabi_get_command(GET_BRIGHTNESS, &sretval);
510 printk(KERN_DEBUG "brightness = 0x%02x\n", sretval.retval[0]);
511 }
512
513 /* Turn on "Linux" mode in the BIOS */
514 retval = sabi_set_command(SET_LINUX, 0x81);
515 if (retval) {
516 printk(KERN_ERR KBUILD_MODNAME ": Linux mode was not set!\n");
517 goto error_no_platform;
518 }
519
520 /* knock up a platform device to hang stuff off of */
521 sdev = platform_device_register_simple("samsung", -1, NULL, 0);
522 if (IS_ERR(sdev))
523 goto error_no_platform;
524
525 /* create a backlight device to talk to this one */
526 backlight_device = backlight_device_register("samsung", &sdev->dev,
527 NULL, &backlight_ops);
528 if (IS_ERR(backlight_device))
529 goto error_no_backlight;
530
531 backlight_device->props.max_brightness = MAX_BRIGHT;
532 backlight_device->props.brightness = read_brightness();
533 backlight_device->props.power = FB_BLANK_UNBLANK;
534 backlight_update_status(backlight_device);
535
536 retval = init_wireless(sdev);
537 if (retval)
538 goto error_no_rfk;
539
540 retval = device_create_file(&sdev->dev, &dev_attr_silent);
541 if (retval)
542 goto error_file_create;
543
544exit:
545 return 0;
546
547error_file_create:
548 destroy_wireless();
549
550error_no_rfk:
551 backlight_device_unregister(backlight_device);
552
553error_no_backlight:
554 platform_device_unregister(sdev);
555
556error_no_platform:
557 iounmap(sabi_iface);
558
559error_no_signature:
560 iounmap(f0000_segment);
561 return -EINVAL;
562}
563
564static void __exit samsung_exit(void)
565{
566 /* Turn off "Linux" mode in the BIOS */
567 sabi_set_command(SET_LINUX, 0x80);
568
569 device_remove_file(&sdev->dev, &dev_attr_silent);
570 backlight_device_unregister(backlight_device);
571 destroy_wireless();
572 iounmap(sabi_iface);
573 iounmap(f0000_segment);
574 platform_device_unregister(sdev);
575}
576
577module_init(samsung_init);
578module_exit(samsung_exit);
579
580MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@suse.de>");
581MODULE_DESCRIPTION("Samsung Backlight driver");
582MODULE_LICENSE("GPL");