blob: d15b6874b1b218d5594e14f1a16771c4bf4cab7a [file] [log] [blame]
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -05001/*
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/fb.h>
21#include <linux/dmi.h>
22#include <linux/platform_device.h>
23#include <linux/rfkill.h>
Corentin Charyf34cd9c2011-11-26 11:00:00 +010024#include <linux/acpi.h>
Corentin Chary5b80fc42011-11-26 11:00:03 +010025#include <linux/seq_file.h>
26#include <linux/debugfs.h>
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -050027
28/*
29 * This driver is needed because a number of Samsung laptops do not hook
30 * their control settings through ACPI. So we have to poke around in the
31 * BIOS to do things like brightness values, and "special" key controls.
32 */
33
34/*
35 * We have 0 - 8 as valid brightness levels. The specs say that level 0 should
36 * be reserved by the BIOS (which really doesn't make much sense), we tell
37 * userspace that the value is 0 - 7 and then just tell the hardware 1 - 8
38 */
39#define MAX_BRIGHT 0x07
40
41
42#define SABI_IFACE_MAIN 0x00
43#define SABI_IFACE_SUB 0x02
44#define SABI_IFACE_COMPLETE 0x04
45#define SABI_IFACE_DATA 0x05
46
Corentin Chary7e960712011-11-26 11:00:02 +010047/* Structure get/set data using sabi */
48struct sabi_data {
49 union {
50 struct {
51 u32 d0;
52 u32 d1;
53 u16 d2;
54 u8 d3;
55 };
56 u8 data[11];
57 };
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -050058};
59
60struct sabi_header_offsets {
61 u8 port;
62 u8 re_mem;
63 u8 iface_func;
64 u8 en_mem;
65 u8 data_offset;
66 u8 data_segment;
67};
68
69struct sabi_commands {
70 /*
71 * Brightness is 0 - 8, as described above.
72 * Value 0 is for the BIOS to use
73 */
Corentin Chary7e960712011-11-26 11:00:02 +010074 u16 get_brightness;
75 u16 set_brightness;
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -050076
77 /*
78 * first byte:
79 * 0x00 - wireless is off
80 * 0x01 - wireless is on
81 * second byte:
82 * 0x02 - 3G is off
83 * 0x03 - 3G is on
84 * TODO, verify 3G is correct, that doesn't seem right...
85 */
Corentin Chary7e960712011-11-26 11:00:02 +010086 u16 get_wireless_button;
87 u16 set_wireless_button;
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -050088
89 /* 0 is off, 1 is on */
Corentin Chary7e960712011-11-26 11:00:02 +010090 u16 get_backlight;
91 u16 set_backlight;
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -050092
93 /*
94 * 0x80 or 0x00 - no action
95 * 0x81 - recovery key pressed
96 */
Corentin Chary7e960712011-11-26 11:00:02 +010097 u16 get_recovery_mode;
98 u16 set_recovery_mode;
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -050099
100 /*
101 * on seclinux: 0 is low, 1 is high,
102 * on swsmi: 0 is normal, 1 is silent, 2 is turbo
103 */
Corentin Chary7e960712011-11-26 11:00:02 +0100104 u16 get_performance_level;
105 u16 set_performance_level;
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500106
107 /*
108 * Tell the BIOS that Linux is running on this machine.
109 * 81 is on, 80 is off
110 */
Corentin Chary7e960712011-11-26 11:00:02 +0100111 u16 set_linux;
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500112};
113
114struct sabi_performance_level {
115 const char *name;
Corentin Chary7e960712011-11-26 11:00:02 +0100116 u16 value;
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500117};
118
119struct sabi_config {
120 const char *test_string;
121 u16 main_function;
122 const struct sabi_header_offsets header_offsets;
123 const struct sabi_commands commands;
124 const struct sabi_performance_level performance_levels[4];
125 u8 min_brightness;
126 u8 max_brightness;
127};
128
129static const struct sabi_config sabi_configs[] = {
130 {
131 .test_string = "SECLINUX",
132
133 .main_function = 0x4c49,
134
135 .header_offsets = {
136 .port = 0x00,
137 .re_mem = 0x02,
138 .iface_func = 0x03,
139 .en_mem = 0x04,
140 .data_offset = 0x05,
141 .data_segment = 0x07,
142 },
143
144 .commands = {
145 .get_brightness = 0x00,
146 .set_brightness = 0x01,
147
148 .get_wireless_button = 0x02,
149 .set_wireless_button = 0x03,
150
151 .get_backlight = 0x04,
152 .set_backlight = 0x05,
153
154 .get_recovery_mode = 0x06,
155 .set_recovery_mode = 0x07,
156
157 .get_performance_level = 0x08,
158 .set_performance_level = 0x09,
159
160 .set_linux = 0x0a,
161 },
162
163 .performance_levels = {
164 {
165 .name = "silent",
166 .value = 0,
167 },
168 {
169 .name = "normal",
170 .value = 1,
171 },
172 { },
173 },
174 .min_brightness = 1,
175 .max_brightness = 8,
176 },
177 {
178 .test_string = "SwSmi@",
179
180 .main_function = 0x5843,
181
182 .header_offsets = {
183 .port = 0x00,
184 .re_mem = 0x04,
185 .iface_func = 0x02,
186 .en_mem = 0x03,
187 .data_offset = 0x05,
188 .data_segment = 0x07,
189 },
190
191 .commands = {
192 .get_brightness = 0x10,
193 .set_brightness = 0x11,
194
195 .get_wireless_button = 0x12,
196 .set_wireless_button = 0x13,
197
198 .get_backlight = 0x2d,
199 .set_backlight = 0x2e,
200
201 .get_recovery_mode = 0xff,
202 .set_recovery_mode = 0xff,
203
204 .get_performance_level = 0x31,
205 .set_performance_level = 0x32,
206
207 .set_linux = 0xff,
208 },
209
210 .performance_levels = {
211 {
212 .name = "normal",
213 .value = 0,
214 },
215 {
216 .name = "silent",
217 .value = 1,
218 },
219 {
220 .name = "overclock",
221 .value = 2,
222 },
223 { },
224 },
225 .min_brightness = 0,
226 .max_brightness = 8,
227 },
228 { },
229};
230
Corentin Chary5b80fc42011-11-26 11:00:03 +0100231/*
232 * samsung-laptop/ - debugfs root directory
233 * f0000_segment - dump f0000 segment
234 * command - current command
235 * data - current data
236 * d0, d1, d2, d3 - data fields
237 * call - call SABI using command and data
238 *
239 * This allow to call arbitrary sabi commands wihout
240 * modifying the driver at all.
241 * For example, setting the keyboard backlight brightness to 5
242 *
243 * echo 0x78 > command
244 * echo 0x0582 > d0
245 * echo 0 > d1
246 * echo 0 > d2
247 * echo 0 > d3
248 * cat call
249 */
250
251struct samsung_laptop_debug {
252 struct dentry *root;
253 struct sabi_data data;
254 u16 command;
255
256 struct debugfs_blob_wrapper f0000_wrapper;
257 struct debugfs_blob_wrapper data_wrapper;
258};
259
Corentin Charya6df4892011-11-26 10:59:58 +0100260struct samsung_laptop {
261 const struct sabi_config *config;
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500262
Corentin Charya6df4892011-11-26 10:59:58 +0100263 void __iomem *sabi;
264 void __iomem *sabi_iface;
265 void __iomem *f0000_segment;
266
267 struct mutex sabi_mutex;
268
Corentin Chary5dea7a22011-11-26 10:59:59 +0100269 struct platform_device *platform_device;
Corentin Charya6df4892011-11-26 10:59:58 +0100270 struct backlight_device *backlight_device;
271 struct rfkill *rfk;
272
Corentin Chary5b80fc42011-11-26 11:00:03 +0100273 struct samsung_laptop_debug debug;
274
Corentin Charyf34cd9c2011-11-26 11:00:00 +0100275 bool handle_backlight;
Corentin Charya6df4892011-11-26 10:59:58 +0100276 bool has_stepping_quirk;
277};
278
Corentin Chary5dea7a22011-11-26 10:59:59 +0100279
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500280
Rusty Russell90ab5ee2012-01-13 09:32:20 +1030281static bool force;
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500282module_param(force, bool, 0);
283MODULE_PARM_DESC(force,
284 "Disable the DMI check and forces the driver to be loaded");
285
Rusty Russell90ab5ee2012-01-13 09:32:20 +1030286static bool debug;
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500287module_param(debug, bool, S_IRUGO | S_IWUSR);
288MODULE_PARM_DESC(debug, "Debug enabled or not");
289
Corentin Chary7e960712011-11-26 11:00:02 +0100290static int sabi_command(struct samsung_laptop *samsung, u16 command,
291 struct sabi_data *in,
292 struct sabi_data *out)
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500293{
Corentin Charya6df4892011-11-26 10:59:58 +0100294 const struct sabi_config *config = samsung->config;
Corentin Chary7e960712011-11-26 11:00:02 +0100295 int ret = 0;
Corentin Charya6df4892011-11-26 10:59:58 +0100296 u16 port = readw(samsung->sabi + config->header_offsets.port);
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500297 u8 complete, iface_data;
298
Corentin Charya6df4892011-11-26 10:59:58 +0100299 mutex_lock(&samsung->sabi_mutex);
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500300
Corentin Chary7e960712011-11-26 11:00:02 +0100301 if (debug) {
302 if (in)
303 pr_info("SABI 0x%04x {0x%08x, 0x%08x, 0x%04x, 0x%02x}",
304 command, in->d0, in->d1, in->d2, in->d3);
305 else
306 pr_info("SABI 0x%04x", command);
307 }
308
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500309 /* enable memory to be able to write to it */
Corentin Charya6df4892011-11-26 10:59:58 +0100310 outb(readb(samsung->sabi + config->header_offsets.en_mem), port);
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500311
312 /* write out the command */
Corentin Charya6df4892011-11-26 10:59:58 +0100313 writew(config->main_function, samsung->sabi_iface + SABI_IFACE_MAIN);
314 writew(command, samsung->sabi_iface + SABI_IFACE_SUB);
315 writeb(0, samsung->sabi_iface + SABI_IFACE_COMPLETE);
Corentin Chary7e960712011-11-26 11:00:02 +0100316 if (in) {
317 writel(in->d0, samsung->sabi_iface + SABI_IFACE_DATA);
318 writel(in->d1, samsung->sabi_iface + SABI_IFACE_DATA + 4);
319 writew(in->d2, samsung->sabi_iface + SABI_IFACE_DATA + 8);
320 writeb(in->d3, samsung->sabi_iface + SABI_IFACE_DATA + 10);
321 }
Corentin Charya6df4892011-11-26 10:59:58 +0100322 outb(readb(samsung->sabi + config->header_offsets.iface_func), port);
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500323
324 /* write protect memory to make it safe */
Corentin Charya6df4892011-11-26 10:59:58 +0100325 outb(readb(samsung->sabi + config->header_offsets.re_mem), port);
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500326
327 /* see if the command actually succeeded */
Corentin Charya6df4892011-11-26 10:59:58 +0100328 complete = readb(samsung->sabi_iface + SABI_IFACE_COMPLETE);
329 iface_data = readb(samsung->sabi_iface + SABI_IFACE_DATA);
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500330 if (complete != 0xaa || iface_data == 0xff) {
Corentin Chary7e960712011-11-26 11:00:02 +0100331 pr_warn("SABI command 0x%04x failed with"
332 " completion flag 0x%02x and interface data 0x%02x",
333 command, complete, iface_data);
334 ret = -EINVAL;
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500335 goto exit;
336 }
Corentin Chary7e960712011-11-26 11:00:02 +0100337
338 if (out) {
339 out->d0 = readl(samsung->sabi_iface + SABI_IFACE_DATA);
340 out->d1 = readl(samsung->sabi_iface + SABI_IFACE_DATA + 4);
341 out->d2 = readw(samsung->sabi_iface + SABI_IFACE_DATA + 2);
342 out->d3 = readb(samsung->sabi_iface + SABI_IFACE_DATA + 1);
343 }
344
345 if (debug && out) {
346 pr_info("SABI {0x%08x, 0x%08x, 0x%04x, 0x%02x}",
347 out->d0, out->d1, out->d2, out->d3);
348 }
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500349
350exit:
Corentin Charya6df4892011-11-26 10:59:58 +0100351 mutex_unlock(&samsung->sabi_mutex);
Corentin Chary7e960712011-11-26 11:00:02 +0100352 return ret;
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500353}
354
Corentin Chary7e960712011-11-26 11:00:02 +0100355/* simple wrappers usable with most commands */
356static int sabi_set_commandb(struct samsung_laptop *samsung,
357 u16 command, u8 data)
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500358{
Corentin Chary7e960712011-11-26 11:00:02 +0100359 struct sabi_data in = { .d0 = 0, .d1 = 0, .d2 = 0, .d3 = 0 };
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500360
Corentin Chary7e960712011-11-26 11:00:02 +0100361 in.data[0] = data;
362 return sabi_command(samsung, command, &in, NULL);
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500363}
364
Corentin Chary5dea7a22011-11-26 10:59:59 +0100365static void test_backlight(struct samsung_laptop *samsung)
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500366{
Corentin Charya6df4892011-11-26 10:59:58 +0100367 const struct sabi_commands *commands = &samsung->config->commands;
Corentin Chary7e960712011-11-26 11:00:02 +0100368 struct sabi_data sretval;
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500369
Corentin Chary7e960712011-11-26 11:00:02 +0100370 sabi_command(samsung, commands->get_backlight, NULL, &sretval);
371 printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.data[0]);
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500372
Corentin Chary7e960712011-11-26 11:00:02 +0100373 sabi_set_commandb(samsung, commands->set_backlight, 0);
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500374 printk(KERN_DEBUG "backlight should be off\n");
375
Corentin Chary7e960712011-11-26 11:00:02 +0100376 sabi_command(samsung, commands->get_backlight, NULL, &sretval);
377 printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.data[0]);
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500378
379 msleep(1000);
380
Corentin Chary7e960712011-11-26 11:00:02 +0100381 sabi_set_commandb(samsung, commands->set_backlight, 1);
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500382 printk(KERN_DEBUG "backlight should be on\n");
383
Corentin Chary7e960712011-11-26 11:00:02 +0100384 sabi_command(samsung, commands->get_backlight, NULL, &sretval);
385 printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.data[0]);
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500386}
387
Corentin Chary5dea7a22011-11-26 10:59:59 +0100388static void test_wireless(struct samsung_laptop *samsung)
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500389{
Corentin Charya6df4892011-11-26 10:59:58 +0100390 const struct sabi_commands *commands = &samsung->config->commands;
Corentin Chary7e960712011-11-26 11:00:02 +0100391 struct sabi_data sretval;
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500392
Corentin Chary7e960712011-11-26 11:00:02 +0100393 sabi_command(samsung, commands->get_wireless_button, NULL, &sretval);
394 printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.data[0]);
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500395
Corentin Chary7e960712011-11-26 11:00:02 +0100396 sabi_set_commandb(samsung, commands->set_wireless_button, 0);
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500397 printk(KERN_DEBUG "wireless led should be off\n");
398
Corentin Chary7e960712011-11-26 11:00:02 +0100399 sabi_command(samsung, commands->get_wireless_button, NULL, &sretval);
400 printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.data[0]);
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500401
402 msleep(1000);
403
Corentin Chary7e960712011-11-26 11:00:02 +0100404 sabi_set_commandb(samsung, commands->set_wireless_button, 1);
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500405 printk(KERN_DEBUG "wireless led should be on\n");
406
Corentin Chary7e960712011-11-26 11:00:02 +0100407 sabi_command(samsung, commands->get_wireless_button, NULL, &sretval);
408 printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.data[0]);
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500409}
410
Corentin Chary5dea7a22011-11-26 10:59:59 +0100411static int read_brightness(struct samsung_laptop *samsung)
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500412{
Corentin Charya6df4892011-11-26 10:59:58 +0100413 const struct sabi_config *config = samsung->config;
414 const struct sabi_commands *commands = &samsung->config->commands;
Corentin Chary7e960712011-11-26 11:00:02 +0100415 struct sabi_data sretval;
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500416 int user_brightness = 0;
417 int retval;
418
Corentin Chary7e960712011-11-26 11:00:02 +0100419 retval = sabi_command(samsung, commands->get_brightness,
420 NULL, &sretval);
421 if (retval)
422 return retval;
423
424 user_brightness = sretval.data[0];
425 if (user_brightness > config->min_brightness)
426 user_brightness -= config->min_brightness;
427 else
428 user_brightness = 0;
429
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500430 return user_brightness;
431}
432
Corentin Chary5dea7a22011-11-26 10:59:59 +0100433static void set_brightness(struct samsung_laptop *samsung, u8 user_brightness)
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500434{
Corentin Charya6df4892011-11-26 10:59:58 +0100435 const struct sabi_config *config = samsung->config;
436 const struct sabi_commands *commands = &samsung->config->commands;
437 u8 user_level = user_brightness + config->min_brightness;
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500438
Corentin Charya6df4892011-11-26 10:59:58 +0100439 if (samsung->has_stepping_quirk && user_level != 0) {
Jason Stubbsac080522011-09-20 09:16:13 -0700440 /*
441 * short circuit if the specified level is what's already set
442 * to prevent the screen from flickering needlessly
443 */
Corentin Chary5dea7a22011-11-26 10:59:59 +0100444 if (user_brightness == read_brightness(samsung))
Jason Stubbsac080522011-09-20 09:16:13 -0700445 return;
446
Corentin Chary7e960712011-11-26 11:00:02 +0100447 sabi_set_commandb(samsung, commands->set_brightness, 0);
Jason Stubbsac080522011-09-20 09:16:13 -0700448 }
449
Corentin Chary7e960712011-11-26 11:00:02 +0100450 sabi_set_commandb(samsung, commands->set_brightness, user_level);
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500451}
452
453static int get_brightness(struct backlight_device *bd)
454{
Corentin Chary5dea7a22011-11-26 10:59:59 +0100455 struct samsung_laptop *samsung = bl_get_data(bd);
456
457 return read_brightness(samsung);
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500458}
459
Corentin Chary5dea7a22011-11-26 10:59:59 +0100460static void check_for_stepping_quirk(struct samsung_laptop *samsung)
Jason Stubbsac080522011-09-20 09:16:13 -0700461{
Corentin Chary5dea7a22011-11-26 10:59:59 +0100462 int initial_level;
463 int check_level;
464 int orig_level = read_brightness(samsung);
Jason Stubbsac080522011-09-20 09:16:13 -0700465
466 /*
467 * Some laptops exhibit the strange behaviour of stepping toward
468 * (rather than setting) the brightness except when changing to/from
469 * brightness level 0. This behaviour is checked for here and worked
470 * around in set_brightness.
471 */
472
John Serockba05b232011-10-13 06:42:01 -0400473 if (orig_level == 0)
Corentin Chary5dea7a22011-11-26 10:59:59 +0100474 set_brightness(samsung, 1);
John Serockba05b232011-10-13 06:42:01 -0400475
Corentin Chary5dea7a22011-11-26 10:59:59 +0100476 initial_level = read_brightness(samsung);
John Serockba05b232011-10-13 06:42:01 -0400477
Jason Stubbsac080522011-09-20 09:16:13 -0700478 if (initial_level <= 2)
479 check_level = initial_level + 2;
480 else
481 check_level = initial_level - 2;
482
Corentin Charya6df4892011-11-26 10:59:58 +0100483 samsung->has_stepping_quirk = false;
Corentin Chary5dea7a22011-11-26 10:59:59 +0100484 set_brightness(samsung, check_level);
Jason Stubbsac080522011-09-20 09:16:13 -0700485
Corentin Chary5dea7a22011-11-26 10:59:59 +0100486 if (read_brightness(samsung) != check_level) {
Corentin Charya6df4892011-11-26 10:59:58 +0100487 samsung->has_stepping_quirk = true;
Jason Stubbsac080522011-09-20 09:16:13 -0700488 pr_info("enabled workaround for brightness stepping quirk\n");
489 }
490
Corentin Chary5dea7a22011-11-26 10:59:59 +0100491 set_brightness(samsung, orig_level);
Jason Stubbsac080522011-09-20 09:16:13 -0700492}
493
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500494static int update_status(struct backlight_device *bd)
495{
Corentin Chary5dea7a22011-11-26 10:59:59 +0100496 struct samsung_laptop *samsung = bl_get_data(bd);
Corentin Charya6df4892011-11-26 10:59:58 +0100497 const struct sabi_commands *commands = &samsung->config->commands;
498
Corentin Chary5dea7a22011-11-26 10:59:59 +0100499 set_brightness(samsung, bd->props.brightness);
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500500
501 if (bd->props.power == FB_BLANK_UNBLANK)
Corentin Chary7e960712011-11-26 11:00:02 +0100502 sabi_set_commandb(samsung, commands->set_backlight, 1);
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500503 else
Corentin Chary7e960712011-11-26 11:00:02 +0100504 sabi_set_commandb(samsung, commands->set_backlight, 0);
Corentin Chary5dea7a22011-11-26 10:59:59 +0100505
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500506 return 0;
507}
508
509static const struct backlight_ops backlight_ops = {
510 .get_brightness = get_brightness,
511 .update_status = update_status,
512};
513
514static int rfkill_set(void *data, bool blocked)
515{
Corentin Chary5dea7a22011-11-26 10:59:59 +0100516 struct samsung_laptop *samsung = data;
Corentin Charya6df4892011-11-26 10:59:58 +0100517 const struct sabi_commands *commands = &samsung->config->commands;
518
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500519 /* Do something with blocked...*/
520 /*
521 * blocked == false is on
522 * blocked == true is off
523 */
524 if (blocked)
Corentin Chary7e960712011-11-26 11:00:02 +0100525 sabi_set_commandb(samsung, commands->set_wireless_button, 0);
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500526 else
Corentin Chary7e960712011-11-26 11:00:02 +0100527 sabi_set_commandb(samsung, commands->set_wireless_button, 1);
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500528
529 return 0;
530}
531
532static struct rfkill_ops rfkill_ops = {
533 .set_block = rfkill_set,
534};
535
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500536static ssize_t get_performance_level(struct device *dev,
537 struct device_attribute *attr, char *buf)
538{
Corentin Chary5dea7a22011-11-26 10:59:59 +0100539 struct samsung_laptop *samsung = dev_get_drvdata(dev);
Corentin Charya6df4892011-11-26 10:59:58 +0100540 const struct sabi_config *config = samsung->config;
Corentin Chary5dea7a22011-11-26 10:59:59 +0100541 const struct sabi_commands *commands = &config->commands;
Corentin Chary7e960712011-11-26 11:00:02 +0100542 struct sabi_data sretval;
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500543 int retval;
544 int i;
545
546 /* Read the state */
Corentin Chary7e960712011-11-26 11:00:02 +0100547 retval = sabi_command(samsung, commands->get_performance_level,
548 NULL, &sretval);
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500549 if (retval)
550 return retval;
551
552 /* The logic is backwards, yeah, lots of fun... */
Corentin Charya6df4892011-11-26 10:59:58 +0100553 for (i = 0; config->performance_levels[i].name; ++i) {
Corentin Chary7e960712011-11-26 11:00:02 +0100554 if (sretval.data[0] == config->performance_levels[i].value)
Corentin Charya6df4892011-11-26 10:59:58 +0100555 return sprintf(buf, "%s\n", config->performance_levels[i].name);
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500556 }
557 return sprintf(buf, "%s\n", "unknown");
558}
559
560static ssize_t set_performance_level(struct device *dev,
561 struct device_attribute *attr, const char *buf,
562 size_t count)
563{
Corentin Chary5dea7a22011-11-26 10:59:59 +0100564 struct samsung_laptop *samsung = dev_get_drvdata(dev);
Corentin Charya6df4892011-11-26 10:59:58 +0100565 const struct sabi_config *config = samsung->config;
Corentin Chary5dea7a22011-11-26 10:59:59 +0100566 const struct sabi_commands *commands = &config->commands;
567 int i;
Corentin Charya6df4892011-11-26 10:59:58 +0100568
Corentin Chary5dea7a22011-11-26 10:59:59 +0100569 if (count < 1)
570 return count;
571
572 for (i = 0; config->performance_levels[i].name; ++i) {
573 const struct sabi_performance_level *level =
574 &config->performance_levels[i];
575 if (!strncasecmp(level->name, buf, strlen(level->name))) {
Corentin Chary7e960712011-11-26 11:00:02 +0100576 sabi_set_commandb(samsung,
577 commands->set_performance_level,
578 level->value);
Corentin Chary5dea7a22011-11-26 10:59:59 +0100579 break;
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500580 }
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500581 }
Corentin Chary5dea7a22011-11-26 10:59:59 +0100582
583 if (!config->performance_levels[i].name)
584 return -EINVAL;
585
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500586 return count;
587}
Corentin Chary5dea7a22011-11-26 10:59:59 +0100588
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500589static DEVICE_ATTR(performance_level, S_IWUSR | S_IRUGO,
590 get_performance_level, set_performance_level);
591
Corentin Charya66c1662011-11-26 11:00:01 +0100592static struct attribute *platform_attributes[] = {
593 &dev_attr_performance_level.attr,
594 NULL
595};
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500596
Corentin Chary5dea7a22011-11-26 10:59:59 +0100597static int find_signature(void __iomem *memcheck, const char *testStr)
598{
599 int i = 0;
600 int loca;
601
602 for (loca = 0; loca < 0xffff; loca++) {
603 char temp = readb(memcheck + loca);
604
605 if (temp == testStr[i]) {
606 if (i == strlen(testStr)-1)
607 break;
608 ++i;
609 } else {
610 i = 0;
611 }
612 }
613 return loca;
614}
615
616static void samsung_rfkill_exit(struct samsung_laptop *samsung)
617{
618 if (samsung->rfk) {
619 rfkill_unregister(samsung->rfk);
620 rfkill_destroy(samsung->rfk);
621 samsung->rfk = NULL;
622 }
623}
624
625static int __init samsung_rfkill_init(struct samsung_laptop *samsung)
626{
627 int retval;
628
629 samsung->rfk = rfkill_alloc("samsung-wifi",
630 &samsung->platform_device->dev,
631 RFKILL_TYPE_WLAN,
632 &rfkill_ops, samsung);
633 if (!samsung->rfk)
634 return -ENOMEM;
635
636 retval = rfkill_register(samsung->rfk);
637 if (retval) {
638 rfkill_destroy(samsung->rfk);
639 samsung->rfk = NULL;
640 return -ENODEV;
641 }
642
643 return 0;
644}
645
646static void samsung_backlight_exit(struct samsung_laptop *samsung)
647{
648 if (samsung->backlight_device) {
649 backlight_device_unregister(samsung->backlight_device);
650 samsung->backlight_device = NULL;
651 }
652}
653
654static int __init samsung_backlight_init(struct samsung_laptop *samsung)
655{
656 struct backlight_device *bd;
657 struct backlight_properties props;
658
Corentin Charyf34cd9c2011-11-26 11:00:00 +0100659 if (!samsung->handle_backlight)
660 return 0;
661
Corentin Chary5dea7a22011-11-26 10:59:59 +0100662 memset(&props, 0, sizeof(struct backlight_properties));
663 props.type = BACKLIGHT_PLATFORM;
664 props.max_brightness = samsung->config->max_brightness -
665 samsung->config->min_brightness;
666
667 bd = backlight_device_register("samsung",
668 &samsung->platform_device->dev,
669 samsung, &backlight_ops,
670 &props);
671 if (IS_ERR(bd))
672 return PTR_ERR(bd);
673
674 samsung->backlight_device = bd;
675 samsung->backlight_device->props.brightness = read_brightness(samsung);
676 samsung->backlight_device->props.power = FB_BLANK_UNBLANK;
677 backlight_update_status(samsung->backlight_device);
678
679 return 0;
680}
681
Corentin Charya66c1662011-11-26 11:00:01 +0100682static mode_t samsung_sysfs_is_visible(struct kobject *kobj,
683 struct attribute *attr, int idx)
684{
685 struct device *dev = container_of(kobj, struct device, kobj);
686 struct platform_device *pdev = to_platform_device(dev);
687 struct samsung_laptop *samsung = platform_get_drvdata(pdev);
688 bool ok = true;
689
690 if (attr == &dev_attr_performance_level.attr)
691 ok = !!samsung->config->performance_levels[0].name;
692
693 return ok ? attr->mode : 0;
694}
695
696static struct attribute_group platform_attribute_group = {
697 .is_visible = samsung_sysfs_is_visible,
698 .attrs = platform_attributes
699};
700
Corentin Chary5dea7a22011-11-26 10:59:59 +0100701static void samsung_sysfs_exit(struct samsung_laptop *samsung)
702{
Corentin Charya66c1662011-11-26 11:00:01 +0100703 struct platform_device *device = samsung->platform_device;
704
705 sysfs_remove_group(&device->dev.kobj, &platform_attribute_group);
Corentin Chary5dea7a22011-11-26 10:59:59 +0100706}
707
708static int __init samsung_sysfs_init(struct samsung_laptop *samsung)
709{
Corentin Charya66c1662011-11-26 11:00:01 +0100710 struct platform_device *device = samsung->platform_device;
711
712 return sysfs_create_group(&device->dev.kobj, &platform_attribute_group);
713
Corentin Chary5dea7a22011-11-26 10:59:59 +0100714}
715
Corentin Chary5b80fc42011-11-26 11:00:03 +0100716static int show_call(struct seq_file *m, void *data)
717{
718 struct samsung_laptop *samsung = m->private;
719 struct sabi_data *sdata = &samsung->debug.data;
720 int ret;
721
722 seq_printf(m, "SABI 0x%04x {0x%08x, 0x%08x, 0x%04x, 0x%02x}\n",
723 samsung->debug.command,
724 sdata->d0, sdata->d1, sdata->d2, sdata->d3);
725
726 ret = sabi_command(samsung, samsung->debug.command, sdata, sdata);
727
728 if (ret) {
729 seq_printf(m, "SABI command 0x%04x failed\n",
730 samsung->debug.command);
731 return ret;
732 }
733
734 seq_printf(m, "SABI {0x%08x, 0x%08x, 0x%04x, 0x%02x}\n",
735 sdata->d0, sdata->d1, sdata->d2, sdata->d3);
736 return 0;
737}
738
739static int samsung_debugfs_open(struct inode *inode, struct file *file)
740{
741 return single_open(file, show_call, inode->i_private);
742}
743
744static const struct file_operations samsung_laptop_call_io_ops = {
745 .owner = THIS_MODULE,
746 .open = samsung_debugfs_open,
747 .read = seq_read,
748 .llseek = seq_lseek,
749 .release = single_release,
750};
751
752static void samsung_debugfs_exit(struct samsung_laptop *samsung)
753{
754 debugfs_remove_recursive(samsung->debug.root);
755}
756
757static int samsung_debugfs_init(struct samsung_laptop *samsung)
758{
759 struct dentry *dent;
760
761 samsung->debug.root = debugfs_create_dir("samsung-laptop", NULL);
762 if (!samsung->debug.root) {
763 pr_err("failed to create debugfs directory");
764 goto error_debugfs;
765 }
766
767 samsung->debug.f0000_wrapper.data = samsung->f0000_segment;
768 samsung->debug.f0000_wrapper.size = 0xffff;
769
770 samsung->debug.data_wrapper.data = &samsung->debug.data;
771 samsung->debug.data_wrapper.size = sizeof(samsung->debug.data);
772
773 dent = debugfs_create_u16("command", S_IRUGO | S_IWUSR,
774 samsung->debug.root, &samsung->debug.command);
775 if (!dent)
776 goto error_debugfs;
777
778 dent = debugfs_create_u32("d0", S_IRUGO | S_IWUSR, samsung->debug.root,
779 &samsung->debug.data.d0);
780 if (!dent)
781 goto error_debugfs;
782
783 dent = debugfs_create_u32("d1", S_IRUGO | S_IWUSR, samsung->debug.root,
784 &samsung->debug.data.d1);
785 if (!dent)
786 goto error_debugfs;
787
788 dent = debugfs_create_u16("d2", S_IRUGO | S_IWUSR, samsung->debug.root,
789 &samsung->debug.data.d2);
790 if (!dent)
791 goto error_debugfs;
792
793 dent = debugfs_create_u8("d3", S_IRUGO | S_IWUSR, samsung->debug.root,
794 &samsung->debug.data.d3);
795 if (!dent)
796 goto error_debugfs;
797
798 dent = debugfs_create_blob("data", S_IRUGO | S_IWUSR,
799 samsung->debug.root,
800 &samsung->debug.data_wrapper);
801 if (!dent)
802 goto error_debugfs;
803
804 dent = debugfs_create_blob("f0000_segment", S_IRUSR | S_IWUSR,
805 samsung->debug.root,
806 &samsung->debug.f0000_wrapper);
807 if (!dent)
808 goto error_debugfs;
809
810 dent = debugfs_create_file("call", S_IFREG | S_IRUGO,
811 samsung->debug.root, samsung,
812 &samsung_laptop_call_io_ops);
813 if (!dent)
814 goto error_debugfs;
815
816 return 0;
817
818error_debugfs:
819 samsung_debugfs_exit(samsung);
820 return -ENOMEM;
821}
822
Corentin Chary5dea7a22011-11-26 10:59:59 +0100823static void samsung_sabi_exit(struct samsung_laptop *samsung)
824{
825 const struct sabi_config *config = samsung->config;
826
827 /* Turn off "Linux" mode in the BIOS */
828 if (config && config->commands.set_linux != 0xff)
Corentin Chary7e960712011-11-26 11:00:02 +0100829 sabi_set_commandb(samsung, config->commands.set_linux, 0x80);
Corentin Chary5dea7a22011-11-26 10:59:59 +0100830
831 if (samsung->sabi_iface) {
832 iounmap(samsung->sabi_iface);
833 samsung->sabi_iface = NULL;
834 }
835 if (samsung->f0000_segment) {
836 iounmap(samsung->f0000_segment);
837 samsung->f0000_segment = NULL;
838 }
839
840 samsung->config = NULL;
841}
842
843static __init void samsung_sabi_infos(struct samsung_laptop *samsung, int loca)
844{
845 const struct sabi_config *config = samsung->config;
846
847 printk(KERN_DEBUG "This computer supports SABI==%x\n",
848 loca + 0xf0000 - 6);
849 printk(KERN_DEBUG "SABI header:\n");
850 printk(KERN_DEBUG " SMI Port Number = 0x%04x\n",
851 readw(samsung->sabi + config->header_offsets.port));
852 printk(KERN_DEBUG " SMI Interface Function = 0x%02x\n",
853 readb(samsung->sabi + config->header_offsets.iface_func));
854 printk(KERN_DEBUG " SMI enable memory buffer = 0x%02x\n",
855 readb(samsung->sabi + config->header_offsets.en_mem));
856 printk(KERN_DEBUG " SMI restore memory buffer = 0x%02x\n",
857 readb(samsung->sabi + config->header_offsets.re_mem));
858 printk(KERN_DEBUG " SABI data offset = 0x%04x\n",
859 readw(samsung->sabi + config->header_offsets.data_offset));
860 printk(KERN_DEBUG " SABI data segment = 0x%04x\n",
861 readw(samsung->sabi + config->header_offsets.data_segment));
862}
863
864static void __init samsung_sabi_selftest(struct samsung_laptop *samsung,
865 unsigned int ifaceP)
866{
867 const struct sabi_config *config = samsung->config;
Corentin Chary7e960712011-11-26 11:00:02 +0100868 struct sabi_data sretval;
Corentin Chary5dea7a22011-11-26 10:59:59 +0100869
870 printk(KERN_DEBUG "ifaceP = 0x%08x\n", ifaceP);
871 printk(KERN_DEBUG "sabi_iface = %p\n", samsung->sabi_iface);
872
Corentin Charyf34cd9c2011-11-26 11:00:00 +0100873 if (samsung->handle_backlight)
874 test_backlight(samsung);
Corentin Chary5dea7a22011-11-26 10:59:59 +0100875 test_wireless(samsung);
876
Corentin Chary7e960712011-11-26 11:00:02 +0100877 sabi_command(samsung, config->commands.get_brightness, NULL, &sretval);
878 printk(KERN_DEBUG "brightness = 0x%02x\n", sretval.data[0]);
Corentin Chary5dea7a22011-11-26 10:59:59 +0100879}
880
881static int __init samsung_sabi_init(struct samsung_laptop *samsung)
882{
883 const struct sabi_config *config = NULL;
884 const struct sabi_commands *commands;
885 unsigned int ifaceP;
886 int ret = 0;
887 int i;
888 int loca;
889
890 samsung->f0000_segment = ioremap_nocache(0xf0000, 0xffff);
891 if (!samsung->f0000_segment) {
892 pr_err("Can't map the segment at 0xf0000\n");
893 ret = -EINVAL;
894 goto exit;
895 }
896
897 /* Try to find one of the signatures in memory to find the header */
898 for (i = 0; sabi_configs[i].test_string != 0; ++i) {
899 samsung->config = &sabi_configs[i];
900 loca = find_signature(samsung->f0000_segment,
901 samsung->config->test_string);
902 if (loca != 0xffff)
903 break;
904 }
905
906 if (loca == 0xffff) {
907 pr_err("This computer does not support SABI\n");
908 ret = -ENODEV;
909 goto exit;
910 }
911
912 config = samsung->config;
913 commands = &config->commands;
914
915 /* point to the SMI port Number */
916 loca += 1;
917 samsung->sabi = (samsung->f0000_segment + loca);
918
919 if (debug)
920 samsung_sabi_infos(samsung, loca);
921
922 /* Get a pointer to the SABI Interface */
923 ifaceP = (readw(samsung->sabi + config->header_offsets.data_segment) & 0x0ffff) << 4;
924 ifaceP += readw(samsung->sabi + config->header_offsets.data_offset) & 0x0ffff;
925 samsung->sabi_iface = ioremap_nocache(ifaceP, 16);
926 if (!samsung->sabi_iface) {
927 pr_err("Can't remap %x\n", ifaceP);
928 ret = -EINVAL;
929 goto exit;
930 }
931
932 if (debug)
933 samsung_sabi_selftest(samsung, ifaceP);
934
935 /* Turn on "Linux" mode in the BIOS */
936 if (commands->set_linux != 0xff) {
Corentin Chary7e960712011-11-26 11:00:02 +0100937 int retval = sabi_set_commandb(samsung,
938 commands->set_linux, 0x81);
Corentin Chary5dea7a22011-11-26 10:59:59 +0100939 if (retval) {
940 pr_warn("Linux mode was not set!\n");
941 ret = -ENODEV;
942 goto exit;
943 }
944 }
945
946 /* Check for stepping quirk */
Corentin Charyf34cd9c2011-11-26 11:00:00 +0100947 if (samsung->handle_backlight)
948 check_for_stepping_quirk(samsung);
Corentin Chary5dea7a22011-11-26 10:59:59 +0100949
950exit:
951 if (ret)
952 samsung_sabi_exit(samsung);
953
954 return ret;
955}
956
957static void samsung_platform_exit(struct samsung_laptop *samsung)
958{
959 if (samsung->platform_device) {
960 platform_device_unregister(samsung->platform_device);
961 samsung->platform_device = NULL;
962 }
963}
964
965static int __init samsung_platform_init(struct samsung_laptop *samsung)
966{
967 struct platform_device *pdev;
968
969 pdev = platform_device_register_simple("samsung", -1, NULL, 0);
970 if (IS_ERR(pdev))
971 return PTR_ERR(pdev);
972
973 samsung->platform_device = pdev;
974 platform_set_drvdata(samsung->platform_device, samsung);
975 return 0;
976}
977
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500978static int __init dmi_check_cb(const struct dmi_system_id *id)
979{
Corentin Chary5dea7a22011-11-26 10:59:59 +0100980 pr_info("found laptop model '%s'\n", id->ident);
Axel Lin27836582011-03-14 18:56:18 +0800981 return 1;
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -0500982}
983
984static struct dmi_system_id __initdata samsung_dmi_table[] = {
985 {
986 .ident = "N128",
987 .matches = {
988 DMI_MATCH(DMI_SYS_VENDOR,
989 "SAMSUNG ELECTRONICS CO., LTD."),
990 DMI_MATCH(DMI_PRODUCT_NAME, "N128"),
991 DMI_MATCH(DMI_BOARD_NAME, "N128"),
992 },
993 .callback = dmi_check_cb,
994 },
995 {
996 .ident = "N130",
997 .matches = {
998 DMI_MATCH(DMI_SYS_VENDOR,
999 "SAMSUNG ELECTRONICS CO., LTD."),
1000 DMI_MATCH(DMI_PRODUCT_NAME, "N130"),
1001 DMI_MATCH(DMI_BOARD_NAME, "N130"),
1002 },
1003 .callback = dmi_check_cb,
1004 },
1005 {
J Witteveen4e2441c2011-07-03 13:15:44 +02001006 .ident = "N510",
1007 .matches = {
1008 DMI_MATCH(DMI_SYS_VENDOR,
1009 "SAMSUNG ELECTRONICS CO., LTD."),
1010 DMI_MATCH(DMI_PRODUCT_NAME, "N510"),
1011 DMI_MATCH(DMI_BOARD_NAME, "N510"),
1012 },
1013 .callback = dmi_check_cb,
1014 },
1015 {
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -05001016 .ident = "X125",
1017 .matches = {
1018 DMI_MATCH(DMI_SYS_VENDOR,
1019 "SAMSUNG ELECTRONICS CO., LTD."),
1020 DMI_MATCH(DMI_PRODUCT_NAME, "X125"),
1021 DMI_MATCH(DMI_BOARD_NAME, "X125"),
1022 },
1023 .callback = dmi_check_cb,
1024 },
1025 {
1026 .ident = "X120/X170",
1027 .matches = {
1028 DMI_MATCH(DMI_SYS_VENDOR,
1029 "SAMSUNG ELECTRONICS CO., LTD."),
1030 DMI_MATCH(DMI_PRODUCT_NAME, "X120/X170"),
1031 DMI_MATCH(DMI_BOARD_NAME, "X120/X170"),
1032 },
1033 .callback = dmi_check_cb,
1034 },
1035 {
1036 .ident = "NC10",
1037 .matches = {
1038 DMI_MATCH(DMI_SYS_VENDOR,
1039 "SAMSUNG ELECTRONICS CO., LTD."),
1040 DMI_MATCH(DMI_PRODUCT_NAME, "NC10"),
1041 DMI_MATCH(DMI_BOARD_NAME, "NC10"),
1042 },
1043 .callback = dmi_check_cb,
1044 },
1045 {
1046 .ident = "NP-Q45",
1047 .matches = {
1048 DMI_MATCH(DMI_SYS_VENDOR,
1049 "SAMSUNG ELECTRONICS CO., LTD."),
1050 DMI_MATCH(DMI_PRODUCT_NAME, "SQ45S70S"),
1051 DMI_MATCH(DMI_BOARD_NAME, "SQ45S70S"),
1052 },
1053 .callback = dmi_check_cb,
1054 },
1055 {
1056 .ident = "X360",
1057 .matches = {
1058 DMI_MATCH(DMI_SYS_VENDOR,
1059 "SAMSUNG ELECTRONICS CO., LTD."),
1060 DMI_MATCH(DMI_PRODUCT_NAME, "X360"),
1061 DMI_MATCH(DMI_BOARD_NAME, "X360"),
1062 },
1063 .callback = dmi_check_cb,
1064 },
1065 {
Alberto Mardegan3d536ed2011-04-08 17:02:03 +02001066 .ident = "R410 Plus",
1067 .matches = {
1068 DMI_MATCH(DMI_SYS_VENDOR,
1069 "SAMSUNG ELECTRONICS CO., LTD."),
1070 DMI_MATCH(DMI_PRODUCT_NAME, "R410P"),
1071 DMI_MATCH(DMI_BOARD_NAME, "R460"),
1072 },
1073 .callback = dmi_check_cb,
1074 },
1075 {
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -05001076 .ident = "R518",
1077 .matches = {
1078 DMI_MATCH(DMI_SYS_VENDOR,
1079 "SAMSUNG ELECTRONICS CO., LTD."),
1080 DMI_MATCH(DMI_PRODUCT_NAME, "R518"),
1081 DMI_MATCH(DMI_BOARD_NAME, "R518"),
1082 },
1083 .callback = dmi_check_cb,
1084 },
1085 {
1086 .ident = "R519/R719",
1087 .matches = {
1088 DMI_MATCH(DMI_SYS_VENDOR,
1089 "SAMSUNG ELECTRONICS CO., LTD."),
1090 DMI_MATCH(DMI_PRODUCT_NAME, "R519/R719"),
1091 DMI_MATCH(DMI_BOARD_NAME, "R519/R719"),
1092 },
1093 .callback = dmi_check_cb,
1094 },
1095 {
Thomas Courbon78a75392011-07-20 22:57:44 +02001096 .ident = "N150/N210/N220",
1097 .matches = {
1098 DMI_MATCH(DMI_SYS_VENDOR,
1099 "SAMSUNG ELECTRONICS CO., LTD."),
1100 DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220"),
1101 DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220"),
1102 },
1103 .callback = dmi_check_cb,
1104 },
1105 {
Raul Gutierrez Segalesf689c872011-09-20 09:16:15 -07001106 .ident = "N220",
1107 .matches = {
1108 DMI_MATCH(DMI_SYS_VENDOR,
1109 "SAMSUNG ELECTRONICS CO., LTD."),
1110 DMI_MATCH(DMI_PRODUCT_NAME, "N220"),
1111 DMI_MATCH(DMI_BOARD_NAME, "N220"),
1112 },
1113 .callback = dmi_check_cb,
1114 },
1115 {
Greg Kroah-Hartman10165072011-04-08 17:02:04 +02001116 .ident = "N150/N210/N220/N230",
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -05001117 .matches = {
1118 DMI_MATCH(DMI_SYS_VENDOR,
1119 "SAMSUNG ELECTRONICS CO., LTD."),
Greg Kroah-Hartman10165072011-04-08 17:02:04 +02001120 DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220/N230"),
1121 DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220/N230"),
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -05001122 },
1123 .callback = dmi_check_cb,
1124 },
1125 {
1126 .ident = "N150P/N210P/N220P",
1127 .matches = {
1128 DMI_MATCH(DMI_SYS_VENDOR,
1129 "SAMSUNG ELECTRONICS CO., LTD."),
1130 DMI_MATCH(DMI_PRODUCT_NAME, "N150P/N210P/N220P"),
1131 DMI_MATCH(DMI_BOARD_NAME, "N150P/N210P/N220P"),
1132 },
1133 .callback = dmi_check_cb,
1134 },
1135 {
Stefan Bellerf87d0292011-09-20 09:16:08 -07001136 .ident = "R700",
1137 .matches = {
1138 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
1139 DMI_MATCH(DMI_PRODUCT_NAME, "SR700"),
1140 DMI_MATCH(DMI_BOARD_NAME, "SR700"),
1141 },
1142 .callback = dmi_check_cb,
1143 },
1144 {
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -05001145 .ident = "R530/R730",
1146 .matches = {
1147 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
1148 DMI_MATCH(DMI_PRODUCT_NAME, "R530/R730"),
1149 DMI_MATCH(DMI_BOARD_NAME, "R530/R730"),
1150 },
1151 .callback = dmi_check_cb,
1152 },
1153 {
1154 .ident = "NF110/NF210/NF310",
1155 .matches = {
1156 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
1157 DMI_MATCH(DMI_PRODUCT_NAME, "NF110/NF210/NF310"),
1158 DMI_MATCH(DMI_BOARD_NAME, "NF110/NF210/NF310"),
1159 },
1160 .callback = dmi_check_cb,
1161 },
1162 {
1163 .ident = "N145P/N250P/N260P",
1164 .matches = {
1165 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
1166 DMI_MATCH(DMI_PRODUCT_NAME, "N145P/N250P/N260P"),
1167 DMI_MATCH(DMI_BOARD_NAME, "N145P/N250P/N260P"),
1168 },
1169 .callback = dmi_check_cb,
1170 },
1171 {
1172 .ident = "R70/R71",
1173 .matches = {
1174 DMI_MATCH(DMI_SYS_VENDOR,
1175 "SAMSUNG ELECTRONICS CO., LTD."),
1176 DMI_MATCH(DMI_PRODUCT_NAME, "R70/R71"),
1177 DMI_MATCH(DMI_BOARD_NAME, "R70/R71"),
1178 },
1179 .callback = dmi_check_cb,
1180 },
1181 {
1182 .ident = "P460",
1183 .matches = {
1184 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
1185 DMI_MATCH(DMI_PRODUCT_NAME, "P460"),
1186 DMI_MATCH(DMI_BOARD_NAME, "P460"),
1187 },
1188 .callback = dmi_check_cb,
1189 },
Smelov Andrey093ed562011-09-20 09:16:10 -07001190 {
1191 .ident = "R528/R728",
1192 .matches = {
1193 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
1194 DMI_MATCH(DMI_PRODUCT_NAME, "R528/R728"),
1195 DMI_MATCH(DMI_BOARD_NAME, "R528/R728"),
1196 },
1197 .callback = dmi_check_cb,
1198 },
Jason Stubbs7b3c2572011-09-20 09:16:14 -07001199 {
1200 .ident = "NC210/NC110",
1201 .matches = {
1202 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
1203 DMI_MATCH(DMI_PRODUCT_NAME, "NC210/NC110"),
1204 DMI_MATCH(DMI_BOARD_NAME, "NC210/NC110"),
1205 },
1206 .callback = dmi_check_cb,
1207 },
Tommaso Massimi7500eeb2011-09-20 09:16:09 -07001208 {
1209 .ident = "X520",
1210 .matches = {
1211 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
1212 DMI_MATCH(DMI_PRODUCT_NAME, "X520"),
1213 DMI_MATCH(DMI_BOARD_NAME, "X520"),
1214 },
1215 .callback = dmi_check_cb,
1216 },
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -05001217 { },
1218};
1219MODULE_DEVICE_TABLE(dmi, samsung_dmi_table);
1220
Corentin Chary5dea7a22011-11-26 10:59:59 +01001221static struct platform_device *samsung_platform_device;
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -05001222
1223static int __init samsung_init(void)
1224{
Corentin Chary5dea7a22011-11-26 10:59:59 +01001225 struct samsung_laptop *samsung;
1226 int ret;
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -05001227
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -05001228 if (!force && !dmi_check_system(samsung_dmi_table))
1229 return -ENODEV;
1230
Corentin Charya6df4892011-11-26 10:59:58 +01001231 samsung = kzalloc(sizeof(*samsung), GFP_KERNEL);
1232 if (!samsung)
1233 return -ENOMEM;
1234
1235 mutex_init(&samsung->sabi_mutex);
Corentin Charyf34cd9c2011-11-26 11:00:00 +01001236 samsung->handle_backlight = true;
1237
1238#ifdef CONFIG_ACPI
1239 /* Don't handle backlight here if the acpi video already handle it */
1240 if (acpi_video_backlight_support()) {
1241 pr_info("Backlight controlled by ACPI video driver\n");
1242 samsung->handle_backlight = false;
1243 }
1244#endif
Corentin Charya6df4892011-11-26 10:59:58 +01001245
Corentin Chary5dea7a22011-11-26 10:59:59 +01001246 ret = samsung_platform_init(samsung);
1247 if (ret)
1248 goto error_platform;
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -05001249
Corentin Chary5dea7a22011-11-26 10:59:59 +01001250 ret = samsung_sabi_init(samsung);
1251 if (ret)
1252 goto error_sabi;
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -05001253
Corentin Chary5dea7a22011-11-26 10:59:59 +01001254 ret = samsung_sysfs_init(samsung);
1255 if (ret)
1256 goto error_sysfs;
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -05001257
Corentin Chary5dea7a22011-11-26 10:59:59 +01001258 ret = samsung_backlight_init(samsung);
1259 if (ret)
1260 goto error_backlight;
Corentin Charya6df4892011-11-26 10:59:58 +01001261
Corentin Chary5dea7a22011-11-26 10:59:59 +01001262 ret = samsung_rfkill_init(samsung);
1263 if (ret)
1264 goto error_rfkill;
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -05001265
Corentin Chary5b80fc42011-11-26 11:00:03 +01001266 ret = samsung_debugfs_init(samsung);
1267 if (ret)
1268 goto error_debugfs;
1269
Corentin Chary5dea7a22011-11-26 10:59:59 +01001270 samsung_platform_device = samsung->platform_device;
1271 return ret;
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -05001272
Corentin Chary5b80fc42011-11-26 11:00:03 +01001273error_debugfs:
1274 samsung_rfkill_exit(samsung);
Corentin Chary5dea7a22011-11-26 10:59:59 +01001275error_rfkill:
1276 samsung_backlight_exit(samsung);
1277error_backlight:
1278 samsung_sysfs_exit(samsung);
1279error_sysfs:
1280 samsung_sabi_exit(samsung);
1281error_sabi:
1282 samsung_platform_exit(samsung);
1283error_platform:
Corentin Charya6df4892011-11-26 10:59:58 +01001284 kfree(samsung);
Corentin Chary5dea7a22011-11-26 10:59:59 +01001285 return ret;
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -05001286}
1287
1288static void __exit samsung_exit(void)
1289{
Corentin Chary5dea7a22011-11-26 10:59:59 +01001290 struct samsung_laptop *samsung;
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -05001291
Corentin Chary5dea7a22011-11-26 10:59:59 +01001292 samsung = platform_get_drvdata(samsung_platform_device);
Corentin Charya6df4892011-11-26 10:59:58 +01001293
Corentin Chary5b80fc42011-11-26 11:00:03 +01001294 samsung_debugfs_exit(samsung);
Corentin Chary5dea7a22011-11-26 10:59:59 +01001295 samsung_rfkill_exit(samsung);
1296 samsung_backlight_exit(samsung);
1297 samsung_sysfs_exit(samsung);
1298 samsung_sabi_exit(samsung);
1299 samsung_platform_exit(samsung);
1300
Corentin Charya6df4892011-11-26 10:59:58 +01001301 kfree(samsung);
Corentin Chary5dea7a22011-11-26 10:59:59 +01001302 samsung_platform_device = NULL;
Greg Kroah-Hartman2d70b732011-03-11 12:41:19 -05001303}
1304
1305module_init(samsung_init);
1306module_exit(samsung_exit);
1307
1308MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@suse.de>");
1309MODULE_DESCRIPTION("Samsung Backlight driver");
1310MODULE_LICENSE("GPL");