Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless
[cascardo/linux.git] / drivers / platform / x86 / samsung-laptop.c
1 /*
2  * Samsung Laptop driver
3  *
4  * Copyright (C) 2009,2011 Greg Kroah-Hartman (gregkh@suse.de)
5  * Copyright (C) 2009,2011 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 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
13
14 #include <linux/kernel.h>
15 #include <linux/init.h>
16 #include <linux/module.h>
17 #include <linux/delay.h>
18 #include <linux/pci.h>
19 #include <linux/backlight.h>
20 #include <linux/leds.h>
21 #include <linux/fb.h>
22 #include <linux/dmi.h>
23 #include <linux/platform_device.h>
24 #include <linux/rfkill.h>
25 #include <linux/acpi.h>
26 #include <linux/seq_file.h>
27 #include <linux/debugfs.h>
28 #include <linux/ctype.h>
29 #include <linux/efi.h>
30 #include <linux/suspend.h>
31 #include <acpi/video.h>
32
33 /*
34  * This driver is needed because a number of Samsung laptops do not hook
35  * their control settings through ACPI.  So we have to poke around in the
36  * BIOS to do things like brightness values, and "special" key controls.
37  */
38
39 /*
40  * We have 0 - 8 as valid brightness levels.  The specs say that level 0 should
41  * be reserved by the BIOS (which really doesn't make much sense), we tell
42  * userspace that the value is 0 - 7 and then just tell the hardware 1 - 8
43  */
44 #define MAX_BRIGHT      0x07
45
46
47 #define SABI_IFACE_MAIN                 0x00
48 #define SABI_IFACE_SUB                  0x02
49 #define SABI_IFACE_COMPLETE             0x04
50 #define SABI_IFACE_DATA                 0x05
51
52 #define WL_STATUS_WLAN                  0x0
53 #define WL_STATUS_BT                    0x2
54
55 /* Structure get/set data using sabi */
56 struct sabi_data {
57         union {
58                 struct {
59                         u32 d0;
60                         u32 d1;
61                         u16 d2;
62                         u8  d3;
63                 };
64                 u8 data[11];
65         };
66 };
67
68 struct sabi_header_offsets {
69         u8 port;
70         u8 re_mem;
71         u8 iface_func;
72         u8 en_mem;
73         u8 data_offset;
74         u8 data_segment;
75 };
76
77 struct sabi_commands {
78         /*
79          * Brightness is 0 - 8, as described above.
80          * Value 0 is for the BIOS to use
81          */
82         u16 get_brightness;
83         u16 set_brightness;
84
85         /*
86          * first byte:
87          * 0x00 - wireless is off
88          * 0x01 - wireless is on
89          * second byte:
90          * 0x02 - 3G is off
91          * 0x03 - 3G is on
92          * TODO, verify 3G is correct, that doesn't seem right...
93          */
94         u16 get_wireless_button;
95         u16 set_wireless_button;
96
97         /* 0 is off, 1 is on */
98         u16 get_backlight;
99         u16 set_backlight;
100
101         /*
102          * 0x80 or 0x00 - no action
103          * 0x81 - recovery key pressed
104          */
105         u16 get_recovery_mode;
106         u16 set_recovery_mode;
107
108         /*
109          * on seclinux: 0 is low, 1 is high,
110          * on swsmi: 0 is normal, 1 is silent, 2 is turbo
111          */
112         u16 get_performance_level;
113         u16 set_performance_level;
114
115         /* 0x80 is off, 0x81 is on */
116         u16 get_battery_life_extender;
117         u16 set_battery_life_extender;
118
119         /* 0x80 is off, 0x81 is on */
120         u16 get_usb_charge;
121         u16 set_usb_charge;
122
123         /* the first byte is for bluetooth and the third one is for wlan */
124         u16 get_wireless_status;
125         u16 set_wireless_status;
126
127         /* 0x81 to read, (0x82 | level << 8) to set, 0xaabb to enable */
128         u16 kbd_backlight;
129
130         /*
131          * Tell the BIOS that Linux is running on this machine.
132          * 81 is on, 80 is off
133          */
134         u16 set_linux;
135 };
136
137 struct sabi_performance_level {
138         const char *name;
139         u16 value;
140 };
141
142 struct sabi_config {
143         int sabi_version;
144         const char *test_string;
145         u16 main_function;
146         const struct sabi_header_offsets header_offsets;
147         const struct sabi_commands commands;
148         const struct sabi_performance_level performance_levels[4];
149         u8 min_brightness;
150         u8 max_brightness;
151 };
152
153 static const struct sabi_config sabi_configs[] = {
154         {
155                 /* I don't know if it is really 2, but it it is
156                  * less than 3 anyway */
157                 .sabi_version = 2,
158
159                 .test_string = "SECLINUX",
160
161                 .main_function = 0x4c49,
162
163                 .header_offsets = {
164                         .port = 0x00,
165                         .re_mem = 0x02,
166                         .iface_func = 0x03,
167                         .en_mem = 0x04,
168                         .data_offset = 0x05,
169                         .data_segment = 0x07,
170                 },
171
172                 .commands = {
173                         .get_brightness = 0x00,
174                         .set_brightness = 0x01,
175
176                         .get_wireless_button = 0x02,
177                         .set_wireless_button = 0x03,
178
179                         .get_backlight = 0x04,
180                         .set_backlight = 0x05,
181
182                         .get_recovery_mode = 0x06,
183                         .set_recovery_mode = 0x07,
184
185                         .get_performance_level = 0x08,
186                         .set_performance_level = 0x09,
187
188                         .get_battery_life_extender = 0xFFFF,
189                         .set_battery_life_extender = 0xFFFF,
190
191                         .get_usb_charge = 0xFFFF,
192                         .set_usb_charge = 0xFFFF,
193
194                         .get_wireless_status = 0xFFFF,
195                         .set_wireless_status = 0xFFFF,
196
197                         .kbd_backlight = 0xFFFF,
198
199                         .set_linux = 0x0a,
200                 },
201
202                 .performance_levels = {
203                         {
204                                 .name = "silent",
205                                 .value = 0,
206                         },
207                         {
208                                 .name = "normal",
209                                 .value = 1,
210                         },
211                         { },
212                 },
213                 .min_brightness = 1,
214                 .max_brightness = 8,
215         },
216         {
217                 .sabi_version = 3,
218
219                 .test_string = "SwSmi@",
220
221                 .main_function = 0x5843,
222
223                 .header_offsets = {
224                         .port = 0x00,
225                         .re_mem = 0x04,
226                         .iface_func = 0x02,
227                         .en_mem = 0x03,
228                         .data_offset = 0x05,
229                         .data_segment = 0x07,
230                 },
231
232                 .commands = {
233                         .get_brightness = 0x10,
234                         .set_brightness = 0x11,
235
236                         .get_wireless_button = 0x12,
237                         .set_wireless_button = 0x13,
238
239                         .get_backlight = 0x2d,
240                         .set_backlight = 0x2e,
241
242                         .get_recovery_mode = 0xff,
243                         .set_recovery_mode = 0xff,
244
245                         .get_performance_level = 0x31,
246                         .set_performance_level = 0x32,
247
248                         .get_battery_life_extender = 0x65,
249                         .set_battery_life_extender = 0x66,
250
251                         .get_usb_charge = 0x67,
252                         .set_usb_charge = 0x68,
253
254                         .get_wireless_status = 0x69,
255                         .set_wireless_status = 0x6a,
256
257                         .kbd_backlight = 0x78,
258
259                         .set_linux = 0xff,
260                 },
261
262                 .performance_levels = {
263                         {
264                                 .name = "normal",
265                                 .value = 0,
266                         },
267                         {
268                                 .name = "silent",
269                                 .value = 1,
270                         },
271                         {
272                                 .name = "overclock",
273                                 .value = 2,
274                         },
275                         { },
276                 },
277                 .min_brightness = 0,
278                 .max_brightness = 8,
279         },
280         { },
281 };
282
283 /*
284  * samsung-laptop/    - debugfs root directory
285  *   f0000_segment    - dump f0000 segment
286  *   command          - current command
287  *   data             - current data
288  *   d0, d1, d2, d3   - data fields
289  *   call             - call SABI using command and data
290  *
291  * This allow to call arbitrary sabi commands wihout
292  * modifying the driver at all.
293  * For example, setting the keyboard backlight brightness to 5
294  *
295  *  echo 0x78 > command
296  *  echo 0x0582 > d0
297  *  echo 0 > d1
298  *  echo 0 > d2
299  *  echo 0 > d3
300  *  cat call
301  */
302
303 struct samsung_laptop_debug {
304         struct dentry *root;
305         struct sabi_data data;
306         u16 command;
307
308         struct debugfs_blob_wrapper f0000_wrapper;
309         struct debugfs_blob_wrapper data_wrapper;
310         struct debugfs_blob_wrapper sdiag_wrapper;
311 };
312
313 struct samsung_laptop;
314
315 struct samsung_rfkill {
316         struct samsung_laptop *samsung;
317         struct rfkill *rfkill;
318         enum rfkill_type type;
319 };
320
321 struct samsung_laptop {
322         const struct sabi_config *config;
323
324         void __iomem *sabi;
325         void __iomem *sabi_iface;
326         void __iomem *f0000_segment;
327
328         struct mutex sabi_mutex;
329
330         struct platform_device *platform_device;
331         struct backlight_device *backlight_device;
332
333         struct samsung_rfkill wlan;
334         struct samsung_rfkill bluetooth;
335
336         struct led_classdev kbd_led;
337         int kbd_led_wk;
338         struct workqueue_struct *led_workqueue;
339         struct work_struct kbd_led_work;
340
341         struct samsung_laptop_debug debug;
342         struct samsung_quirks *quirks;
343
344         struct notifier_block pm_nb;
345
346         bool handle_backlight;
347         bool has_stepping_quirk;
348
349         char sdiag[64];
350 };
351
352 struct samsung_quirks {
353         bool broken_acpi_video;
354         bool four_kbd_backlight_levels;
355         bool enable_kbd_backlight;
356 };
357
358 static struct samsung_quirks samsung_unknown = {};
359
360 static struct samsung_quirks samsung_broken_acpi_video = {
361         .broken_acpi_video = true,
362 };
363
364 static struct samsung_quirks samsung_np740u3e = {
365         .four_kbd_backlight_levels = true,
366         .enable_kbd_backlight = true,
367 };
368
369 static bool force;
370 module_param(force, bool, 0);
371 MODULE_PARM_DESC(force,
372                 "Disable the DMI check and forces the driver to be loaded");
373
374 static bool debug;
375 module_param(debug, bool, S_IRUGO | S_IWUSR);
376 MODULE_PARM_DESC(debug, "Debug enabled or not");
377
378 static int sabi_command(struct samsung_laptop *samsung, u16 command,
379                         struct sabi_data *in,
380                         struct sabi_data *out)
381 {
382         const struct sabi_config *config = samsung->config;
383         int ret = 0;
384         u16 port = readw(samsung->sabi + config->header_offsets.port);
385         u8 complete, iface_data;
386
387         mutex_lock(&samsung->sabi_mutex);
388
389         if (debug) {
390                 if (in)
391                         pr_info("SABI command:0x%04x "
392                                 "data:{0x%08x, 0x%08x, 0x%04x, 0x%02x}",
393                                 command, in->d0, in->d1, in->d2, in->d3);
394                 else
395                         pr_info("SABI command:0x%04x", command);
396         }
397
398         /* enable memory to be able to write to it */
399         outb(readb(samsung->sabi + config->header_offsets.en_mem), port);
400
401         /* write out the command */
402         writew(config->main_function, samsung->sabi_iface + SABI_IFACE_MAIN);
403         writew(command, samsung->sabi_iface + SABI_IFACE_SUB);
404         writeb(0, samsung->sabi_iface + SABI_IFACE_COMPLETE);
405         if (in) {
406                 writel(in->d0, samsung->sabi_iface + SABI_IFACE_DATA);
407                 writel(in->d1, samsung->sabi_iface + SABI_IFACE_DATA + 4);
408                 writew(in->d2, samsung->sabi_iface + SABI_IFACE_DATA + 8);
409                 writeb(in->d3, samsung->sabi_iface + SABI_IFACE_DATA + 10);
410         }
411         outb(readb(samsung->sabi + config->header_offsets.iface_func), port);
412
413         /* write protect memory to make it safe */
414         outb(readb(samsung->sabi + config->header_offsets.re_mem), port);
415
416         /* see if the command actually succeeded */
417         complete = readb(samsung->sabi_iface + SABI_IFACE_COMPLETE);
418         iface_data = readb(samsung->sabi_iface + SABI_IFACE_DATA);
419
420         /* iface_data = 0xFF happens when a command is not known
421          * so we only add a warning in debug mode since we will
422          * probably issue some unknown command at startup to find
423          * out which features are supported */
424         if (complete != 0xaa || (iface_data == 0xff && debug))
425                 pr_warn("SABI command 0x%04x failed with"
426                         " completion flag 0x%02x and interface data 0x%02x",
427                         command, complete, iface_data);
428
429         if (complete != 0xaa || iface_data == 0xff) {
430                 ret = -EINVAL;
431                 goto exit;
432         }
433
434         if (out) {
435                 out->d0 = readl(samsung->sabi_iface + SABI_IFACE_DATA);
436                 out->d1 = readl(samsung->sabi_iface + SABI_IFACE_DATA + 4);
437                 out->d2 = readw(samsung->sabi_iface + SABI_IFACE_DATA + 2);
438                 out->d3 = readb(samsung->sabi_iface + SABI_IFACE_DATA + 1);
439         }
440
441         if (debug && out) {
442                 pr_info("SABI return data:{0x%08x, 0x%08x, 0x%04x, 0x%02x}",
443                         out->d0, out->d1, out->d2, out->d3);
444         }
445
446 exit:
447         mutex_unlock(&samsung->sabi_mutex);
448         return ret;
449 }
450
451 /* simple wrappers usable with most commands */
452 static int sabi_set_commandb(struct samsung_laptop *samsung,
453                              u16 command, u8 data)
454 {
455         struct sabi_data in = { { { .d0 = 0, .d1 = 0, .d2 = 0, .d3 = 0 } } };
456
457         in.data[0] = data;
458         return sabi_command(samsung, command, &in, NULL);
459 }
460
461 static int read_brightness(struct samsung_laptop *samsung)
462 {
463         const struct sabi_config *config = samsung->config;
464         const struct sabi_commands *commands = &samsung->config->commands;
465         struct sabi_data sretval;
466         int user_brightness = 0;
467         int retval;
468
469         retval = sabi_command(samsung, commands->get_brightness,
470                               NULL, &sretval);
471         if (retval)
472                 return retval;
473
474         user_brightness = sretval.data[0];
475         if (user_brightness > config->min_brightness)
476                 user_brightness -= config->min_brightness;
477         else
478                 user_brightness = 0;
479
480         return user_brightness;
481 }
482
483 static void set_brightness(struct samsung_laptop *samsung, u8 user_brightness)
484 {
485         const struct sabi_config *config = samsung->config;
486         const struct sabi_commands *commands = &samsung->config->commands;
487         u8 user_level = user_brightness + config->min_brightness;
488
489         if (samsung->has_stepping_quirk && user_level != 0) {
490                 /*
491                  * short circuit if the specified level is what's already set
492                  * to prevent the screen from flickering needlessly
493                  */
494                 if (user_brightness == read_brightness(samsung))
495                         return;
496
497                 sabi_set_commandb(samsung, commands->set_brightness, 0);
498         }
499
500         sabi_set_commandb(samsung, commands->set_brightness, user_level);
501 }
502
503 static int get_brightness(struct backlight_device *bd)
504 {
505         struct samsung_laptop *samsung = bl_get_data(bd);
506
507         return read_brightness(samsung);
508 }
509
510 static void check_for_stepping_quirk(struct samsung_laptop *samsung)
511 {
512         int initial_level;
513         int check_level;
514         int orig_level = read_brightness(samsung);
515
516         /*
517          * Some laptops exhibit the strange behaviour of stepping toward
518          * (rather than setting) the brightness except when changing to/from
519          * brightness level 0. This behaviour is checked for here and worked
520          * around in set_brightness.
521          */
522
523         if (orig_level == 0)
524                 set_brightness(samsung, 1);
525
526         initial_level = read_brightness(samsung);
527
528         if (initial_level <= 2)
529                 check_level = initial_level + 2;
530         else
531                 check_level = initial_level - 2;
532
533         samsung->has_stepping_quirk = false;
534         set_brightness(samsung, check_level);
535
536         if (read_brightness(samsung) != check_level) {
537                 samsung->has_stepping_quirk = true;
538                 pr_info("enabled workaround for brightness stepping quirk\n");
539         }
540
541         set_brightness(samsung, orig_level);
542 }
543
544 static int update_status(struct backlight_device *bd)
545 {
546         struct samsung_laptop *samsung = bl_get_data(bd);
547         const struct sabi_commands *commands = &samsung->config->commands;
548
549         set_brightness(samsung, bd->props.brightness);
550
551         if (bd->props.power == FB_BLANK_UNBLANK)
552                 sabi_set_commandb(samsung, commands->set_backlight, 1);
553         else
554                 sabi_set_commandb(samsung, commands->set_backlight, 0);
555
556         return 0;
557 }
558
559 static const struct backlight_ops backlight_ops = {
560         .get_brightness = get_brightness,
561         .update_status  = update_status,
562 };
563
564 static int seclinux_rfkill_set(void *data, bool blocked)
565 {
566         struct samsung_rfkill *srfkill = data;
567         struct samsung_laptop *samsung = srfkill->samsung;
568         const struct sabi_commands *commands = &samsung->config->commands;
569
570         return sabi_set_commandb(samsung, commands->set_wireless_button,
571                                  !blocked);
572 }
573
574 static struct rfkill_ops seclinux_rfkill_ops = {
575         .set_block = seclinux_rfkill_set,
576 };
577
578 static int swsmi_wireless_status(struct samsung_laptop *samsung,
579                                  struct sabi_data *data)
580 {
581         const struct sabi_commands *commands = &samsung->config->commands;
582
583         return sabi_command(samsung, commands->get_wireless_status,
584                             NULL, data);
585 }
586
587 static int swsmi_rfkill_set(void *priv, bool blocked)
588 {
589         struct samsung_rfkill *srfkill = priv;
590         struct samsung_laptop *samsung = srfkill->samsung;
591         const struct sabi_commands *commands = &samsung->config->commands;
592         struct sabi_data data;
593         int ret, i;
594
595         ret = swsmi_wireless_status(samsung, &data);
596         if (ret)
597                 return ret;
598
599         /* Don't set the state for non-present devices */
600         for (i = 0; i < 4; i++)
601                 if (data.data[i] == 0x02)
602                         data.data[1] = 0;
603
604         if (srfkill->type == RFKILL_TYPE_WLAN)
605                 data.data[WL_STATUS_WLAN] = !blocked;
606         else if (srfkill->type == RFKILL_TYPE_BLUETOOTH)
607                 data.data[WL_STATUS_BT] = !blocked;
608
609         return sabi_command(samsung, commands->set_wireless_status,
610                             &data, &data);
611 }
612
613 static void swsmi_rfkill_query(struct rfkill *rfkill, void *priv)
614 {
615         struct samsung_rfkill *srfkill = priv;
616         struct samsung_laptop *samsung = srfkill->samsung;
617         struct sabi_data data;
618         int ret;
619
620         ret = swsmi_wireless_status(samsung, &data);
621         if (ret)
622                 return ;
623
624         if (srfkill->type == RFKILL_TYPE_WLAN)
625                 ret = data.data[WL_STATUS_WLAN];
626         else if (srfkill->type == RFKILL_TYPE_BLUETOOTH)
627                 ret = data.data[WL_STATUS_BT];
628         else
629                 return ;
630
631         rfkill_set_sw_state(rfkill, !ret);
632 }
633
634 static struct rfkill_ops swsmi_rfkill_ops = {
635         .set_block = swsmi_rfkill_set,
636         .query = swsmi_rfkill_query,
637 };
638
639 static ssize_t get_performance_level(struct device *dev,
640                                      struct device_attribute *attr, char *buf)
641 {
642         struct samsung_laptop *samsung = dev_get_drvdata(dev);
643         const struct sabi_config *config = samsung->config;
644         const struct sabi_commands *commands = &config->commands;
645         struct sabi_data sretval;
646         int retval;
647         int i;
648
649         /* Read the state */
650         retval = sabi_command(samsung, commands->get_performance_level,
651                               NULL, &sretval);
652         if (retval)
653                 return retval;
654
655         /* The logic is backwards, yeah, lots of fun... */
656         for (i = 0; config->performance_levels[i].name; ++i) {
657                 if (sretval.data[0] == config->performance_levels[i].value)
658                         return sprintf(buf, "%s\n", config->performance_levels[i].name);
659         }
660         return sprintf(buf, "%s\n", "unknown");
661 }
662
663 static ssize_t set_performance_level(struct device *dev,
664                                 struct device_attribute *attr, const char *buf,
665                                 size_t count)
666 {
667         struct samsung_laptop *samsung = dev_get_drvdata(dev);
668         const struct sabi_config *config = samsung->config;
669         const struct sabi_commands *commands = &config->commands;
670         int i;
671
672         if (count < 1)
673                 return count;
674
675         for (i = 0; config->performance_levels[i].name; ++i) {
676                 const struct sabi_performance_level *level =
677                         &config->performance_levels[i];
678                 if (!strncasecmp(level->name, buf, strlen(level->name))) {
679                         sabi_set_commandb(samsung,
680                                           commands->set_performance_level,
681                                           level->value);
682                         break;
683                 }
684         }
685
686         if (!config->performance_levels[i].name)
687                 return -EINVAL;
688
689         return count;
690 }
691
692 static DEVICE_ATTR(performance_level, S_IWUSR | S_IRUGO,
693                    get_performance_level, set_performance_level);
694
695 static int read_battery_life_extender(struct samsung_laptop *samsung)
696 {
697         const struct sabi_commands *commands = &samsung->config->commands;
698         struct sabi_data data;
699         int retval;
700
701         if (commands->get_battery_life_extender == 0xFFFF)
702                 return -ENODEV;
703
704         memset(&data, 0, sizeof(data));
705         data.data[0] = 0x80;
706         retval = sabi_command(samsung, commands->get_battery_life_extender,
707                               &data, &data);
708
709         if (retval)
710                 return retval;
711
712         if (data.data[0] != 0 && data.data[0] != 1)
713                 return -ENODEV;
714
715         return data.data[0];
716 }
717
718 static int write_battery_life_extender(struct samsung_laptop *samsung,
719                                        int enabled)
720 {
721         const struct sabi_commands *commands = &samsung->config->commands;
722         struct sabi_data data;
723
724         memset(&data, 0, sizeof(data));
725         data.data[0] = 0x80 | enabled;
726         return sabi_command(samsung, commands->set_battery_life_extender,
727                             &data, NULL);
728 }
729
730 static ssize_t get_battery_life_extender(struct device *dev,
731                                          struct device_attribute *attr,
732                                          char *buf)
733 {
734         struct samsung_laptop *samsung = dev_get_drvdata(dev);
735         int ret;
736
737         ret = read_battery_life_extender(samsung);
738         if (ret < 0)
739                 return ret;
740
741         return sprintf(buf, "%d\n", ret);
742 }
743
744 static ssize_t set_battery_life_extender(struct device *dev,
745                                         struct device_attribute *attr,
746                                         const char *buf, size_t count)
747 {
748         struct samsung_laptop *samsung = dev_get_drvdata(dev);
749         int ret, value;
750
751         if (!count || sscanf(buf, "%i", &value) != 1)
752                 return -EINVAL;
753
754         ret = write_battery_life_extender(samsung, !!value);
755         if (ret < 0)
756                 return ret;
757
758         return count;
759 }
760
761 static DEVICE_ATTR(battery_life_extender, S_IWUSR | S_IRUGO,
762                    get_battery_life_extender, set_battery_life_extender);
763
764 static int read_usb_charge(struct samsung_laptop *samsung)
765 {
766         const struct sabi_commands *commands = &samsung->config->commands;
767         struct sabi_data data;
768         int retval;
769
770         if (commands->get_usb_charge == 0xFFFF)
771                 return -ENODEV;
772
773         memset(&data, 0, sizeof(data));
774         data.data[0] = 0x80;
775         retval = sabi_command(samsung, commands->get_usb_charge,
776                               &data, &data);
777
778         if (retval)
779                 return retval;
780
781         if (data.data[0] != 0 && data.data[0] != 1)
782                 return -ENODEV;
783
784         return data.data[0];
785 }
786
787 static int write_usb_charge(struct samsung_laptop *samsung,
788                             int enabled)
789 {
790         const struct sabi_commands *commands = &samsung->config->commands;
791         struct sabi_data data;
792
793         memset(&data, 0, sizeof(data));
794         data.data[0] = 0x80 | enabled;
795         return sabi_command(samsung, commands->set_usb_charge,
796                             &data, NULL);
797 }
798
799 static ssize_t get_usb_charge(struct device *dev,
800                               struct device_attribute *attr,
801                               char *buf)
802 {
803         struct samsung_laptop *samsung = dev_get_drvdata(dev);
804         int ret;
805
806         ret = read_usb_charge(samsung);
807         if (ret < 0)
808                 return ret;
809
810         return sprintf(buf, "%d\n", ret);
811 }
812
813 static ssize_t set_usb_charge(struct device *dev,
814                               struct device_attribute *attr,
815                               const char *buf, size_t count)
816 {
817         struct samsung_laptop *samsung = dev_get_drvdata(dev);
818         int ret, value;
819
820         if (!count || sscanf(buf, "%i", &value) != 1)
821                 return -EINVAL;
822
823         ret = write_usb_charge(samsung, !!value);
824         if (ret < 0)
825                 return ret;
826
827         return count;
828 }
829
830 static DEVICE_ATTR(usb_charge, S_IWUSR | S_IRUGO,
831                    get_usb_charge, set_usb_charge);
832
833 static struct attribute *platform_attributes[] = {
834         &dev_attr_performance_level.attr,
835         &dev_attr_battery_life_extender.attr,
836         &dev_attr_usb_charge.attr,
837         NULL
838 };
839
840 static int find_signature(void __iomem *memcheck, const char *testStr)
841 {
842         int i = 0;
843         int loca;
844
845         for (loca = 0; loca < 0xffff; loca++) {
846                 char temp = readb(memcheck + loca);
847
848                 if (temp == testStr[i]) {
849                         if (i == strlen(testStr)-1)
850                                 break;
851                         ++i;
852                 } else {
853                         i = 0;
854                 }
855         }
856         return loca;
857 }
858
859 static void samsung_rfkill_exit(struct samsung_laptop *samsung)
860 {
861         if (samsung->wlan.rfkill) {
862                 rfkill_unregister(samsung->wlan.rfkill);
863                 rfkill_destroy(samsung->wlan.rfkill);
864                 samsung->wlan.rfkill = NULL;
865         }
866         if (samsung->bluetooth.rfkill) {
867                 rfkill_unregister(samsung->bluetooth.rfkill);
868                 rfkill_destroy(samsung->bluetooth.rfkill);
869                 samsung->bluetooth.rfkill = NULL;
870         }
871 }
872
873 static int samsung_new_rfkill(struct samsung_laptop *samsung,
874                               struct samsung_rfkill *arfkill,
875                               const char *name, enum rfkill_type type,
876                               const struct rfkill_ops *ops,
877                               int blocked)
878 {
879         struct rfkill **rfkill = &arfkill->rfkill;
880         int ret;
881
882         arfkill->type = type;
883         arfkill->samsung = samsung;
884
885         *rfkill = rfkill_alloc(name, &samsung->platform_device->dev,
886                                type, ops, arfkill);
887
888         if (!*rfkill)
889                 return -EINVAL;
890
891         if (blocked != -1)
892                 rfkill_init_sw_state(*rfkill, blocked);
893
894         ret = rfkill_register(*rfkill);
895         if (ret) {
896                 rfkill_destroy(*rfkill);
897                 *rfkill = NULL;
898                 return ret;
899         }
900         return 0;
901 }
902
903 static int __init samsung_rfkill_init_seclinux(struct samsung_laptop *samsung)
904 {
905         return samsung_new_rfkill(samsung, &samsung->wlan, "samsung-wlan",
906                                   RFKILL_TYPE_WLAN, &seclinux_rfkill_ops, -1);
907 }
908
909 static int __init samsung_rfkill_init_swsmi(struct samsung_laptop *samsung)
910 {
911         struct sabi_data data;
912         int ret;
913
914         ret = swsmi_wireless_status(samsung, &data);
915         if (ret) {
916                 /* Some swsmi laptops use the old seclinux way to control
917                  * wireless devices */
918                 if (ret == -EINVAL)
919                         ret = samsung_rfkill_init_seclinux(samsung);
920                 return ret;
921         }
922
923         /* 0x02 seems to mean that the device is no present/available */
924
925         if (data.data[WL_STATUS_WLAN] != 0x02)
926                 ret = samsung_new_rfkill(samsung, &samsung->wlan,
927                                          "samsung-wlan",
928                                          RFKILL_TYPE_WLAN,
929                                          &swsmi_rfkill_ops,
930                                          !data.data[WL_STATUS_WLAN]);
931         if (ret)
932                 goto exit;
933
934         if (data.data[WL_STATUS_BT] != 0x02)
935                 ret = samsung_new_rfkill(samsung, &samsung->bluetooth,
936                                          "samsung-bluetooth",
937                                          RFKILL_TYPE_BLUETOOTH,
938                                          &swsmi_rfkill_ops,
939                                          !data.data[WL_STATUS_BT]);
940         if (ret)
941                 goto exit;
942
943 exit:
944         if (ret)
945                 samsung_rfkill_exit(samsung);
946
947         return ret;
948 }
949
950 static int __init samsung_rfkill_init(struct samsung_laptop *samsung)
951 {
952         if (samsung->config->sabi_version == 2)
953                 return samsung_rfkill_init_seclinux(samsung);
954         if (samsung->config->sabi_version == 3)
955                 return samsung_rfkill_init_swsmi(samsung);
956         return 0;
957 }
958
959 static int kbd_backlight_enable(struct samsung_laptop *samsung)
960 {
961         const struct sabi_commands *commands = &samsung->config->commands;
962         struct sabi_data data;
963         int retval;
964
965         if (commands->kbd_backlight == 0xFFFF)
966                 return -ENODEV;
967
968         memset(&data, 0, sizeof(data));
969         data.d0 = 0xaabb;
970         retval = sabi_command(samsung, commands->kbd_backlight,
971                               &data, &data);
972
973         if (retval)
974                 return retval;
975
976         if (data.d0 != 0xccdd)
977                 return -ENODEV;
978         return 0;
979 }
980
981 static int kbd_backlight_read(struct samsung_laptop *samsung)
982 {
983         const struct sabi_commands *commands = &samsung->config->commands;
984         struct sabi_data data;
985         int retval;
986
987         memset(&data, 0, sizeof(data));
988         data.data[0] = 0x81;
989         retval = sabi_command(samsung, commands->kbd_backlight,
990                               &data, &data);
991
992         if (retval)
993                 return retval;
994
995         return data.data[0];
996 }
997
998 static int kbd_backlight_write(struct samsung_laptop *samsung, int brightness)
999 {
1000         const struct sabi_commands *commands = &samsung->config->commands;
1001         struct sabi_data data;
1002
1003         memset(&data, 0, sizeof(data));
1004         data.d0 = 0x82 | ((brightness & 0xFF) << 8);
1005         return sabi_command(samsung, commands->kbd_backlight,
1006                             &data, NULL);
1007 }
1008
1009 static void kbd_led_update(struct work_struct *work)
1010 {
1011         struct samsung_laptop *samsung;
1012
1013         samsung = container_of(work, struct samsung_laptop, kbd_led_work);
1014         kbd_backlight_write(samsung, samsung->kbd_led_wk);
1015 }
1016
1017 static void kbd_led_set(struct led_classdev *led_cdev,
1018                         enum led_brightness value)
1019 {
1020         struct samsung_laptop *samsung;
1021
1022         samsung = container_of(led_cdev, struct samsung_laptop, kbd_led);
1023
1024         if (value > samsung->kbd_led.max_brightness)
1025                 value = samsung->kbd_led.max_brightness;
1026         else if (value < 0)
1027                 value = 0;
1028
1029         samsung->kbd_led_wk = value;
1030         queue_work(samsung->led_workqueue, &samsung->kbd_led_work);
1031 }
1032
1033 static enum led_brightness kbd_led_get(struct led_classdev *led_cdev)
1034 {
1035         struct samsung_laptop *samsung;
1036
1037         samsung = container_of(led_cdev, struct samsung_laptop, kbd_led);
1038         return kbd_backlight_read(samsung);
1039 }
1040
1041 static void samsung_leds_exit(struct samsung_laptop *samsung)
1042 {
1043         if (!IS_ERR_OR_NULL(samsung->kbd_led.dev))
1044                 led_classdev_unregister(&samsung->kbd_led);
1045         if (samsung->led_workqueue)
1046                 destroy_workqueue(samsung->led_workqueue);
1047 }
1048
1049 static int __init samsung_leds_init(struct samsung_laptop *samsung)
1050 {
1051         int ret = 0;
1052
1053         samsung->led_workqueue = create_singlethread_workqueue("led_workqueue");
1054         if (!samsung->led_workqueue)
1055                 return -ENOMEM;
1056
1057         if (kbd_backlight_enable(samsung) >= 0) {
1058                 INIT_WORK(&samsung->kbd_led_work, kbd_led_update);
1059
1060                 samsung->kbd_led.name = "samsung::kbd_backlight";
1061                 samsung->kbd_led.brightness_set = kbd_led_set;
1062                 samsung->kbd_led.brightness_get = kbd_led_get;
1063                 samsung->kbd_led.max_brightness = 8;
1064                 if (samsung->quirks->four_kbd_backlight_levels)
1065                         samsung->kbd_led.max_brightness = 4;
1066
1067                 ret = led_classdev_register(&samsung->platform_device->dev,
1068                                            &samsung->kbd_led);
1069         }
1070
1071         if (ret)
1072                 samsung_leds_exit(samsung);
1073
1074         return ret;
1075 }
1076
1077 static void samsung_backlight_exit(struct samsung_laptop *samsung)
1078 {
1079         if (samsung->backlight_device) {
1080                 backlight_device_unregister(samsung->backlight_device);
1081                 samsung->backlight_device = NULL;
1082         }
1083 }
1084
1085 static int __init samsung_backlight_init(struct samsung_laptop *samsung)
1086 {
1087         struct backlight_device *bd;
1088         struct backlight_properties props;
1089
1090         if (!samsung->handle_backlight)
1091                 return 0;
1092
1093         memset(&props, 0, sizeof(struct backlight_properties));
1094         props.type = BACKLIGHT_PLATFORM;
1095         props.max_brightness = samsung->config->max_brightness -
1096                 samsung->config->min_brightness;
1097
1098         bd = backlight_device_register("samsung",
1099                                        &samsung->platform_device->dev,
1100                                        samsung, &backlight_ops,
1101                                        &props);
1102         if (IS_ERR(bd))
1103                 return PTR_ERR(bd);
1104
1105         samsung->backlight_device = bd;
1106         samsung->backlight_device->props.brightness = read_brightness(samsung);
1107         samsung->backlight_device->props.power = FB_BLANK_UNBLANK;
1108         backlight_update_status(samsung->backlight_device);
1109
1110         return 0;
1111 }
1112
1113 static umode_t samsung_sysfs_is_visible(struct kobject *kobj,
1114                                        struct attribute *attr, int idx)
1115 {
1116         struct device *dev = container_of(kobj, struct device, kobj);
1117         struct platform_device *pdev = to_platform_device(dev);
1118         struct samsung_laptop *samsung = platform_get_drvdata(pdev);
1119         bool ok = true;
1120
1121         if (attr == &dev_attr_performance_level.attr)
1122                 ok = !!samsung->config->performance_levels[0].name;
1123         if (attr == &dev_attr_battery_life_extender.attr)
1124                 ok = !!(read_battery_life_extender(samsung) >= 0);
1125         if (attr == &dev_attr_usb_charge.attr)
1126                 ok = !!(read_usb_charge(samsung) >= 0);
1127
1128         return ok ? attr->mode : 0;
1129 }
1130
1131 static struct attribute_group platform_attribute_group = {
1132         .is_visible = samsung_sysfs_is_visible,
1133         .attrs = platform_attributes
1134 };
1135
1136 static void samsung_sysfs_exit(struct samsung_laptop *samsung)
1137 {
1138         struct platform_device *device = samsung->platform_device;
1139
1140         sysfs_remove_group(&device->dev.kobj, &platform_attribute_group);
1141 }
1142
1143 static int __init samsung_sysfs_init(struct samsung_laptop *samsung)
1144 {
1145         struct platform_device *device = samsung->platform_device;
1146
1147         return sysfs_create_group(&device->dev.kobj, &platform_attribute_group);
1148
1149 }
1150
1151 static int show_call(struct seq_file *m, void *data)
1152 {
1153         struct samsung_laptop *samsung = m->private;
1154         struct sabi_data *sdata = &samsung->debug.data;
1155         int ret;
1156
1157         seq_printf(m, "SABI 0x%04x {0x%08x, 0x%08x, 0x%04x, 0x%02x}\n",
1158                    samsung->debug.command,
1159                    sdata->d0, sdata->d1, sdata->d2, sdata->d3);
1160
1161         ret = sabi_command(samsung, samsung->debug.command, sdata, sdata);
1162
1163         if (ret) {
1164                 seq_printf(m, "SABI command 0x%04x failed\n",
1165                            samsung->debug.command);
1166                 return ret;
1167         }
1168
1169         seq_printf(m, "SABI {0x%08x, 0x%08x, 0x%04x, 0x%02x}\n",
1170                    sdata->d0, sdata->d1, sdata->d2, sdata->d3);
1171         return 0;
1172 }
1173
1174 static int samsung_debugfs_open(struct inode *inode, struct file *file)
1175 {
1176         return single_open(file, show_call, inode->i_private);
1177 }
1178
1179 static const struct file_operations samsung_laptop_call_io_ops = {
1180         .owner = THIS_MODULE,
1181         .open = samsung_debugfs_open,
1182         .read = seq_read,
1183         .llseek = seq_lseek,
1184         .release = single_release,
1185 };
1186
1187 static void samsung_debugfs_exit(struct samsung_laptop *samsung)
1188 {
1189         debugfs_remove_recursive(samsung->debug.root);
1190 }
1191
1192 static int samsung_debugfs_init(struct samsung_laptop *samsung)
1193 {
1194         struct dentry *dent;
1195
1196         samsung->debug.root = debugfs_create_dir("samsung-laptop", NULL);
1197         if (!samsung->debug.root) {
1198                 pr_err("failed to create debugfs directory");
1199                 goto error_debugfs;
1200         }
1201
1202         samsung->debug.f0000_wrapper.data = samsung->f0000_segment;
1203         samsung->debug.f0000_wrapper.size = 0xffff;
1204
1205         samsung->debug.data_wrapper.data = &samsung->debug.data;
1206         samsung->debug.data_wrapper.size = sizeof(samsung->debug.data);
1207
1208         samsung->debug.sdiag_wrapper.data = samsung->sdiag;
1209         samsung->debug.sdiag_wrapper.size = strlen(samsung->sdiag);
1210
1211         dent = debugfs_create_u16("command", S_IRUGO | S_IWUSR,
1212                                   samsung->debug.root, &samsung->debug.command);
1213         if (!dent)
1214                 goto error_debugfs;
1215
1216         dent = debugfs_create_u32("d0", S_IRUGO | S_IWUSR, samsung->debug.root,
1217                                   &samsung->debug.data.d0);
1218         if (!dent)
1219                 goto error_debugfs;
1220
1221         dent = debugfs_create_u32("d1", S_IRUGO | S_IWUSR, samsung->debug.root,
1222                                   &samsung->debug.data.d1);
1223         if (!dent)
1224                 goto error_debugfs;
1225
1226         dent = debugfs_create_u16("d2", S_IRUGO | S_IWUSR, samsung->debug.root,
1227                                   &samsung->debug.data.d2);
1228         if (!dent)
1229                 goto error_debugfs;
1230
1231         dent = debugfs_create_u8("d3", S_IRUGO | S_IWUSR, samsung->debug.root,
1232                                  &samsung->debug.data.d3);
1233         if (!dent)
1234                 goto error_debugfs;
1235
1236         dent = debugfs_create_blob("data", S_IRUGO | S_IWUSR,
1237                                    samsung->debug.root,
1238                                    &samsung->debug.data_wrapper);
1239         if (!dent)
1240                 goto error_debugfs;
1241
1242         dent = debugfs_create_blob("f0000_segment", S_IRUSR | S_IWUSR,
1243                                    samsung->debug.root,
1244                                    &samsung->debug.f0000_wrapper);
1245         if (!dent)
1246                 goto error_debugfs;
1247
1248         dent = debugfs_create_file("call", S_IFREG | S_IRUGO,
1249                                    samsung->debug.root, samsung,
1250                                    &samsung_laptop_call_io_ops);
1251         if (!dent)
1252                 goto error_debugfs;
1253
1254         dent = debugfs_create_blob("sdiag", S_IRUGO | S_IWUSR,
1255                                    samsung->debug.root,
1256                                    &samsung->debug.sdiag_wrapper);
1257         if (!dent)
1258                 goto error_debugfs;
1259
1260         return 0;
1261
1262 error_debugfs:
1263         samsung_debugfs_exit(samsung);
1264         return -ENOMEM;
1265 }
1266
1267 static void samsung_sabi_exit(struct samsung_laptop *samsung)
1268 {
1269         const struct sabi_config *config = samsung->config;
1270
1271         /* Turn off "Linux" mode in the BIOS */
1272         if (config && config->commands.set_linux != 0xff)
1273                 sabi_set_commandb(samsung, config->commands.set_linux, 0x80);
1274
1275         if (samsung->sabi_iface) {
1276                 iounmap(samsung->sabi_iface);
1277                 samsung->sabi_iface = NULL;
1278         }
1279         if (samsung->f0000_segment) {
1280                 iounmap(samsung->f0000_segment);
1281                 samsung->f0000_segment = NULL;
1282         }
1283
1284         samsung->config = NULL;
1285 }
1286
1287 static __init void samsung_sabi_infos(struct samsung_laptop *samsung, int loca,
1288                                       unsigned int ifaceP)
1289 {
1290         const struct sabi_config *config = samsung->config;
1291
1292         printk(KERN_DEBUG "This computer supports SABI==%x\n",
1293                loca + 0xf0000 - 6);
1294
1295         printk(KERN_DEBUG "SABI header:\n");
1296         printk(KERN_DEBUG " SMI Port Number = 0x%04x\n",
1297                readw(samsung->sabi + config->header_offsets.port));
1298         printk(KERN_DEBUG " SMI Interface Function = 0x%02x\n",
1299                readb(samsung->sabi + config->header_offsets.iface_func));
1300         printk(KERN_DEBUG " SMI enable memory buffer = 0x%02x\n",
1301                readb(samsung->sabi + config->header_offsets.en_mem));
1302         printk(KERN_DEBUG " SMI restore memory buffer = 0x%02x\n",
1303                readb(samsung->sabi + config->header_offsets.re_mem));
1304         printk(KERN_DEBUG " SABI data offset = 0x%04x\n",
1305                readw(samsung->sabi + config->header_offsets.data_offset));
1306         printk(KERN_DEBUG " SABI data segment = 0x%04x\n",
1307                readw(samsung->sabi + config->header_offsets.data_segment));
1308
1309         printk(KERN_DEBUG " SABI pointer = 0x%08x\n", ifaceP);
1310 }
1311
1312 static void __init samsung_sabi_diag(struct samsung_laptop *samsung)
1313 {
1314         int loca = find_signature(samsung->f0000_segment, "SDiaG@");
1315         int i;
1316
1317         if (loca == 0xffff)
1318                 return ;
1319
1320         /* Example:
1321          * Ident: @SDiaG@686XX-N90X3A/966-SEC-07HL-S90X3A
1322          *
1323          * Product name: 90X3A
1324          * BIOS Version: 07HL
1325          */
1326         loca += 1;
1327         for (i = 0; loca < 0xffff && i < sizeof(samsung->sdiag) - 1; loca++) {
1328                 char temp = readb(samsung->f0000_segment + loca);
1329
1330                 if (isalnum(temp) || temp == '/' || temp == '-')
1331                         samsung->sdiag[i++] = temp;
1332                 else
1333                         break ;
1334         }
1335
1336         if (debug && samsung->sdiag[0])
1337                 pr_info("sdiag: %s", samsung->sdiag);
1338 }
1339
1340 static int __init samsung_sabi_init(struct samsung_laptop *samsung)
1341 {
1342         const struct sabi_config *config = NULL;
1343         const struct sabi_commands *commands;
1344         unsigned int ifaceP;
1345         int ret = 0;
1346         int i;
1347         int loca;
1348
1349         samsung->f0000_segment = ioremap_nocache(0xf0000, 0xffff);
1350         if (!samsung->f0000_segment) {
1351                 if (debug || force)
1352                         pr_err("Can't map the segment at 0xf0000\n");
1353                 ret = -EINVAL;
1354                 goto exit;
1355         }
1356
1357         samsung_sabi_diag(samsung);
1358
1359         /* Try to find one of the signatures in memory to find the header */
1360         for (i = 0; sabi_configs[i].test_string != 0; ++i) {
1361                 samsung->config = &sabi_configs[i];
1362                 loca = find_signature(samsung->f0000_segment,
1363                                       samsung->config->test_string);
1364                 if (loca != 0xffff)
1365                         break;
1366         }
1367
1368         if (loca == 0xffff) {
1369                 if (debug || force)
1370                         pr_err("This computer does not support SABI\n");
1371                 ret = -ENODEV;
1372                 goto exit;
1373         }
1374
1375         config = samsung->config;
1376         commands = &config->commands;
1377
1378         /* point to the SMI port Number */
1379         loca += 1;
1380         samsung->sabi = (samsung->f0000_segment + loca);
1381
1382         /* Get a pointer to the SABI Interface */
1383         ifaceP = (readw(samsung->sabi + config->header_offsets.data_segment) & 0x0ffff) << 4;
1384         ifaceP += readw(samsung->sabi + config->header_offsets.data_offset) & 0x0ffff;
1385
1386         if (debug)
1387                 samsung_sabi_infos(samsung, loca, ifaceP);
1388
1389         samsung->sabi_iface = ioremap_nocache(ifaceP, 16);
1390         if (!samsung->sabi_iface) {
1391                 pr_err("Can't remap %x\n", ifaceP);
1392                 ret = -EINVAL;
1393                 goto exit;
1394         }
1395
1396         /* Turn on "Linux" mode in the BIOS */
1397         if (commands->set_linux != 0xff) {
1398                 int retval = sabi_set_commandb(samsung,
1399                                                commands->set_linux, 0x81);
1400                 if (retval) {
1401                         pr_warn("Linux mode was not set!\n");
1402                         ret = -ENODEV;
1403                         goto exit;
1404                 }
1405         }
1406
1407         /* Check for stepping quirk */
1408         if (samsung->handle_backlight)
1409                 check_for_stepping_quirk(samsung);
1410
1411         pr_info("detected SABI interface: %s\n",
1412                 samsung->config->test_string);
1413
1414 exit:
1415         if (ret)
1416                 samsung_sabi_exit(samsung);
1417
1418         return ret;
1419 }
1420
1421 static void samsung_platform_exit(struct samsung_laptop *samsung)
1422 {
1423         if (samsung->platform_device) {
1424                 platform_device_unregister(samsung->platform_device);
1425                 samsung->platform_device = NULL;
1426         }
1427 }
1428
1429 static int samsung_pm_notification(struct notifier_block *nb,
1430                                    unsigned long val, void *ptr)
1431 {
1432         struct samsung_laptop *samsung;
1433
1434         samsung = container_of(nb, struct samsung_laptop, pm_nb);
1435         if (val == PM_POST_HIBERNATION &&
1436             samsung->quirks->enable_kbd_backlight)
1437                 kbd_backlight_enable(samsung);
1438
1439         return 0;
1440 }
1441
1442 static int __init samsung_platform_init(struct samsung_laptop *samsung)
1443 {
1444         struct platform_device *pdev;
1445
1446         pdev = platform_device_register_simple("samsung", -1, NULL, 0);
1447         if (IS_ERR(pdev))
1448                 return PTR_ERR(pdev);
1449
1450         samsung->platform_device = pdev;
1451         platform_set_drvdata(samsung->platform_device, samsung);
1452         return 0;
1453 }
1454
1455 static struct samsung_quirks *quirks;
1456
1457 static int __init samsung_dmi_matched(const struct dmi_system_id *d)
1458 {
1459         quirks = d->driver_data;
1460         return 0;
1461 }
1462
1463 static struct dmi_system_id __initdata samsung_dmi_table[] = {
1464         {
1465                 .matches = {
1466                         DMI_MATCH(DMI_SYS_VENDOR,
1467                                         "SAMSUNG ELECTRONICS CO., LTD."),
1468                         DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */
1469                 },
1470         },
1471         {
1472                 .matches = {
1473                         DMI_MATCH(DMI_SYS_VENDOR,
1474                                         "SAMSUNG ELECTRONICS CO., LTD."),
1475                         DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /* Laptop */
1476                 },
1477         },
1478         {
1479                 .matches = {
1480                         DMI_MATCH(DMI_SYS_VENDOR,
1481                                         "SAMSUNG ELECTRONICS CO., LTD."),
1482                         DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */
1483                 },
1484         },
1485         {
1486                 .matches = {
1487                         DMI_MATCH(DMI_SYS_VENDOR,
1488                                         "SAMSUNG ELECTRONICS CO., LTD."),
1489                         DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */
1490                 },
1491         },
1492         /* DMI ids for laptops with bad Chassis Type */
1493         {
1494           .ident = "R40/R41",
1495           .matches = {
1496                 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
1497                 DMI_MATCH(DMI_PRODUCT_NAME, "R40/R41"),
1498                 DMI_MATCH(DMI_BOARD_NAME, "R40/R41"),
1499                 },
1500         },
1501         /* Specific DMI ids for laptop with quirks */
1502         {
1503          .callback = samsung_dmi_matched,
1504          .ident = "N150P",
1505          .matches = {
1506                 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
1507                 DMI_MATCH(DMI_PRODUCT_NAME, "N150P"),
1508                 DMI_MATCH(DMI_BOARD_NAME, "N150P"),
1509                 },
1510          .driver_data = &samsung_broken_acpi_video,
1511         },
1512         {
1513          .callback = samsung_dmi_matched,
1514          .ident = "N145P/N250P/N260P",
1515          .matches = {
1516                 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
1517                 DMI_MATCH(DMI_PRODUCT_NAME, "N145P/N250P/N260P"),
1518                 DMI_MATCH(DMI_BOARD_NAME, "N145P/N250P/N260P"),
1519                 },
1520          .driver_data = &samsung_broken_acpi_video,
1521         },
1522         {
1523          .callback = samsung_dmi_matched,
1524          .ident = "N150/N210/N220",
1525          .matches = {
1526                 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
1527                 DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220"),
1528                 DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220"),
1529                 },
1530          .driver_data = &samsung_broken_acpi_video,
1531         },
1532         {
1533          .callback = samsung_dmi_matched,
1534          .ident = "NF110/NF210/NF310",
1535          .matches = {
1536                 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
1537                 DMI_MATCH(DMI_PRODUCT_NAME, "NF110/NF210/NF310"),
1538                 DMI_MATCH(DMI_BOARD_NAME, "NF110/NF210/NF310"),
1539                 },
1540          .driver_data = &samsung_broken_acpi_video,
1541         },
1542         {
1543          .callback = samsung_dmi_matched,
1544          .ident = "X360",
1545          .matches = {
1546                 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
1547                 DMI_MATCH(DMI_PRODUCT_NAME, "X360"),
1548                 DMI_MATCH(DMI_BOARD_NAME, "X360"),
1549                 },
1550          .driver_data = &samsung_broken_acpi_video,
1551         },
1552         {
1553          .callback = samsung_dmi_matched,
1554          .ident = "N250P",
1555          .matches = {
1556                 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
1557                 DMI_MATCH(DMI_PRODUCT_NAME, "N250P"),
1558                 DMI_MATCH(DMI_BOARD_NAME, "N250P"),
1559                 },
1560          .driver_data = &samsung_broken_acpi_video,
1561         },
1562         {
1563          .callback = samsung_dmi_matched,
1564          .ident = "730U3E/740U3E",
1565          .matches = {
1566                 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
1567                 DMI_MATCH(DMI_PRODUCT_NAME, "730U3E/740U3E"),
1568                 },
1569          .driver_data = &samsung_np740u3e,
1570         },
1571         { },
1572 };
1573 MODULE_DEVICE_TABLE(dmi, samsung_dmi_table);
1574
1575 static struct platform_device *samsung_platform_device;
1576
1577 static int __init samsung_init(void)
1578 {
1579         struct samsung_laptop *samsung;
1580         int ret;
1581
1582         if (efi_enabled(EFI_BOOT))
1583                 return -ENODEV;
1584
1585         quirks = &samsung_unknown;
1586         if (!force && !dmi_check_system(samsung_dmi_table))
1587                 return -ENODEV;
1588
1589         samsung = kzalloc(sizeof(*samsung), GFP_KERNEL);
1590         if (!samsung)
1591                 return -ENOMEM;
1592
1593         mutex_init(&samsung->sabi_mutex);
1594         samsung->handle_backlight = true;
1595         samsung->quirks = quirks;
1596
1597
1598 #ifdef CONFIG_ACPI
1599         if (samsung->quirks->broken_acpi_video)
1600                 acpi_video_dmi_promote_vendor();
1601
1602         /* Don't handle backlight here if the acpi video already handle it */
1603         if (acpi_video_backlight_support()) {
1604                 samsung->handle_backlight = false;
1605         } else if (samsung->quirks->broken_acpi_video) {
1606                 pr_info("Disabling ACPI video driver\n");
1607                 acpi_video_unregister();
1608         }
1609 #endif
1610
1611         ret = samsung_platform_init(samsung);
1612         if (ret)
1613                 goto error_platform;
1614
1615         ret = samsung_sabi_init(samsung);
1616         if (ret)
1617                 goto error_sabi;
1618
1619 #ifdef CONFIG_ACPI
1620         /* Only log that if we are really on a sabi platform */
1621         if (acpi_video_backlight_support())
1622                 pr_info("Backlight controlled by ACPI video driver\n");
1623 #endif
1624
1625         ret = samsung_sysfs_init(samsung);
1626         if (ret)
1627                 goto error_sysfs;
1628
1629         ret = samsung_backlight_init(samsung);
1630         if (ret)
1631                 goto error_backlight;
1632
1633         ret = samsung_rfkill_init(samsung);
1634         if (ret)
1635                 goto error_rfkill;
1636
1637         ret = samsung_leds_init(samsung);
1638         if (ret)
1639                 goto error_leds;
1640
1641         ret = samsung_debugfs_init(samsung);
1642         if (ret)
1643                 goto error_debugfs;
1644
1645         samsung->pm_nb.notifier_call = samsung_pm_notification;
1646         register_pm_notifier(&samsung->pm_nb);
1647
1648         samsung_platform_device = samsung->platform_device;
1649         return ret;
1650
1651 error_debugfs:
1652         samsung_leds_exit(samsung);
1653 error_leds:
1654         samsung_rfkill_exit(samsung);
1655 error_rfkill:
1656         samsung_backlight_exit(samsung);
1657 error_backlight:
1658         samsung_sysfs_exit(samsung);
1659 error_sysfs:
1660         samsung_sabi_exit(samsung);
1661 error_sabi:
1662         samsung_platform_exit(samsung);
1663 error_platform:
1664         kfree(samsung);
1665         return ret;
1666 }
1667
1668 static void __exit samsung_exit(void)
1669 {
1670         struct samsung_laptop *samsung;
1671
1672         samsung = platform_get_drvdata(samsung_platform_device);
1673         unregister_pm_notifier(&samsung->pm_nb);
1674
1675         samsung_debugfs_exit(samsung);
1676         samsung_leds_exit(samsung);
1677         samsung_rfkill_exit(samsung);
1678         samsung_backlight_exit(samsung);
1679         samsung_sysfs_exit(samsung);
1680         samsung_sabi_exit(samsung);
1681         samsung_platform_exit(samsung);
1682
1683         kfree(samsung);
1684         samsung_platform_device = NULL;
1685 }
1686
1687 module_init(samsung_init);
1688 module_exit(samsung_exit);
1689
1690 MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@suse.de>");
1691 MODULE_DESCRIPTION("Samsung Backlight driver");
1692 MODULE_LICENSE("GPL");