samsung-laptop.c 33 KB

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