samsung-laptop.c 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549
  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. #include <linux/kernel.h>
  14. #include <linux/init.h>
  15. #include <linux/module.h>
  16. #include <linux/delay.h>
  17. #include <linux/pci.h>
  18. #include <linux/backlight.h>
  19. #include <linux/leds.h>
  20. #include <linux/fb.h>
  21. #include <linux/dmi.h>
  22. #include <linux/platform_device.h>
  23. #include <linux/rfkill.h>
  24. #include <linux/acpi.h>
  25. #include <linux/seq_file.h>
  26. #include <linux/debugfs.h>
  27. /*
  28. * This driver is needed because a number of Samsung laptops do not hook
  29. * their control settings through ACPI. So we have to poke around in the
  30. * BIOS to do things like brightness values, and "special" key controls.
  31. */
  32. /*
  33. * We have 0 - 8 as valid brightness levels. The specs say that level 0 should
  34. * be reserved by the BIOS (which really doesn't make much sense), we tell
  35. * userspace that the value is 0 - 7 and then just tell the hardware 1 - 8
  36. */
  37. #define MAX_BRIGHT 0x07
  38. #define SABI_IFACE_MAIN 0x00
  39. #define SABI_IFACE_SUB 0x02
  40. #define SABI_IFACE_COMPLETE 0x04
  41. #define SABI_IFACE_DATA 0x05
  42. /* Structure get/set data using sabi */
  43. struct sabi_data {
  44. union {
  45. struct {
  46. u32 d0;
  47. u32 d1;
  48. u16 d2;
  49. u8 d3;
  50. };
  51. u8 data[11];
  52. };
  53. };
  54. struct sabi_header_offsets {
  55. u8 port;
  56. u8 re_mem;
  57. u8 iface_func;
  58. u8 en_mem;
  59. u8 data_offset;
  60. u8 data_segment;
  61. };
  62. struct sabi_commands {
  63. /*
  64. * Brightness is 0 - 8, as described above.
  65. * Value 0 is for the BIOS to use
  66. */
  67. u16 get_brightness;
  68. u16 set_brightness;
  69. /*
  70. * first byte:
  71. * 0x00 - wireless is off
  72. * 0x01 - wireless is on
  73. * second byte:
  74. * 0x02 - 3G is off
  75. * 0x03 - 3G is on
  76. * TODO, verify 3G is correct, that doesn't seem right...
  77. */
  78. u16 get_wireless_button;
  79. u16 set_wireless_button;
  80. /* 0 is off, 1 is on */
  81. u16 get_backlight;
  82. u16 set_backlight;
  83. /*
  84. * 0x80 or 0x00 - no action
  85. * 0x81 - recovery key pressed
  86. */
  87. u16 get_recovery_mode;
  88. u16 set_recovery_mode;
  89. /*
  90. * on seclinux: 0 is low, 1 is high,
  91. * on swsmi: 0 is normal, 1 is silent, 2 is turbo
  92. */
  93. u16 get_performance_level;
  94. u16 set_performance_level;
  95. /* 0x80 is off, 0x81 is on */
  96. u16 get_battery_life_extender;
  97. u16 set_battery_life_extender;
  98. /* 0x80 is off, 0x81 is on */
  99. u16 get_usb_charge;
  100. u16 set_usb_charge;
  101. /* 0x81 to read, (0x82 | level << 8) to set, 0xaabb to enable */
  102. u16 kbd_backlight;
  103. /*
  104. * Tell the BIOS that Linux is running on this machine.
  105. * 81 is on, 80 is off
  106. */
  107. u16 set_linux;
  108. };
  109. struct sabi_performance_level {
  110. const char *name;
  111. u16 value;
  112. };
  113. struct sabi_config {
  114. const char *test_string;
  115. u16 main_function;
  116. const struct sabi_header_offsets header_offsets;
  117. const struct sabi_commands commands;
  118. const struct sabi_performance_level performance_levels[4];
  119. u8 min_brightness;
  120. u8 max_brightness;
  121. };
  122. static const struct sabi_config sabi_configs[] = {
  123. {
  124. .test_string = "SECLINUX",
  125. .main_function = 0x4c49,
  126. .header_offsets = {
  127. .port = 0x00,
  128. .re_mem = 0x02,
  129. .iface_func = 0x03,
  130. .en_mem = 0x04,
  131. .data_offset = 0x05,
  132. .data_segment = 0x07,
  133. },
  134. .commands = {
  135. .get_brightness = 0x00,
  136. .set_brightness = 0x01,
  137. .get_wireless_button = 0x02,
  138. .set_wireless_button = 0x03,
  139. .get_backlight = 0x04,
  140. .set_backlight = 0x05,
  141. .get_recovery_mode = 0x06,
  142. .set_recovery_mode = 0x07,
  143. .get_performance_level = 0x08,
  144. .set_performance_level = 0x09,
  145. .get_battery_life_extender = 0xFFFF,
  146. .set_battery_life_extender = 0xFFFF,
  147. .get_usb_charge = 0xFFFF,
  148. .set_usb_charge = 0xFFFF,
  149. .kbd_backlight = 0xFFFF,
  150. .set_linux = 0x0a,
  151. },
  152. .performance_levels = {
  153. {
  154. .name = "silent",
  155. .value = 0,
  156. },
  157. {
  158. .name = "normal",
  159. .value = 1,
  160. },
  161. { },
  162. },
  163. .min_brightness = 1,
  164. .max_brightness = 8,
  165. },
  166. {
  167. .test_string = "SwSmi@",
  168. .main_function = 0x5843,
  169. .header_offsets = {
  170. .port = 0x00,
  171. .re_mem = 0x04,
  172. .iface_func = 0x02,
  173. .en_mem = 0x03,
  174. .data_offset = 0x05,
  175. .data_segment = 0x07,
  176. },
  177. .commands = {
  178. .get_brightness = 0x10,
  179. .set_brightness = 0x11,
  180. .get_wireless_button = 0x12,
  181. .set_wireless_button = 0x13,
  182. .get_backlight = 0x2d,
  183. .set_backlight = 0x2e,
  184. .get_recovery_mode = 0xff,
  185. .set_recovery_mode = 0xff,
  186. .get_performance_level = 0x31,
  187. .set_performance_level = 0x32,
  188. .get_battery_life_extender = 0x65,
  189. .set_battery_life_extender = 0x66,
  190. .get_usb_charge = 0x67,
  191. .set_usb_charge = 0x68,
  192. .kbd_backlight = 0x78,
  193. .set_linux = 0xff,
  194. },
  195. .performance_levels = {
  196. {
  197. .name = "normal",
  198. .value = 0,
  199. },
  200. {
  201. .name = "silent",
  202. .value = 1,
  203. },
  204. {
  205. .name = "overclock",
  206. .value = 2,
  207. },
  208. { },
  209. },
  210. .min_brightness = 0,
  211. .max_brightness = 8,
  212. },
  213. { },
  214. };
  215. /*
  216. * samsung-laptop/ - debugfs root directory
  217. * f0000_segment - dump f0000 segment
  218. * command - current command
  219. * data - current data
  220. * d0, d1, d2, d3 - data fields
  221. * call - call SABI using command and data
  222. *
  223. * This allow to call arbitrary sabi commands wihout
  224. * modifying the driver at all.
  225. * For example, setting the keyboard backlight brightness to 5
  226. *
  227. * echo 0x78 > command
  228. * echo 0x0582 > d0
  229. * echo 0 > d1
  230. * echo 0 > d2
  231. * echo 0 > d3
  232. * cat call
  233. */
  234. struct samsung_laptop_debug {
  235. struct dentry *root;
  236. struct sabi_data data;
  237. u16 command;
  238. struct debugfs_blob_wrapper f0000_wrapper;
  239. struct debugfs_blob_wrapper data_wrapper;
  240. };
  241. struct samsung_laptop {
  242. const struct sabi_config *config;
  243. void __iomem *sabi;
  244. void __iomem *sabi_iface;
  245. void __iomem *f0000_segment;
  246. struct mutex sabi_mutex;
  247. struct platform_device *platform_device;
  248. struct backlight_device *backlight_device;
  249. struct rfkill *rfk;
  250. struct led_classdev kbd_led;
  251. int kbd_led_wk;
  252. struct workqueue_struct *led_workqueue;
  253. struct work_struct kbd_led_work;
  254. struct samsung_laptop_debug debug;
  255. bool handle_backlight;
  256. bool has_stepping_quirk;
  257. };
  258. static bool force;
  259. module_param(force, bool, 0);
  260. MODULE_PARM_DESC(force,
  261. "Disable the DMI check and forces the driver to be loaded");
  262. static bool debug;
  263. module_param(debug, bool, S_IRUGO | S_IWUSR);
  264. MODULE_PARM_DESC(debug, "Debug enabled or not");
  265. static int sabi_command(struct samsung_laptop *samsung, u16 command,
  266. struct sabi_data *in,
  267. struct sabi_data *out)
  268. {
  269. const struct sabi_config *config = samsung->config;
  270. int ret = 0;
  271. u16 port = readw(samsung->sabi + config->header_offsets.port);
  272. u8 complete, iface_data;
  273. mutex_lock(&samsung->sabi_mutex);
  274. if (debug) {
  275. if (in)
  276. pr_info("SABI 0x%04x {0x%08x, 0x%08x, 0x%04x, 0x%02x}",
  277. command, in->d0, in->d1, in->d2, in->d3);
  278. else
  279. pr_info("SABI 0x%04x", command);
  280. }
  281. /* enable memory to be able to write to it */
  282. outb(readb(samsung->sabi + config->header_offsets.en_mem), port);
  283. /* write out the command */
  284. writew(config->main_function, samsung->sabi_iface + SABI_IFACE_MAIN);
  285. writew(command, samsung->sabi_iface + SABI_IFACE_SUB);
  286. writeb(0, samsung->sabi_iface + SABI_IFACE_COMPLETE);
  287. if (in) {
  288. writel(in->d0, samsung->sabi_iface + SABI_IFACE_DATA);
  289. writel(in->d1, samsung->sabi_iface + SABI_IFACE_DATA + 4);
  290. writew(in->d2, samsung->sabi_iface + SABI_IFACE_DATA + 8);
  291. writeb(in->d3, samsung->sabi_iface + SABI_IFACE_DATA + 10);
  292. }
  293. outb(readb(samsung->sabi + config->header_offsets.iface_func), port);
  294. /* write protect memory to make it safe */
  295. outb(readb(samsung->sabi + config->header_offsets.re_mem), port);
  296. /* see if the command actually succeeded */
  297. complete = readb(samsung->sabi_iface + SABI_IFACE_COMPLETE);
  298. iface_data = readb(samsung->sabi_iface + SABI_IFACE_DATA);
  299. if (complete != 0xaa || iface_data == 0xff) {
  300. pr_warn("SABI command 0x%04x failed with"
  301. " completion flag 0x%02x and interface data 0x%02x",
  302. command, complete, iface_data);
  303. ret = -EINVAL;
  304. goto exit;
  305. }
  306. if (out) {
  307. out->d0 = readl(samsung->sabi_iface + SABI_IFACE_DATA);
  308. out->d1 = readl(samsung->sabi_iface + SABI_IFACE_DATA + 4);
  309. out->d2 = readw(samsung->sabi_iface + SABI_IFACE_DATA + 2);
  310. out->d3 = readb(samsung->sabi_iface + SABI_IFACE_DATA + 1);
  311. }
  312. if (debug && out) {
  313. pr_info("SABI {0x%08x, 0x%08x, 0x%04x, 0x%02x}",
  314. out->d0, out->d1, out->d2, out->d3);
  315. }
  316. exit:
  317. mutex_unlock(&samsung->sabi_mutex);
  318. return ret;
  319. }
  320. /* simple wrappers usable with most commands */
  321. static int sabi_set_commandb(struct samsung_laptop *samsung,
  322. u16 command, u8 data)
  323. {
  324. struct sabi_data in = { .d0 = 0, .d1 = 0, .d2 = 0, .d3 = 0 };
  325. in.data[0] = data;
  326. return sabi_command(samsung, command, &in, NULL);
  327. }
  328. static int read_brightness(struct samsung_laptop *samsung)
  329. {
  330. const struct sabi_config *config = samsung->config;
  331. const struct sabi_commands *commands = &samsung->config->commands;
  332. struct sabi_data sretval;
  333. int user_brightness = 0;
  334. int retval;
  335. retval = sabi_command(samsung, commands->get_brightness,
  336. NULL, &sretval);
  337. if (retval)
  338. return retval;
  339. user_brightness = sretval.data[0];
  340. if (user_brightness > config->min_brightness)
  341. user_brightness -= config->min_brightness;
  342. else
  343. user_brightness = 0;
  344. return user_brightness;
  345. }
  346. static void set_brightness(struct samsung_laptop *samsung, u8 user_brightness)
  347. {
  348. const struct sabi_config *config = samsung->config;
  349. const struct sabi_commands *commands = &samsung->config->commands;
  350. u8 user_level = user_brightness + config->min_brightness;
  351. if (samsung->has_stepping_quirk && user_level != 0) {
  352. /*
  353. * short circuit if the specified level is what's already set
  354. * to prevent the screen from flickering needlessly
  355. */
  356. if (user_brightness == read_brightness(samsung))
  357. return;
  358. sabi_set_commandb(samsung, commands->set_brightness, 0);
  359. }
  360. sabi_set_commandb(samsung, commands->set_brightness, user_level);
  361. }
  362. static int get_brightness(struct backlight_device *bd)
  363. {
  364. struct samsung_laptop *samsung = bl_get_data(bd);
  365. return read_brightness(samsung);
  366. }
  367. static void check_for_stepping_quirk(struct samsung_laptop *samsung)
  368. {
  369. int initial_level;
  370. int check_level;
  371. int orig_level = read_brightness(samsung);
  372. /*
  373. * Some laptops exhibit the strange behaviour of stepping toward
  374. * (rather than setting) the brightness except when changing to/from
  375. * brightness level 0. This behaviour is checked for here and worked
  376. * around in set_brightness.
  377. */
  378. if (orig_level == 0)
  379. set_brightness(samsung, 1);
  380. initial_level = read_brightness(samsung);
  381. if (initial_level <= 2)
  382. check_level = initial_level + 2;
  383. else
  384. check_level = initial_level - 2;
  385. samsung->has_stepping_quirk = false;
  386. set_brightness(samsung, check_level);
  387. if (read_brightness(samsung) != check_level) {
  388. samsung->has_stepping_quirk = true;
  389. pr_info("enabled workaround for brightness stepping quirk\n");
  390. }
  391. set_brightness(samsung, orig_level);
  392. }
  393. static int update_status(struct backlight_device *bd)
  394. {
  395. struct samsung_laptop *samsung = bl_get_data(bd);
  396. const struct sabi_commands *commands = &samsung->config->commands;
  397. set_brightness(samsung, bd->props.brightness);
  398. if (bd->props.power == FB_BLANK_UNBLANK)
  399. sabi_set_commandb(samsung, commands->set_backlight, 1);
  400. else
  401. sabi_set_commandb(samsung, commands->set_backlight, 0);
  402. return 0;
  403. }
  404. static const struct backlight_ops backlight_ops = {
  405. .get_brightness = get_brightness,
  406. .update_status = update_status,
  407. };
  408. static int rfkill_set(void *data, bool blocked)
  409. {
  410. struct samsung_laptop *samsung = data;
  411. const struct sabi_commands *commands = &samsung->config->commands;
  412. /* Do something with blocked...*/
  413. /*
  414. * blocked == false is on
  415. * blocked == true is off
  416. */
  417. if (blocked)
  418. sabi_set_commandb(samsung, commands->set_wireless_button, 0);
  419. else
  420. sabi_set_commandb(samsung, commands->set_wireless_button, 1);
  421. return 0;
  422. }
  423. static struct rfkill_ops rfkill_ops = {
  424. .set_block = rfkill_set,
  425. };
  426. static ssize_t get_performance_level(struct device *dev,
  427. struct device_attribute *attr, char *buf)
  428. {
  429. struct samsung_laptop *samsung = dev_get_drvdata(dev);
  430. const struct sabi_config *config = samsung->config;
  431. const struct sabi_commands *commands = &config->commands;
  432. struct sabi_data sretval;
  433. int retval;
  434. int i;
  435. /* Read the state */
  436. retval = sabi_command(samsung, commands->get_performance_level,
  437. NULL, &sretval);
  438. if (retval)
  439. return retval;
  440. /* The logic is backwards, yeah, lots of fun... */
  441. for (i = 0; config->performance_levels[i].name; ++i) {
  442. if (sretval.data[0] == config->performance_levels[i].value)
  443. return sprintf(buf, "%s\n", config->performance_levels[i].name);
  444. }
  445. return sprintf(buf, "%s\n", "unknown");
  446. }
  447. static ssize_t set_performance_level(struct device *dev,
  448. struct device_attribute *attr, const char *buf,
  449. size_t count)
  450. {
  451. struct samsung_laptop *samsung = dev_get_drvdata(dev);
  452. const struct sabi_config *config = samsung->config;
  453. const struct sabi_commands *commands = &config->commands;
  454. int i;
  455. if (count < 1)
  456. return count;
  457. for (i = 0; config->performance_levels[i].name; ++i) {
  458. const struct sabi_performance_level *level =
  459. &config->performance_levels[i];
  460. if (!strncasecmp(level->name, buf, strlen(level->name))) {
  461. sabi_set_commandb(samsung,
  462. commands->set_performance_level,
  463. level->value);
  464. break;
  465. }
  466. }
  467. if (!config->performance_levels[i].name)
  468. return -EINVAL;
  469. return count;
  470. }
  471. static DEVICE_ATTR(performance_level, S_IWUSR | S_IRUGO,
  472. get_performance_level, set_performance_level);
  473. static int read_battery_life_extender(struct samsung_laptop *samsung)
  474. {
  475. const struct sabi_commands *commands = &samsung->config->commands;
  476. struct sabi_data data;
  477. int retval;
  478. if (commands->get_battery_life_extender == 0xFFFF)
  479. return -ENODEV;
  480. memset(&data, 0, sizeof(data));
  481. data.data[0] = 0x80;
  482. retval = sabi_command(samsung, commands->get_battery_life_extender,
  483. &data, &data);
  484. if (retval)
  485. return retval;
  486. if (data.data[0] != 0 && data.data[0] != 1)
  487. return -ENODEV;
  488. return data.data[0];
  489. }
  490. static int write_battery_life_extender(struct samsung_laptop *samsung,
  491. int enabled)
  492. {
  493. const struct sabi_commands *commands = &samsung->config->commands;
  494. struct sabi_data data;
  495. memset(&data, 0, sizeof(data));
  496. data.data[0] = 0x80 | enabled;
  497. return sabi_command(samsung, commands->set_battery_life_extender,
  498. &data, NULL);
  499. }
  500. static ssize_t get_battery_life_extender(struct device *dev,
  501. struct device_attribute *attr,
  502. char *buf)
  503. {
  504. struct samsung_laptop *samsung = dev_get_drvdata(dev);
  505. int ret;
  506. ret = read_battery_life_extender(samsung);
  507. if (ret < 0)
  508. return ret;
  509. return sprintf(buf, "%d\n", ret);
  510. }
  511. static ssize_t set_battery_life_extender(struct device *dev,
  512. struct device_attribute *attr,
  513. const char *buf, size_t count)
  514. {
  515. struct samsung_laptop *samsung = dev_get_drvdata(dev);
  516. int ret, value;
  517. if (!count || sscanf(buf, "%i", &value) != 1)
  518. return -EINVAL;
  519. ret = write_battery_life_extender(samsung, !!value);
  520. if (ret < 0)
  521. return ret;
  522. return count;
  523. }
  524. static DEVICE_ATTR(battery_life_extender, S_IWUSR | S_IRUGO,
  525. get_battery_life_extender, set_battery_life_extender);
  526. static int read_usb_charge(struct samsung_laptop *samsung)
  527. {
  528. const struct sabi_commands *commands = &samsung->config->commands;
  529. struct sabi_data data;
  530. int retval;
  531. if (commands->get_usb_charge == 0xFFFF)
  532. return -ENODEV;
  533. memset(&data, 0, sizeof(data));
  534. data.data[0] = 0x80;
  535. retval = sabi_command(samsung, commands->get_usb_charge,
  536. &data, &data);
  537. if (retval)
  538. return retval;
  539. if (data.data[0] != 0 && data.data[0] != 1)
  540. return -ENODEV;
  541. return data.data[0];
  542. }
  543. static int write_usb_charge(struct samsung_laptop *samsung,
  544. int enabled)
  545. {
  546. const struct sabi_commands *commands = &samsung->config->commands;
  547. struct sabi_data data;
  548. memset(&data, 0, sizeof(data));
  549. data.data[0] = 0x80 | enabled;
  550. return sabi_command(samsung, commands->set_usb_charge,
  551. &data, NULL);
  552. }
  553. static ssize_t get_usb_charge(struct device *dev,
  554. struct device_attribute *attr,
  555. char *buf)
  556. {
  557. struct samsung_laptop *samsung = dev_get_drvdata(dev);
  558. int ret;
  559. ret = read_usb_charge(samsung);
  560. if (ret < 0)
  561. return ret;
  562. return sprintf(buf, "%d\n", ret);
  563. }
  564. static ssize_t set_usb_charge(struct device *dev,
  565. struct device_attribute *attr,
  566. const char *buf, size_t count)
  567. {
  568. struct samsung_laptop *samsung = dev_get_drvdata(dev);
  569. int ret, value;
  570. if (!count || sscanf(buf, "%i", &value) != 1)
  571. return -EINVAL;
  572. ret = write_usb_charge(samsung, !!value);
  573. if (ret < 0)
  574. return ret;
  575. return count;
  576. }
  577. static DEVICE_ATTR(usb_charge, S_IWUSR | S_IRUGO,
  578. get_usb_charge, set_usb_charge);
  579. static struct attribute *platform_attributes[] = {
  580. &dev_attr_performance_level.attr,
  581. &dev_attr_battery_life_extender.attr,
  582. &dev_attr_usb_charge.attr,
  583. NULL
  584. };
  585. static int find_signature(void __iomem *memcheck, const char *testStr)
  586. {
  587. int i = 0;
  588. int loca;
  589. for (loca = 0; loca < 0xffff; loca++) {
  590. char temp = readb(memcheck + loca);
  591. if (temp == testStr[i]) {
  592. if (i == strlen(testStr)-1)
  593. break;
  594. ++i;
  595. } else {
  596. i = 0;
  597. }
  598. }
  599. return loca;
  600. }
  601. static void samsung_rfkill_exit(struct samsung_laptop *samsung)
  602. {
  603. if (samsung->rfk) {
  604. rfkill_unregister(samsung->rfk);
  605. rfkill_destroy(samsung->rfk);
  606. samsung->rfk = NULL;
  607. }
  608. }
  609. static int __init samsung_rfkill_init(struct samsung_laptop *samsung)
  610. {
  611. int retval;
  612. samsung->rfk = rfkill_alloc("samsung-wifi",
  613. &samsung->platform_device->dev,
  614. RFKILL_TYPE_WLAN,
  615. &rfkill_ops, samsung);
  616. if (!samsung->rfk)
  617. return -ENOMEM;
  618. retval = rfkill_register(samsung->rfk);
  619. if (retval) {
  620. rfkill_destroy(samsung->rfk);
  621. samsung->rfk = NULL;
  622. return -ENODEV;
  623. }
  624. return 0;
  625. }
  626. static int kbd_backlight_enable(struct samsung_laptop *samsung)
  627. {
  628. const struct sabi_commands *commands = &samsung->config->commands;
  629. struct sabi_data data;
  630. int retval;
  631. if (commands->kbd_backlight == 0xFFFF)
  632. return -ENODEV;
  633. memset(&data, 0, sizeof(data));
  634. data.d0 = 0xaabb;
  635. retval = sabi_command(samsung, commands->kbd_backlight,
  636. &data, &data);
  637. if (retval)
  638. return retval;
  639. if (data.d0 != 0xccdd)
  640. return -ENODEV;
  641. return 0;
  642. }
  643. static int kbd_backlight_read(struct samsung_laptop *samsung)
  644. {
  645. const struct sabi_commands *commands = &samsung->config->commands;
  646. struct sabi_data data;
  647. int retval;
  648. memset(&data, 0, sizeof(data));
  649. data.data[0] = 0x81;
  650. retval = sabi_command(samsung, commands->kbd_backlight,
  651. &data, &data);
  652. if (retval)
  653. return retval;
  654. return data.data[0];
  655. }
  656. static int kbd_backlight_write(struct samsung_laptop *samsung, int brightness)
  657. {
  658. const struct sabi_commands *commands = &samsung->config->commands;
  659. struct sabi_data data;
  660. memset(&data, 0, sizeof(data));
  661. data.d0 = 0x82 | ((brightness & 0xFF) << 8);
  662. return sabi_command(samsung, commands->kbd_backlight,
  663. &data, NULL);
  664. }
  665. static void kbd_led_update(struct work_struct *work)
  666. {
  667. struct samsung_laptop *samsung;
  668. samsung = container_of(work, struct samsung_laptop, kbd_led_work);
  669. kbd_backlight_write(samsung, samsung->kbd_led_wk);
  670. }
  671. static void kbd_led_set(struct led_classdev *led_cdev,
  672. enum led_brightness value)
  673. {
  674. struct samsung_laptop *samsung;
  675. samsung = container_of(led_cdev, struct samsung_laptop, kbd_led);
  676. if (value > samsung->kbd_led.max_brightness)
  677. value = samsung->kbd_led.max_brightness;
  678. else if (value < 0)
  679. value = 0;
  680. samsung->kbd_led_wk = value;
  681. queue_work(samsung->led_workqueue, &samsung->kbd_led_work);
  682. }
  683. static enum led_brightness kbd_led_get(struct led_classdev *led_cdev)
  684. {
  685. struct samsung_laptop *samsung;
  686. samsung = container_of(led_cdev, struct samsung_laptop, kbd_led);
  687. return kbd_backlight_read(samsung);
  688. }
  689. static void samsung_leds_exit(struct samsung_laptop *samsung)
  690. {
  691. if (!IS_ERR_OR_NULL(samsung->kbd_led.dev))
  692. led_classdev_unregister(&samsung->kbd_led);
  693. if (samsung->led_workqueue)
  694. destroy_workqueue(samsung->led_workqueue);
  695. }
  696. static int __init samsung_leds_init(struct samsung_laptop *samsung)
  697. {
  698. int ret = 0;
  699. samsung->led_workqueue = create_singlethread_workqueue("led_workqueue");
  700. if (!samsung->led_workqueue)
  701. return -ENOMEM;
  702. if (kbd_backlight_enable(samsung) >= 0) {
  703. INIT_WORK(&samsung->kbd_led_work, kbd_led_update);
  704. samsung->kbd_led.name = "samsung::kbd_backlight";
  705. samsung->kbd_led.brightness_set = kbd_led_set;
  706. samsung->kbd_led.brightness_get = kbd_led_get;
  707. samsung->kbd_led.max_brightness = 8;
  708. ret = led_classdev_register(&samsung->platform_device->dev,
  709. &samsung->kbd_led);
  710. }
  711. if (ret)
  712. samsung_leds_exit(samsung);
  713. return ret;
  714. }
  715. static void samsung_backlight_exit(struct samsung_laptop *samsung)
  716. {
  717. if (samsung->backlight_device) {
  718. backlight_device_unregister(samsung->backlight_device);
  719. samsung->backlight_device = NULL;
  720. }
  721. }
  722. static int __init samsung_backlight_init(struct samsung_laptop *samsung)
  723. {
  724. struct backlight_device *bd;
  725. struct backlight_properties props;
  726. if (!samsung->handle_backlight)
  727. return 0;
  728. memset(&props, 0, sizeof(struct backlight_properties));
  729. props.type = BACKLIGHT_PLATFORM;
  730. props.max_brightness = samsung->config->max_brightness -
  731. samsung->config->min_brightness;
  732. bd = backlight_device_register("samsung",
  733. &samsung->platform_device->dev,
  734. samsung, &backlight_ops,
  735. &props);
  736. if (IS_ERR(bd))
  737. return PTR_ERR(bd);
  738. samsung->backlight_device = bd;
  739. samsung->backlight_device->props.brightness = read_brightness(samsung);
  740. samsung->backlight_device->props.power = FB_BLANK_UNBLANK;
  741. backlight_update_status(samsung->backlight_device);
  742. return 0;
  743. }
  744. static mode_t samsung_sysfs_is_visible(struct kobject *kobj,
  745. struct attribute *attr, int idx)
  746. {
  747. struct device *dev = container_of(kobj, struct device, kobj);
  748. struct platform_device *pdev = to_platform_device(dev);
  749. struct samsung_laptop *samsung = platform_get_drvdata(pdev);
  750. bool ok = true;
  751. if (attr == &dev_attr_performance_level.attr)
  752. ok = !!samsung->config->performance_levels[0].name;
  753. if (attr == &dev_attr_battery_life_extender.attr)
  754. ok = !!(read_battery_life_extender(samsung) >= 0);
  755. if (attr == &dev_attr_usb_charge.attr)
  756. ok = !!(read_usb_charge(samsung) >= 0);
  757. return ok ? attr->mode : 0;
  758. }
  759. static struct attribute_group platform_attribute_group = {
  760. .is_visible = samsung_sysfs_is_visible,
  761. .attrs = platform_attributes
  762. };
  763. static void samsung_sysfs_exit(struct samsung_laptop *samsung)
  764. {
  765. struct platform_device *device = samsung->platform_device;
  766. sysfs_remove_group(&device->dev.kobj, &platform_attribute_group);
  767. }
  768. static int __init samsung_sysfs_init(struct samsung_laptop *samsung)
  769. {
  770. struct platform_device *device = samsung->platform_device;
  771. return sysfs_create_group(&device->dev.kobj, &platform_attribute_group);
  772. }
  773. static int show_call(struct seq_file *m, void *data)
  774. {
  775. struct samsung_laptop *samsung = m->private;
  776. struct sabi_data *sdata = &samsung->debug.data;
  777. int ret;
  778. seq_printf(m, "SABI 0x%04x {0x%08x, 0x%08x, 0x%04x, 0x%02x}\n",
  779. samsung->debug.command,
  780. sdata->d0, sdata->d1, sdata->d2, sdata->d3);
  781. ret = sabi_command(samsung, samsung->debug.command, sdata, sdata);
  782. if (ret) {
  783. seq_printf(m, "SABI command 0x%04x failed\n",
  784. samsung->debug.command);
  785. return ret;
  786. }
  787. seq_printf(m, "SABI {0x%08x, 0x%08x, 0x%04x, 0x%02x}\n",
  788. sdata->d0, sdata->d1, sdata->d2, sdata->d3);
  789. return 0;
  790. }
  791. static int samsung_debugfs_open(struct inode *inode, struct file *file)
  792. {
  793. return single_open(file, show_call, inode->i_private);
  794. }
  795. static const struct file_operations samsung_laptop_call_io_ops = {
  796. .owner = THIS_MODULE,
  797. .open = samsung_debugfs_open,
  798. .read = seq_read,
  799. .llseek = seq_lseek,
  800. .release = single_release,
  801. };
  802. static void samsung_debugfs_exit(struct samsung_laptop *samsung)
  803. {
  804. debugfs_remove_recursive(samsung->debug.root);
  805. }
  806. static int samsung_debugfs_init(struct samsung_laptop *samsung)
  807. {
  808. struct dentry *dent;
  809. samsung->debug.root = debugfs_create_dir("samsung-laptop", NULL);
  810. if (!samsung->debug.root) {
  811. pr_err("failed to create debugfs directory");
  812. goto error_debugfs;
  813. }
  814. samsung->debug.f0000_wrapper.data = samsung->f0000_segment;
  815. samsung->debug.f0000_wrapper.size = 0xffff;
  816. samsung->debug.data_wrapper.data = &samsung->debug.data;
  817. samsung->debug.data_wrapper.size = sizeof(samsung->debug.data);
  818. dent = debugfs_create_u16("command", S_IRUGO | S_IWUSR,
  819. samsung->debug.root, &samsung->debug.command);
  820. if (!dent)
  821. goto error_debugfs;
  822. dent = debugfs_create_u32("d0", S_IRUGO | S_IWUSR, samsung->debug.root,
  823. &samsung->debug.data.d0);
  824. if (!dent)
  825. goto error_debugfs;
  826. dent = debugfs_create_u32("d1", S_IRUGO | S_IWUSR, samsung->debug.root,
  827. &samsung->debug.data.d1);
  828. if (!dent)
  829. goto error_debugfs;
  830. dent = debugfs_create_u16("d2", S_IRUGO | S_IWUSR, samsung->debug.root,
  831. &samsung->debug.data.d2);
  832. if (!dent)
  833. goto error_debugfs;
  834. dent = debugfs_create_u8("d3", S_IRUGO | S_IWUSR, samsung->debug.root,
  835. &samsung->debug.data.d3);
  836. if (!dent)
  837. goto error_debugfs;
  838. dent = debugfs_create_blob("data", S_IRUGO | S_IWUSR,
  839. samsung->debug.root,
  840. &samsung->debug.data_wrapper);
  841. if (!dent)
  842. goto error_debugfs;
  843. dent = debugfs_create_blob("f0000_segment", S_IRUSR | S_IWUSR,
  844. samsung->debug.root,
  845. &samsung->debug.f0000_wrapper);
  846. if (!dent)
  847. goto error_debugfs;
  848. dent = debugfs_create_file("call", S_IFREG | S_IRUGO,
  849. samsung->debug.root, samsung,
  850. &samsung_laptop_call_io_ops);
  851. if (!dent)
  852. goto error_debugfs;
  853. return 0;
  854. error_debugfs:
  855. samsung_debugfs_exit(samsung);
  856. return -ENOMEM;
  857. }
  858. static void samsung_sabi_exit(struct samsung_laptop *samsung)
  859. {
  860. const struct sabi_config *config = samsung->config;
  861. /* Turn off "Linux" mode in the BIOS */
  862. if (config && config->commands.set_linux != 0xff)
  863. sabi_set_commandb(samsung, config->commands.set_linux, 0x80);
  864. if (samsung->sabi_iface) {
  865. iounmap(samsung->sabi_iface);
  866. samsung->sabi_iface = NULL;
  867. }
  868. if (samsung->f0000_segment) {
  869. iounmap(samsung->f0000_segment);
  870. samsung->f0000_segment = NULL;
  871. }
  872. samsung->config = NULL;
  873. }
  874. static __init void samsung_sabi_infos(struct samsung_laptop *samsung, int loca,
  875. unsigned int ifaceP)
  876. {
  877. const struct sabi_config *config = samsung->config;
  878. printk(KERN_DEBUG "This computer supports SABI==%x\n",
  879. loca + 0xf0000 - 6);
  880. printk(KERN_DEBUG "SABI header:\n");
  881. printk(KERN_DEBUG " SMI Port Number = 0x%04x\n",
  882. readw(samsung->sabi + config->header_offsets.port));
  883. printk(KERN_DEBUG " SMI Interface Function = 0x%02x\n",
  884. readb(samsung->sabi + config->header_offsets.iface_func));
  885. printk(KERN_DEBUG " SMI enable memory buffer = 0x%02x\n",
  886. readb(samsung->sabi + config->header_offsets.en_mem));
  887. printk(KERN_DEBUG " SMI restore memory buffer = 0x%02x\n",
  888. readb(samsung->sabi + config->header_offsets.re_mem));
  889. printk(KERN_DEBUG " SABI data offset = 0x%04x\n",
  890. readw(samsung->sabi + config->header_offsets.data_offset));
  891. printk(KERN_DEBUG " SABI data segment = 0x%04x\n",
  892. readw(samsung->sabi + config->header_offsets.data_segment));
  893. printk(KERN_DEBUG " SABI pointer = 0x%08x\n", ifaceP);
  894. }
  895. static int __init samsung_sabi_init(struct samsung_laptop *samsung)
  896. {
  897. const struct sabi_config *config = NULL;
  898. const struct sabi_commands *commands;
  899. unsigned int ifaceP;
  900. int ret = 0;
  901. int i;
  902. int loca;
  903. samsung->f0000_segment = ioremap_nocache(0xf0000, 0xffff);
  904. if (!samsung->f0000_segment) {
  905. pr_err("Can't map the segment at 0xf0000\n");
  906. ret = -EINVAL;
  907. goto exit;
  908. }
  909. /* Try to find one of the signatures in memory to find the header */
  910. for (i = 0; sabi_configs[i].test_string != 0; ++i) {
  911. samsung->config = &sabi_configs[i];
  912. loca = find_signature(samsung->f0000_segment,
  913. samsung->config->test_string);
  914. if (loca != 0xffff)
  915. break;
  916. }
  917. if (loca == 0xffff) {
  918. pr_err("This computer does not support SABI\n");
  919. ret = -ENODEV;
  920. goto exit;
  921. }
  922. config = samsung->config;
  923. commands = &config->commands;
  924. /* point to the SMI port Number */
  925. loca += 1;
  926. samsung->sabi = (samsung->f0000_segment + loca);
  927. /* Get a pointer to the SABI Interface */
  928. ifaceP = (readw(samsung->sabi + config->header_offsets.data_segment) & 0x0ffff) << 4;
  929. ifaceP += readw(samsung->sabi + config->header_offsets.data_offset) & 0x0ffff;
  930. if (debug)
  931. samsung_sabi_infos(samsung, loca, ifaceP);
  932. samsung->sabi_iface = ioremap_nocache(ifaceP, 16);
  933. if (!samsung->sabi_iface) {
  934. pr_err("Can't remap %x\n", ifaceP);
  935. ret = -EINVAL;
  936. goto exit;
  937. }
  938. /* Turn on "Linux" mode in the BIOS */
  939. if (commands->set_linux != 0xff) {
  940. int retval = sabi_set_commandb(samsung,
  941. commands->set_linux, 0x81);
  942. if (retval) {
  943. pr_warn("Linux mode was not set!\n");
  944. ret = -ENODEV;
  945. goto exit;
  946. }
  947. }
  948. /* Check for stepping quirk */
  949. if (samsung->handle_backlight)
  950. check_for_stepping_quirk(samsung);
  951. exit:
  952. if (ret)
  953. samsung_sabi_exit(samsung);
  954. return ret;
  955. }
  956. static void samsung_platform_exit(struct samsung_laptop *samsung)
  957. {
  958. if (samsung->platform_device) {
  959. platform_device_unregister(samsung->platform_device);
  960. samsung->platform_device = NULL;
  961. }
  962. }
  963. static int __init samsung_platform_init(struct samsung_laptop *samsung)
  964. {
  965. struct platform_device *pdev;
  966. pdev = platform_device_register_simple("samsung", -1, NULL, 0);
  967. if (IS_ERR(pdev))
  968. return PTR_ERR(pdev);
  969. samsung->platform_device = pdev;
  970. platform_set_drvdata(samsung->platform_device, samsung);
  971. return 0;
  972. }
  973. static int __init dmi_check_cb(const struct dmi_system_id *id)
  974. {
  975. pr_info("found laptop model '%s'\n", id->ident);
  976. return 1;
  977. }
  978. static struct dmi_system_id __initdata samsung_dmi_table[] = {
  979. {
  980. .ident = "N128",
  981. .matches = {
  982. DMI_MATCH(DMI_SYS_VENDOR,
  983. "SAMSUNG ELECTRONICS CO., LTD."),
  984. DMI_MATCH(DMI_PRODUCT_NAME, "N128"),
  985. DMI_MATCH(DMI_BOARD_NAME, "N128"),
  986. },
  987. .callback = dmi_check_cb,
  988. },
  989. {
  990. .ident = "N130",
  991. .matches = {
  992. DMI_MATCH(DMI_SYS_VENDOR,
  993. "SAMSUNG ELECTRONICS CO., LTD."),
  994. DMI_MATCH(DMI_PRODUCT_NAME, "N130"),
  995. DMI_MATCH(DMI_BOARD_NAME, "N130"),
  996. },
  997. .callback = dmi_check_cb,
  998. },
  999. {
  1000. .ident = "N510",
  1001. .matches = {
  1002. DMI_MATCH(DMI_SYS_VENDOR,
  1003. "SAMSUNG ELECTRONICS CO., LTD."),
  1004. DMI_MATCH(DMI_PRODUCT_NAME, "N510"),
  1005. DMI_MATCH(DMI_BOARD_NAME, "N510"),
  1006. },
  1007. .callback = dmi_check_cb,
  1008. },
  1009. {
  1010. .ident = "X125",
  1011. .matches = {
  1012. DMI_MATCH(DMI_SYS_VENDOR,
  1013. "SAMSUNG ELECTRONICS CO., LTD."),
  1014. DMI_MATCH(DMI_PRODUCT_NAME, "X125"),
  1015. DMI_MATCH(DMI_BOARD_NAME, "X125"),
  1016. },
  1017. .callback = dmi_check_cb,
  1018. },
  1019. {
  1020. .ident = "X120/X170",
  1021. .matches = {
  1022. DMI_MATCH(DMI_SYS_VENDOR,
  1023. "SAMSUNG ELECTRONICS CO., LTD."),
  1024. DMI_MATCH(DMI_PRODUCT_NAME, "X120/X170"),
  1025. DMI_MATCH(DMI_BOARD_NAME, "X120/X170"),
  1026. },
  1027. .callback = dmi_check_cb,
  1028. },
  1029. {
  1030. .ident = "NC10",
  1031. .matches = {
  1032. DMI_MATCH(DMI_SYS_VENDOR,
  1033. "SAMSUNG ELECTRONICS CO., LTD."),
  1034. DMI_MATCH(DMI_PRODUCT_NAME, "NC10"),
  1035. DMI_MATCH(DMI_BOARD_NAME, "NC10"),
  1036. },
  1037. .callback = dmi_check_cb,
  1038. },
  1039. {
  1040. .ident = "NP-Q45",
  1041. .matches = {
  1042. DMI_MATCH(DMI_SYS_VENDOR,
  1043. "SAMSUNG ELECTRONICS CO., LTD."),
  1044. DMI_MATCH(DMI_PRODUCT_NAME, "SQ45S70S"),
  1045. DMI_MATCH(DMI_BOARD_NAME, "SQ45S70S"),
  1046. },
  1047. .callback = dmi_check_cb,
  1048. },
  1049. {
  1050. .ident = "X360",
  1051. .matches = {
  1052. DMI_MATCH(DMI_SYS_VENDOR,
  1053. "SAMSUNG ELECTRONICS CO., LTD."),
  1054. DMI_MATCH(DMI_PRODUCT_NAME, "X360"),
  1055. DMI_MATCH(DMI_BOARD_NAME, "X360"),
  1056. },
  1057. .callback = dmi_check_cb,
  1058. },
  1059. {
  1060. .ident = "R410 Plus",
  1061. .matches = {
  1062. DMI_MATCH(DMI_SYS_VENDOR,
  1063. "SAMSUNG ELECTRONICS CO., LTD."),
  1064. DMI_MATCH(DMI_PRODUCT_NAME, "R410P"),
  1065. DMI_MATCH(DMI_BOARD_NAME, "R460"),
  1066. },
  1067. .callback = dmi_check_cb,
  1068. },
  1069. {
  1070. .ident = "R518",
  1071. .matches = {
  1072. DMI_MATCH(DMI_SYS_VENDOR,
  1073. "SAMSUNG ELECTRONICS CO., LTD."),
  1074. DMI_MATCH(DMI_PRODUCT_NAME, "R518"),
  1075. DMI_MATCH(DMI_BOARD_NAME, "R518"),
  1076. },
  1077. .callback = dmi_check_cb,
  1078. },
  1079. {
  1080. .ident = "R519/R719",
  1081. .matches = {
  1082. DMI_MATCH(DMI_SYS_VENDOR,
  1083. "SAMSUNG ELECTRONICS CO., LTD."),
  1084. DMI_MATCH(DMI_PRODUCT_NAME, "R519/R719"),
  1085. DMI_MATCH(DMI_BOARD_NAME, "R519/R719"),
  1086. },
  1087. .callback = dmi_check_cb,
  1088. },
  1089. {
  1090. .ident = "N150/N210/N220",
  1091. .matches = {
  1092. DMI_MATCH(DMI_SYS_VENDOR,
  1093. "SAMSUNG ELECTRONICS CO., LTD."),
  1094. DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220"),
  1095. DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220"),
  1096. },
  1097. .callback = dmi_check_cb,
  1098. },
  1099. {
  1100. .ident = "N220",
  1101. .matches = {
  1102. DMI_MATCH(DMI_SYS_VENDOR,
  1103. "SAMSUNG ELECTRONICS CO., LTD."),
  1104. DMI_MATCH(DMI_PRODUCT_NAME, "N220"),
  1105. DMI_MATCH(DMI_BOARD_NAME, "N220"),
  1106. },
  1107. .callback = dmi_check_cb,
  1108. },
  1109. {
  1110. .ident = "N150/N210/N220/N230",
  1111. .matches = {
  1112. DMI_MATCH(DMI_SYS_VENDOR,
  1113. "SAMSUNG ELECTRONICS CO., LTD."),
  1114. DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220/N230"),
  1115. DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220/N230"),
  1116. },
  1117. .callback = dmi_check_cb,
  1118. },
  1119. {
  1120. .ident = "N150P/N210P/N220P",
  1121. .matches = {
  1122. DMI_MATCH(DMI_SYS_VENDOR,
  1123. "SAMSUNG ELECTRONICS CO., LTD."),
  1124. DMI_MATCH(DMI_PRODUCT_NAME, "N150P/N210P/N220P"),
  1125. DMI_MATCH(DMI_BOARD_NAME, "N150P/N210P/N220P"),
  1126. },
  1127. .callback = dmi_check_cb,
  1128. },
  1129. {
  1130. .ident = "R700",
  1131. .matches = {
  1132. DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
  1133. DMI_MATCH(DMI_PRODUCT_NAME, "SR700"),
  1134. DMI_MATCH(DMI_BOARD_NAME, "SR700"),
  1135. },
  1136. .callback = dmi_check_cb,
  1137. },
  1138. {
  1139. .ident = "R530/R730",
  1140. .matches = {
  1141. DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
  1142. DMI_MATCH(DMI_PRODUCT_NAME, "R530/R730"),
  1143. DMI_MATCH(DMI_BOARD_NAME, "R530/R730"),
  1144. },
  1145. .callback = dmi_check_cb,
  1146. },
  1147. {
  1148. .ident = "NF110/NF210/NF310",
  1149. .matches = {
  1150. DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
  1151. DMI_MATCH(DMI_PRODUCT_NAME, "NF110/NF210/NF310"),
  1152. DMI_MATCH(DMI_BOARD_NAME, "NF110/NF210/NF310"),
  1153. },
  1154. .callback = dmi_check_cb,
  1155. },
  1156. {
  1157. .ident = "N145P/N250P/N260P",
  1158. .matches = {
  1159. DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
  1160. DMI_MATCH(DMI_PRODUCT_NAME, "N145P/N250P/N260P"),
  1161. DMI_MATCH(DMI_BOARD_NAME, "N145P/N250P/N260P"),
  1162. },
  1163. .callback = dmi_check_cb,
  1164. },
  1165. {
  1166. .ident = "R70/R71",
  1167. .matches = {
  1168. DMI_MATCH(DMI_SYS_VENDOR,
  1169. "SAMSUNG ELECTRONICS CO., LTD."),
  1170. DMI_MATCH(DMI_PRODUCT_NAME, "R70/R71"),
  1171. DMI_MATCH(DMI_BOARD_NAME, "R70/R71"),
  1172. },
  1173. .callback = dmi_check_cb,
  1174. },
  1175. {
  1176. .ident = "P460",
  1177. .matches = {
  1178. DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
  1179. DMI_MATCH(DMI_PRODUCT_NAME, "P460"),
  1180. DMI_MATCH(DMI_BOARD_NAME, "P460"),
  1181. },
  1182. .callback = dmi_check_cb,
  1183. },
  1184. {
  1185. .ident = "R528/R728",
  1186. .matches = {
  1187. DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
  1188. DMI_MATCH(DMI_PRODUCT_NAME, "R528/R728"),
  1189. DMI_MATCH(DMI_BOARD_NAME, "R528/R728"),
  1190. },
  1191. .callback = dmi_check_cb,
  1192. },
  1193. {
  1194. .ident = "NC210/NC110",
  1195. .matches = {
  1196. DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
  1197. DMI_MATCH(DMI_PRODUCT_NAME, "NC210/NC110"),
  1198. DMI_MATCH(DMI_BOARD_NAME, "NC210/NC110"),
  1199. },
  1200. .callback = dmi_check_cb,
  1201. },
  1202. {
  1203. .ident = "X520",
  1204. .matches = {
  1205. DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
  1206. DMI_MATCH(DMI_PRODUCT_NAME, "X520"),
  1207. DMI_MATCH(DMI_BOARD_NAME, "X520"),
  1208. },
  1209. .callback = dmi_check_cb,
  1210. },
  1211. { },
  1212. };
  1213. MODULE_DEVICE_TABLE(dmi, samsung_dmi_table);
  1214. static struct platform_device *samsung_platform_device;
  1215. static int __init samsung_init(void)
  1216. {
  1217. struct samsung_laptop *samsung;
  1218. int ret;
  1219. if (!force && !dmi_check_system(samsung_dmi_table))
  1220. return -ENODEV;
  1221. samsung = kzalloc(sizeof(*samsung), GFP_KERNEL);
  1222. if (!samsung)
  1223. return -ENOMEM;
  1224. mutex_init(&samsung->sabi_mutex);
  1225. samsung->handle_backlight = true;
  1226. #ifdef CONFIG_ACPI
  1227. /* Don't handle backlight here if the acpi video already handle it */
  1228. if (acpi_video_backlight_support()) {
  1229. pr_info("Backlight controlled by ACPI video driver\n");
  1230. samsung->handle_backlight = false;
  1231. }
  1232. #endif
  1233. ret = samsung_platform_init(samsung);
  1234. if (ret)
  1235. goto error_platform;
  1236. ret = samsung_sabi_init(samsung);
  1237. if (ret)
  1238. goto error_sabi;
  1239. ret = samsung_sysfs_init(samsung);
  1240. if (ret)
  1241. goto error_sysfs;
  1242. ret = samsung_backlight_init(samsung);
  1243. if (ret)
  1244. goto error_backlight;
  1245. ret = samsung_rfkill_init(samsung);
  1246. if (ret)
  1247. goto error_rfkill;
  1248. ret = samsung_leds_init(samsung);
  1249. if (ret)
  1250. goto error_leds;
  1251. ret = samsung_debugfs_init(samsung);
  1252. if (ret)
  1253. goto error_debugfs;
  1254. samsung_platform_device = samsung->platform_device;
  1255. return ret;
  1256. error_debugfs:
  1257. samsung_leds_exit(samsung);
  1258. error_leds:
  1259. samsung_rfkill_exit(samsung);
  1260. error_rfkill:
  1261. samsung_backlight_exit(samsung);
  1262. error_backlight:
  1263. samsung_sysfs_exit(samsung);
  1264. error_sysfs:
  1265. samsung_sabi_exit(samsung);
  1266. error_sabi:
  1267. samsung_platform_exit(samsung);
  1268. error_platform:
  1269. kfree(samsung);
  1270. return ret;
  1271. }
  1272. static void __exit samsung_exit(void)
  1273. {
  1274. struct samsung_laptop *samsung;
  1275. samsung = platform_get_drvdata(samsung_platform_device);
  1276. samsung_debugfs_exit(samsung);
  1277. samsung_leds_exit(samsung);
  1278. samsung_rfkill_exit(samsung);
  1279. samsung_backlight_exit(samsung);
  1280. samsung_sysfs_exit(samsung);
  1281. samsung_sabi_exit(samsung);
  1282. samsung_platform_exit(samsung);
  1283. kfree(samsung);
  1284. samsung_platform_device = NULL;
  1285. }
  1286. module_init(samsung_init);
  1287. module_exit(samsung_exit);
  1288. MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@suse.de>");
  1289. MODULE_DESCRIPTION("Samsung Backlight driver");
  1290. MODULE_LICENSE("GPL");