samsung-galaxybook.c 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Samsung Galaxy Book driver
  4. *
  5. * Copyright (c) 2025 Joshua Grisham <josh@joshuagrisham.com>
  6. *
  7. * With contributions to the SCAI ACPI device interface:
  8. * Copyright (c) 2024 Giulio Girardi <giulio.girardi@protechgroup.it>
  9. *
  10. * Implementation inspired by existing x86 platform drivers.
  11. * Thank you to the authors!
  12. */
  13. #include <linux/acpi.h>
  14. #include <linux/bits.h>
  15. #include <linux/err.h>
  16. #include <linux/i8042.h>
  17. #include <linux/init.h>
  18. #include <linux/input.h>
  19. #include <linux/kernel.h>
  20. #include <linux/leds.h>
  21. #include <linux/module.h>
  22. #include <linux/mutex.h>
  23. #include <linux/platform_device.h>
  24. #include <linux/platform_profile.h>
  25. #include <linux/serio.h>
  26. #include <linux/sysfs.h>
  27. #include <linux/uuid.h>
  28. #include <linux/workqueue.h>
  29. #include <acpi/battery.h>
  30. #include "firmware_attributes_class.h"
  31. #define DRIVER_NAME "samsung-galaxybook"
  32. struct samsung_galaxybook {
  33. struct platform_device *platform;
  34. struct acpi_device *acpi;
  35. struct device *fw_attrs_dev;
  36. struct kset *fw_attrs_kset;
  37. /* block in case firmware attributes are updated in multiple threads */
  38. struct mutex fw_attr_lock;
  39. bool has_kbd_backlight;
  40. bool has_block_recording;
  41. bool has_performance_mode;
  42. struct led_classdev kbd_backlight;
  43. struct work_struct kbd_backlight_hotkey_work;
  44. /* block in case brightness updated using hotkey and another thread */
  45. struct mutex kbd_backlight_lock;
  46. void *i8042_filter_ptr;
  47. struct work_struct block_recording_hotkey_work;
  48. struct input_dev *camera_lens_cover_switch;
  49. struct acpi_battery_hook battery_hook;
  50. u8 profile_performance_modes[PLATFORM_PROFILE_LAST];
  51. };
  52. enum galaxybook_fw_attr_id {
  53. GB_ATTR_POWER_ON_LID_OPEN,
  54. GB_ATTR_USB_CHARGING,
  55. GB_ATTR_BLOCK_RECORDING,
  56. };
  57. static const char * const galaxybook_fw_attr_name[] = {
  58. [GB_ATTR_POWER_ON_LID_OPEN] = "power_on_lid_open",
  59. [GB_ATTR_USB_CHARGING] = "usb_charging",
  60. [GB_ATTR_BLOCK_RECORDING] = "block_recording",
  61. };
  62. static const char * const galaxybook_fw_attr_desc[] = {
  63. [GB_ATTR_POWER_ON_LID_OPEN] = "Power On Lid Open",
  64. [GB_ATTR_USB_CHARGING] = "USB Charging",
  65. [GB_ATTR_BLOCK_RECORDING] = "Block Recording",
  66. };
  67. #define GB_ATTR_LANGUAGE_CODE "en_US.UTF-8"
  68. struct galaxybook_fw_attr {
  69. struct samsung_galaxybook *galaxybook;
  70. enum galaxybook_fw_attr_id fw_attr_id;
  71. struct attribute_group attr_group;
  72. struct kobj_attribute display_name;
  73. struct kobj_attribute current_value;
  74. int (*get_value)(struct samsung_galaxybook *galaxybook, bool *value);
  75. int (*set_value)(struct samsung_galaxybook *galaxybook, const bool value);
  76. };
  77. struct sawb {
  78. u16 safn;
  79. u16 sasb;
  80. u8 rflg;
  81. union {
  82. struct {
  83. u8 gunm;
  84. u8 guds[250];
  85. } __packed;
  86. struct {
  87. u8 caid[16];
  88. u8 fncn;
  89. u8 subn;
  90. u8 iob0;
  91. u8 iob1;
  92. u8 iob2;
  93. u8 iob3;
  94. u8 iob4;
  95. u8 iob5;
  96. u8 iob6;
  97. u8 iob7;
  98. u8 iob8;
  99. u8 iob9;
  100. } __packed;
  101. struct {
  102. u8 iob_prefix[18];
  103. u8 iobs[10];
  104. } __packed;
  105. } __packed;
  106. } __packed;
  107. #define GB_SAWB_LEN_SETTINGS 0x15
  108. #define GB_SAWB_LEN_PERFORMANCE_MODE 0x100
  109. #define GB_SAFN 0x5843
  110. #define GB_SASB_KBD_BACKLIGHT 0x78
  111. #define GB_SASB_POWER_MANAGEMENT 0x7a
  112. #define GB_SASB_USB_CHARGING_GET 0x67
  113. #define GB_SASB_USB_CHARGING_SET 0x68
  114. #define GB_SASB_NOTIFICATIONS 0x86
  115. #define GB_SASB_BLOCK_RECORDING 0x8a
  116. #define GB_SASB_PERFORMANCE_MODE 0x91
  117. #define GB_SAWB_RFLG_POS 4
  118. #define GB_SAWB_GB_GUNM_POS 5
  119. #define GB_RFLG_SUCCESS 0xaa
  120. #define GB_GUNM_FAIL 0xff
  121. #define GB_GUNM_FEATURE_ENABLE 0xbb
  122. #define GB_GUNM_FEATURE_ENABLE_SUCCESS 0xdd
  123. #define GB_GUDS_FEATURE_ENABLE 0xaa
  124. #define GB_GUDS_FEATURE_ENABLE_SUCCESS 0xcc
  125. #define GB_GUNM_GET 0x81
  126. #define GB_GUNM_SET 0x82
  127. #define GB_GUNM_POWER_MANAGEMENT 0x82
  128. #define GB_GUNM_USB_CHARGING_GET 0x80
  129. #define GB_GUNM_USB_CHARGING_ON 0x81
  130. #define GB_GUNM_USB_CHARGING_OFF 0x80
  131. #define GB_GUDS_POWER_ON_LID_OPEN 0xa3
  132. #define GB_GUDS_POWER_ON_LID_OPEN_GET 0x81
  133. #define GB_GUDS_POWER_ON_LID_OPEN_SET 0x80
  134. #define GB_GUDS_BATTERY_CHARGE_CONTROL 0xe9
  135. #define GB_GUDS_BATTERY_CHARGE_CONTROL_GET 0x91
  136. #define GB_GUDS_BATTERY_CHARGE_CONTROL_SET 0x90
  137. #define GB_GUNM_ACPI_NOTIFY_ENABLE 0x80
  138. #define GB_GUDS_ACPI_NOTIFY_ENABLE 0x02
  139. #define GB_BLOCK_RECORDING_ON 0x0
  140. #define GB_BLOCK_RECORDING_OFF 0x1
  141. #define GB_FNCN_PERFORMANCE_MODE 0x51
  142. #define GB_SUBN_PERFORMANCE_MODE_LIST 0x01
  143. #define GB_SUBN_PERFORMANCE_MODE_GET 0x02
  144. #define GB_SUBN_PERFORMANCE_MODE_SET 0x03
  145. /* guid 8246028d-8bca-4a55-ba0f-6f1e6b921b8f */
  146. static const guid_t performance_mode_guid =
  147. GUID_INIT(0x8246028d, 0x8bca, 0x4a55, 0xba, 0x0f, 0x6f, 0x1e, 0x6b, 0x92, 0x1b, 0x8f);
  148. #define GB_PERFORMANCE_MODE_GUID performance_mode_guid
  149. #define GB_PERFORMANCE_MODE_FANOFF 0xb
  150. #define GB_PERFORMANCE_MODE_LOWNOISE 0xa
  151. #define GB_PERFORMANCE_MODE_OPTIMIZED 0x0
  152. #define GB_PERFORMANCE_MODE_OPTIMIZED_V2 0x2
  153. #define GB_PERFORMANCE_MODE_PERFORMANCE 0x1
  154. #define GB_PERFORMANCE_MODE_PERFORMANCE_V2 0x15
  155. #define GB_PERFORMANCE_MODE_ULTRA 0x16
  156. #define GB_PERFORMANCE_MODE_IGNORE1 0x14
  157. #define GB_PERFORMANCE_MODE_IGNORE2 0xc
  158. #define GB_ACPI_METHOD_ENABLE "SDLS"
  159. #define GB_ACPI_METHOD_ENABLE_ON 1
  160. #define GB_ACPI_METHOD_ENABLE_OFF 0
  161. #define GB_ACPI_METHOD_SETTINGS "CSFI"
  162. #define GB_ACPI_METHOD_PERFORMANCE_MODE "CSXI"
  163. #define GB_KBD_BACKLIGHT_MAX_BRIGHTNESS 3
  164. #define GB_ACPI_NOTIFY_BATTERY_STATE_CHANGED 0x61
  165. #define GB_ACPI_NOTIFY_DEVICE_ON_TABLE 0x6c
  166. #define GB_ACPI_NOTIFY_DEVICE_OFF_TABLE 0x6d
  167. #define GB_ACPI_NOTIFY_HOTKEY_PERFORMANCE_MODE 0x70
  168. #define GB_KEY_KBD_BACKLIGHT_KEYDOWN 0x2c
  169. #define GB_KEY_KBD_BACKLIGHT_KEYUP 0xac
  170. #define GB_KEY_BLOCK_RECORDING_KEYDOWN 0x1f
  171. #define GB_KEY_BLOCK_RECORDING_KEYUP 0x9f
  172. #define GB_KEY_BATTERY_NOTIFY_KEYUP 0xf
  173. #define GB_KEY_BATTERY_NOTIFY_KEYDOWN 0x8f
  174. /*
  175. * Optional features which have been determined as not supported on a particular
  176. * device will return GB_NOT_SUPPORTED from their init function. Positive
  177. * EOPNOTSUPP is used as the underlying value instead of negative to
  178. * differentiate this return code from valid upstream failures.
  179. */
  180. #define GB_NOT_SUPPORTED EOPNOTSUPP /* Galaxy Book feature not supported */
  181. /*
  182. * ACPI method handling
  183. */
  184. static int galaxybook_acpi_method(struct samsung_galaxybook *galaxybook, acpi_string method,
  185. struct sawb *buf, size_t len)
  186. {
  187. struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
  188. union acpi_object in_obj, *out_obj;
  189. struct acpi_object_list input;
  190. acpi_status status;
  191. int err;
  192. in_obj.type = ACPI_TYPE_BUFFER;
  193. in_obj.buffer.length = len;
  194. in_obj.buffer.pointer = (u8 *)buf;
  195. input.count = 1;
  196. input.pointer = &in_obj;
  197. status = acpi_evaluate_object_typed(galaxybook->acpi->handle, method, &input, &output,
  198. ACPI_TYPE_BUFFER);
  199. if (ACPI_FAILURE(status)) {
  200. dev_err(&galaxybook->acpi->dev, "failed to execute method %s; got %s\n",
  201. method, acpi_format_exception(status));
  202. return -EIO;
  203. }
  204. out_obj = output.pointer;
  205. if (out_obj->buffer.length != len || out_obj->buffer.length < GB_SAWB_GB_GUNM_POS + 1) {
  206. dev_err(&galaxybook->acpi->dev,
  207. "failed to execute %s; response length mismatch\n",
  208. method);
  209. err = -EPROTO;
  210. goto out_free;
  211. }
  212. if (out_obj->buffer.pointer[GB_SAWB_RFLG_POS] != GB_RFLG_SUCCESS) {
  213. dev_err(&galaxybook->acpi->dev,
  214. "failed to execute %s; device did not respond with success code 0x%x\n",
  215. method, GB_RFLG_SUCCESS);
  216. err = -ENXIO;
  217. goto out_free;
  218. }
  219. if (out_obj->buffer.pointer[GB_SAWB_GB_GUNM_POS] == GB_GUNM_FAIL) {
  220. dev_err(&galaxybook->acpi->dev,
  221. "failed to execute %s; device responded with failure code 0x%x\n",
  222. method, GB_GUNM_FAIL);
  223. err = -ENXIO;
  224. goto out_free;
  225. }
  226. memcpy(buf, out_obj->buffer.pointer, len);
  227. err = 0;
  228. out_free:
  229. kfree(out_obj);
  230. return err;
  231. }
  232. static int galaxybook_enable_acpi_feature(struct samsung_galaxybook *galaxybook, const u16 sasb)
  233. {
  234. struct sawb buf = {};
  235. int err;
  236. buf.safn = GB_SAFN;
  237. buf.sasb = sasb;
  238. buf.gunm = GB_GUNM_FEATURE_ENABLE;
  239. buf.guds[0] = GB_GUDS_FEATURE_ENABLE;
  240. err = galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS,
  241. &buf, GB_SAWB_LEN_SETTINGS);
  242. if (err)
  243. return err;
  244. if (buf.gunm != GB_GUNM_FEATURE_ENABLE_SUCCESS &&
  245. buf.guds[0] != GB_GUDS_FEATURE_ENABLE_SUCCESS)
  246. return -ENODEV;
  247. return 0;
  248. }
  249. /*
  250. * Keyboard Backlight
  251. */
  252. static int kbd_backlight_acpi_get(struct samsung_galaxybook *galaxybook,
  253. enum led_brightness *brightness)
  254. {
  255. struct sawb buf = {};
  256. int err;
  257. buf.safn = GB_SAFN;
  258. buf.sasb = GB_SASB_KBD_BACKLIGHT;
  259. buf.gunm = GB_GUNM_GET;
  260. err = galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS,
  261. &buf, GB_SAWB_LEN_SETTINGS);
  262. if (err)
  263. return err;
  264. *brightness = buf.gunm;
  265. return 0;
  266. }
  267. static int kbd_backlight_acpi_set(struct samsung_galaxybook *galaxybook,
  268. const enum led_brightness brightness)
  269. {
  270. struct sawb buf = {};
  271. buf.safn = GB_SAFN;
  272. buf.sasb = GB_SASB_KBD_BACKLIGHT;
  273. buf.gunm = GB_GUNM_SET;
  274. buf.guds[0] = brightness;
  275. return galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS,
  276. &buf, GB_SAWB_LEN_SETTINGS);
  277. }
  278. static enum led_brightness kbd_backlight_show(struct led_classdev *led)
  279. {
  280. struct samsung_galaxybook *galaxybook =
  281. container_of(led, struct samsung_galaxybook, kbd_backlight);
  282. enum led_brightness brightness;
  283. int err;
  284. err = kbd_backlight_acpi_get(galaxybook, &brightness);
  285. if (err)
  286. return err;
  287. return brightness;
  288. }
  289. static int kbd_backlight_store(struct led_classdev *led,
  290. const enum led_brightness brightness)
  291. {
  292. struct samsung_galaxybook *galaxybook =
  293. container_of_const(led, struct samsung_galaxybook, kbd_backlight);
  294. return kbd_backlight_acpi_set(galaxybook, brightness);
  295. }
  296. static int galaxybook_kbd_backlight_init(struct samsung_galaxybook *galaxybook)
  297. {
  298. struct led_init_data init_data = {};
  299. enum led_brightness brightness;
  300. int err;
  301. err = devm_mutex_init(&galaxybook->platform->dev, &galaxybook->kbd_backlight_lock);
  302. if (err)
  303. return err;
  304. err = galaxybook_enable_acpi_feature(galaxybook, GB_SASB_KBD_BACKLIGHT);
  305. if (err) {
  306. dev_dbg(&galaxybook->platform->dev,
  307. "failed to enable kbd_backlight feature, error %d\n", err);
  308. return GB_NOT_SUPPORTED;
  309. }
  310. err = kbd_backlight_acpi_get(galaxybook, &brightness);
  311. if (err) {
  312. dev_dbg(&galaxybook->platform->dev,
  313. "failed to get initial kbd_backlight brightness, error %d\n", err);
  314. return GB_NOT_SUPPORTED;
  315. }
  316. init_data.devicename = DRIVER_NAME;
  317. init_data.default_label = ":" LED_FUNCTION_KBD_BACKLIGHT;
  318. init_data.devname_mandatory = true;
  319. galaxybook->kbd_backlight.brightness_get = kbd_backlight_show;
  320. galaxybook->kbd_backlight.brightness_set_blocking = kbd_backlight_store;
  321. galaxybook->kbd_backlight.flags = LED_BRIGHT_HW_CHANGED;
  322. galaxybook->kbd_backlight.max_brightness = GB_KBD_BACKLIGHT_MAX_BRIGHTNESS;
  323. return devm_led_classdev_register_ext(&galaxybook->platform->dev,
  324. &galaxybook->kbd_backlight, &init_data);
  325. }
  326. /*
  327. * Battery Extension (adds charge_control_end_threshold to the battery device)
  328. */
  329. static int charge_control_end_threshold_acpi_get(struct samsung_galaxybook *galaxybook, u8 *value)
  330. {
  331. struct sawb buf = {};
  332. int err;
  333. buf.safn = GB_SAFN;
  334. buf.sasb = GB_SASB_POWER_MANAGEMENT;
  335. buf.gunm = GB_GUNM_POWER_MANAGEMENT;
  336. buf.guds[0] = GB_GUDS_BATTERY_CHARGE_CONTROL;
  337. buf.guds[1] = GB_GUDS_BATTERY_CHARGE_CONTROL_GET;
  338. err = galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS,
  339. &buf, GB_SAWB_LEN_SETTINGS);
  340. if (err)
  341. return err;
  342. *value = buf.guds[1];
  343. return 0;
  344. }
  345. static int charge_control_end_threshold_acpi_set(struct samsung_galaxybook *galaxybook, u8 value)
  346. {
  347. struct sawb buf = {};
  348. buf.safn = GB_SAFN;
  349. buf.sasb = GB_SASB_POWER_MANAGEMENT;
  350. buf.gunm = GB_GUNM_POWER_MANAGEMENT;
  351. buf.guds[0] = GB_GUDS_BATTERY_CHARGE_CONTROL;
  352. buf.guds[1] = GB_GUDS_BATTERY_CHARGE_CONTROL_SET;
  353. buf.guds[2] = value;
  354. return galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS,
  355. &buf, GB_SAWB_LEN_SETTINGS);
  356. }
  357. static int galaxybook_battery_ext_property_get(struct power_supply *psy,
  358. const struct power_supply_ext *ext,
  359. void *ext_data,
  360. enum power_supply_property psp,
  361. union power_supply_propval *val)
  362. {
  363. struct samsung_galaxybook *galaxybook = ext_data;
  364. u8 value;
  365. int err;
  366. if (psp != POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD)
  367. return -EINVAL;
  368. err = charge_control_end_threshold_acpi_get(galaxybook, &value);
  369. if (err)
  370. return err;
  371. /*
  372. * device stores "no end threshold" as 0 instead of 100;
  373. * if device has 0, report 100
  374. */
  375. if (value == 0)
  376. value = 100;
  377. val->intval = value;
  378. return 0;
  379. }
  380. static int galaxybook_battery_ext_property_set(struct power_supply *psy,
  381. const struct power_supply_ext *ext,
  382. void *ext_data,
  383. enum power_supply_property psp,
  384. const union power_supply_propval *val)
  385. {
  386. struct samsung_galaxybook *galaxybook = ext_data;
  387. u8 value;
  388. if (psp != POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD)
  389. return -EINVAL;
  390. value = val->intval;
  391. if (value < 1 || value > 100)
  392. return -EINVAL;
  393. /*
  394. * device stores "no end threshold" as 0 instead of 100;
  395. * if setting to 100, send 0
  396. */
  397. if (value == 100)
  398. value = 0;
  399. return charge_control_end_threshold_acpi_set(galaxybook, value);
  400. }
  401. static int galaxybook_battery_ext_property_is_writeable(struct power_supply *psy,
  402. const struct power_supply_ext *ext,
  403. void *ext_data,
  404. enum power_supply_property psp)
  405. {
  406. if (psp == POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD)
  407. return true;
  408. return false;
  409. }
  410. static const enum power_supply_property galaxybook_battery_properties[] = {
  411. POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD,
  412. };
  413. static const struct power_supply_ext galaxybook_battery_ext = {
  414. .name = DRIVER_NAME,
  415. .properties = galaxybook_battery_properties,
  416. .num_properties = ARRAY_SIZE(galaxybook_battery_properties),
  417. .get_property = galaxybook_battery_ext_property_get,
  418. .set_property = galaxybook_battery_ext_property_set,
  419. .property_is_writeable = galaxybook_battery_ext_property_is_writeable,
  420. };
  421. static int galaxybook_battery_add(struct power_supply *battery, struct acpi_battery_hook *hook)
  422. {
  423. struct samsung_galaxybook *galaxybook =
  424. container_of(hook, struct samsung_galaxybook, battery_hook);
  425. return power_supply_register_extension(battery, &galaxybook_battery_ext,
  426. &battery->dev, galaxybook);
  427. }
  428. static int galaxybook_battery_remove(struct power_supply *battery, struct acpi_battery_hook *hook)
  429. {
  430. power_supply_unregister_extension(battery, &galaxybook_battery_ext);
  431. return 0;
  432. }
  433. static int galaxybook_battery_threshold_init(struct samsung_galaxybook *galaxybook)
  434. {
  435. u8 value;
  436. int err;
  437. err = charge_control_end_threshold_acpi_get(galaxybook, &value);
  438. if (err) {
  439. dev_dbg(&galaxybook->platform->dev,
  440. "failed to get initial battery charge end threshold, error %d\n", err);
  441. return 0;
  442. }
  443. galaxybook->battery_hook.add_battery = galaxybook_battery_add;
  444. galaxybook->battery_hook.remove_battery = galaxybook_battery_remove;
  445. galaxybook->battery_hook.name = "Samsung Galaxy Book Battery Extension";
  446. return devm_battery_hook_register(&galaxybook->platform->dev, &galaxybook->battery_hook);
  447. }
  448. /*
  449. * Platform Profile / Performance mode
  450. */
  451. static int performance_mode_acpi_get(struct samsung_galaxybook *galaxybook, u8 *performance_mode)
  452. {
  453. struct sawb buf = {};
  454. int err;
  455. buf.safn = GB_SAFN;
  456. buf.sasb = GB_SASB_PERFORMANCE_MODE;
  457. export_guid(buf.caid, &GB_PERFORMANCE_MODE_GUID);
  458. buf.fncn = GB_FNCN_PERFORMANCE_MODE;
  459. buf.subn = GB_SUBN_PERFORMANCE_MODE_GET;
  460. err = galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_PERFORMANCE_MODE,
  461. &buf, GB_SAWB_LEN_PERFORMANCE_MODE);
  462. if (err)
  463. return err;
  464. *performance_mode = buf.iob0;
  465. return 0;
  466. }
  467. static int performance_mode_acpi_set(struct samsung_galaxybook *galaxybook,
  468. const u8 performance_mode)
  469. {
  470. struct sawb buf = {};
  471. buf.safn = GB_SAFN;
  472. buf.sasb = GB_SASB_PERFORMANCE_MODE;
  473. export_guid(buf.caid, &GB_PERFORMANCE_MODE_GUID);
  474. buf.fncn = GB_FNCN_PERFORMANCE_MODE;
  475. buf.subn = GB_SUBN_PERFORMANCE_MODE_SET;
  476. buf.iob0 = performance_mode;
  477. return galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_PERFORMANCE_MODE,
  478. &buf, GB_SAWB_LEN_PERFORMANCE_MODE);
  479. }
  480. static int get_performance_mode_profile(struct samsung_galaxybook *galaxybook,
  481. const u8 performance_mode,
  482. enum platform_profile_option *profile)
  483. {
  484. switch (performance_mode) {
  485. case GB_PERFORMANCE_MODE_FANOFF:
  486. *profile = PLATFORM_PROFILE_LOW_POWER;
  487. break;
  488. case GB_PERFORMANCE_MODE_LOWNOISE:
  489. *profile = PLATFORM_PROFILE_QUIET;
  490. break;
  491. case GB_PERFORMANCE_MODE_OPTIMIZED:
  492. case GB_PERFORMANCE_MODE_OPTIMIZED_V2:
  493. *profile = PLATFORM_PROFILE_BALANCED;
  494. break;
  495. case GB_PERFORMANCE_MODE_PERFORMANCE:
  496. case GB_PERFORMANCE_MODE_PERFORMANCE_V2:
  497. case GB_PERFORMANCE_MODE_ULTRA:
  498. *profile = PLATFORM_PROFILE_PERFORMANCE;
  499. break;
  500. case GB_PERFORMANCE_MODE_IGNORE1:
  501. case GB_PERFORMANCE_MODE_IGNORE2:
  502. return -EOPNOTSUPP;
  503. default:
  504. dev_warn(&galaxybook->platform->dev,
  505. "unrecognized performance mode 0x%x\n", performance_mode);
  506. return -EOPNOTSUPP;
  507. }
  508. return 0;
  509. }
  510. static int galaxybook_platform_profile_get(struct device *dev,
  511. enum platform_profile_option *profile)
  512. {
  513. struct samsung_galaxybook *galaxybook = dev_get_drvdata(dev);
  514. u8 performance_mode;
  515. int err;
  516. err = performance_mode_acpi_get(galaxybook, &performance_mode);
  517. if (err)
  518. return err;
  519. return get_performance_mode_profile(galaxybook, performance_mode, profile);
  520. }
  521. static int galaxybook_platform_profile_set(struct device *dev,
  522. enum platform_profile_option profile)
  523. {
  524. struct samsung_galaxybook *galaxybook = dev_get_drvdata(dev);
  525. return performance_mode_acpi_set(galaxybook,
  526. galaxybook->profile_performance_modes[profile]);
  527. }
  528. static int galaxybook_platform_profile_probe(void *drvdata, unsigned long *choices)
  529. {
  530. struct samsung_galaxybook *galaxybook = drvdata;
  531. u8 *perfmodes = galaxybook->profile_performance_modes;
  532. enum platform_profile_option profile;
  533. struct sawb buf = {};
  534. unsigned int i;
  535. int err;
  536. buf.safn = GB_SAFN;
  537. buf.sasb = GB_SASB_PERFORMANCE_MODE;
  538. export_guid(buf.caid, &GB_PERFORMANCE_MODE_GUID);
  539. buf.fncn = GB_FNCN_PERFORMANCE_MODE;
  540. buf.subn = GB_SUBN_PERFORMANCE_MODE_LIST;
  541. err = galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_PERFORMANCE_MODE,
  542. &buf, GB_SAWB_LEN_PERFORMANCE_MODE);
  543. if (err) {
  544. dev_dbg(&galaxybook->platform->dev,
  545. "failed to get supported performance modes, error %d\n", err);
  546. return err;
  547. }
  548. /* set initial default profile performance mode values */
  549. perfmodes[PLATFORM_PROFILE_LOW_POWER] = GB_PERFORMANCE_MODE_FANOFF;
  550. perfmodes[PLATFORM_PROFILE_QUIET] = GB_PERFORMANCE_MODE_LOWNOISE;
  551. perfmodes[PLATFORM_PROFILE_BALANCED] = GB_PERFORMANCE_MODE_OPTIMIZED;
  552. perfmodes[PLATFORM_PROFILE_PERFORMANCE] = GB_PERFORMANCE_MODE_PERFORMANCE;
  553. /*
  554. * Value returned in iob0 will have the number of supported performance
  555. * modes per device. The performance mode values will then be given as a
  556. * list after this (iob1-iobX). Loop through the supported values and
  557. * enable their mapped platform_profile choice, overriding "legacy"
  558. * values along the way if a non-legacy value exists.
  559. */
  560. for (i = 1; i <= buf.iob0; i++) {
  561. err = get_performance_mode_profile(galaxybook, buf.iobs[i], &profile);
  562. if (err) {
  563. dev_dbg(&galaxybook->platform->dev,
  564. "ignoring unmapped performance mode 0x%x\n", buf.iobs[i]);
  565. continue;
  566. }
  567. switch (buf.iobs[i]) {
  568. case GB_PERFORMANCE_MODE_OPTIMIZED_V2:
  569. perfmodes[profile] = GB_PERFORMANCE_MODE_OPTIMIZED_V2;
  570. break;
  571. case GB_PERFORMANCE_MODE_PERFORMANCE_V2:
  572. /* only update if not already overwritten by Ultra */
  573. if (perfmodes[profile] != GB_PERFORMANCE_MODE_ULTRA)
  574. perfmodes[profile] = GB_PERFORMANCE_MODE_PERFORMANCE_V2;
  575. break;
  576. case GB_PERFORMANCE_MODE_ULTRA:
  577. perfmodes[profile] = GB_PERFORMANCE_MODE_ULTRA;
  578. break;
  579. default:
  580. break;
  581. }
  582. set_bit(profile, choices);
  583. dev_dbg(&galaxybook->platform->dev,
  584. "setting platform profile %d to use performance mode 0x%x\n",
  585. profile, perfmodes[profile]);
  586. }
  587. /* initialize performance_mode using balanced's mapped value */
  588. if (test_bit(PLATFORM_PROFILE_BALANCED, choices))
  589. return performance_mode_acpi_set(galaxybook, perfmodes[PLATFORM_PROFILE_BALANCED]);
  590. return 0;
  591. }
  592. static const struct platform_profile_ops galaxybook_platform_profile_ops = {
  593. .probe = galaxybook_platform_profile_probe,
  594. .profile_get = galaxybook_platform_profile_get,
  595. .profile_set = galaxybook_platform_profile_set,
  596. };
  597. static int galaxybook_platform_profile_init(struct samsung_galaxybook *galaxybook)
  598. {
  599. struct device *platform_profile_dev;
  600. u8 performance_mode;
  601. int err;
  602. err = performance_mode_acpi_get(galaxybook, &performance_mode);
  603. if (err) {
  604. dev_dbg(&galaxybook->platform->dev,
  605. "failed to get initial performance mode, error %d\n", err);
  606. return GB_NOT_SUPPORTED;
  607. }
  608. platform_profile_dev = devm_platform_profile_register(&galaxybook->platform->dev,
  609. DRIVER_NAME, galaxybook,
  610. &galaxybook_platform_profile_ops);
  611. return PTR_ERR_OR_ZERO(platform_profile_dev);
  612. }
  613. /*
  614. * Firmware Attributes
  615. */
  616. /* Power on lid open (device should power on when lid is opened) */
  617. static int power_on_lid_open_acpi_get(struct samsung_galaxybook *galaxybook, bool *value)
  618. {
  619. struct sawb buf = {};
  620. int err;
  621. buf.safn = GB_SAFN;
  622. buf.sasb = GB_SASB_POWER_MANAGEMENT;
  623. buf.gunm = GB_GUNM_POWER_MANAGEMENT;
  624. buf.guds[0] = GB_GUDS_POWER_ON_LID_OPEN;
  625. buf.guds[1] = GB_GUDS_POWER_ON_LID_OPEN_GET;
  626. err = galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS,
  627. &buf, GB_SAWB_LEN_SETTINGS);
  628. if (err)
  629. return err;
  630. *value = buf.guds[1];
  631. return 0;
  632. }
  633. static int power_on_lid_open_acpi_set(struct samsung_galaxybook *galaxybook, const bool value)
  634. {
  635. struct sawb buf = {};
  636. lockdep_assert_held(&galaxybook->fw_attr_lock);
  637. buf.safn = GB_SAFN;
  638. buf.sasb = GB_SASB_POWER_MANAGEMENT;
  639. buf.gunm = GB_GUNM_POWER_MANAGEMENT;
  640. buf.guds[0] = GB_GUDS_POWER_ON_LID_OPEN;
  641. buf.guds[1] = GB_GUDS_POWER_ON_LID_OPEN_SET;
  642. buf.guds[2] = value ? 1 : 0;
  643. return galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS,
  644. &buf, GB_SAWB_LEN_SETTINGS);
  645. }
  646. /* USB Charging (USB ports can provide power when device is powered off) */
  647. static int usb_charging_acpi_get(struct samsung_galaxybook *galaxybook, bool *value)
  648. {
  649. struct sawb buf = {};
  650. int err;
  651. buf.safn = GB_SAFN;
  652. buf.sasb = GB_SASB_USB_CHARGING_GET;
  653. buf.gunm = GB_GUNM_USB_CHARGING_GET;
  654. err = galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS,
  655. &buf, GB_SAWB_LEN_SETTINGS);
  656. if (err)
  657. return err;
  658. *value = buf.gunm == 1;
  659. return 0;
  660. }
  661. static int usb_charging_acpi_set(struct samsung_galaxybook *galaxybook, const bool value)
  662. {
  663. struct sawb buf = {};
  664. lockdep_assert_held(&galaxybook->fw_attr_lock);
  665. buf.safn = GB_SAFN;
  666. buf.sasb = GB_SASB_USB_CHARGING_SET;
  667. buf.gunm = value ? GB_GUNM_USB_CHARGING_ON : GB_GUNM_USB_CHARGING_OFF;
  668. return galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS,
  669. &buf, GB_SAWB_LEN_SETTINGS);
  670. }
  671. /* Block recording (blocks access to camera and microphone) */
  672. static int block_recording_acpi_get(struct samsung_galaxybook *galaxybook, bool *value)
  673. {
  674. struct sawb buf = {};
  675. int err;
  676. buf.safn = GB_SAFN;
  677. buf.sasb = GB_SASB_BLOCK_RECORDING;
  678. buf.gunm = GB_GUNM_GET;
  679. err = galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS,
  680. &buf, GB_SAWB_LEN_SETTINGS);
  681. if (err)
  682. return err;
  683. *value = buf.gunm == GB_BLOCK_RECORDING_ON;
  684. return 0;
  685. }
  686. static int block_recording_acpi_set(struct samsung_galaxybook *galaxybook, const bool value)
  687. {
  688. struct sawb buf = {};
  689. int err;
  690. lockdep_assert_held(&galaxybook->fw_attr_lock);
  691. buf.safn = GB_SAFN;
  692. buf.sasb = GB_SASB_BLOCK_RECORDING;
  693. buf.gunm = GB_GUNM_SET;
  694. buf.guds[0] = value ? GB_BLOCK_RECORDING_ON : GB_BLOCK_RECORDING_OFF;
  695. err = galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS,
  696. &buf, GB_SAWB_LEN_SETTINGS);
  697. if (err)
  698. return err;
  699. input_report_switch(galaxybook->camera_lens_cover_switch,
  700. SW_CAMERA_LENS_COVER, value ? 1 : 0);
  701. input_sync(galaxybook->camera_lens_cover_switch);
  702. return 0;
  703. }
  704. static int galaxybook_block_recording_init(struct samsung_galaxybook *galaxybook)
  705. {
  706. bool value;
  707. int err;
  708. err = galaxybook_enable_acpi_feature(galaxybook, GB_SASB_BLOCK_RECORDING);
  709. if (err) {
  710. dev_dbg(&galaxybook->platform->dev,
  711. "failed to initialize block_recording, error %d\n", err);
  712. return GB_NOT_SUPPORTED;
  713. }
  714. guard(mutex)(&galaxybook->fw_attr_lock);
  715. err = block_recording_acpi_get(galaxybook, &value);
  716. if (err) {
  717. dev_dbg(&galaxybook->platform->dev,
  718. "failed to get initial block_recording state, error %d\n", err);
  719. return GB_NOT_SUPPORTED;
  720. }
  721. galaxybook->camera_lens_cover_switch =
  722. devm_input_allocate_device(&galaxybook->platform->dev);
  723. if (!galaxybook->camera_lens_cover_switch)
  724. return -ENOMEM;
  725. galaxybook->camera_lens_cover_switch->name = "Samsung Galaxy Book Camera Lens Cover";
  726. galaxybook->camera_lens_cover_switch->phys = DRIVER_NAME "/input0";
  727. galaxybook->camera_lens_cover_switch->id.bustype = BUS_HOST;
  728. input_set_capability(galaxybook->camera_lens_cover_switch, EV_SW, SW_CAMERA_LENS_COVER);
  729. err = input_register_device(galaxybook->camera_lens_cover_switch);
  730. if (err)
  731. return err;
  732. input_report_switch(galaxybook->camera_lens_cover_switch,
  733. SW_CAMERA_LENS_COVER, value ? 1 : 0);
  734. input_sync(galaxybook->camera_lens_cover_switch);
  735. return 0;
  736. }
  737. /* Firmware Attributes setup */
  738. static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
  739. {
  740. return sysfs_emit(buf, "enumeration\n");
  741. }
  742. static struct kobj_attribute fw_attr_type = __ATTR_RO(type);
  743. static ssize_t default_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
  744. {
  745. return sysfs_emit(buf, "0\n");
  746. }
  747. static struct kobj_attribute fw_attr_default_value = __ATTR_RO(default_value);
  748. static ssize_t possible_values_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
  749. {
  750. return sysfs_emit(buf, "0;1\n");
  751. }
  752. static struct kobj_attribute fw_attr_possible_values = __ATTR_RO(possible_values);
  753. static ssize_t display_name_language_code_show(struct kobject *kobj, struct kobj_attribute *attr,
  754. char *buf)
  755. {
  756. return sysfs_emit(buf, "%s\n", GB_ATTR_LANGUAGE_CODE);
  757. }
  758. static struct kobj_attribute fw_attr_display_name_language_code =
  759. __ATTR_RO(display_name_language_code);
  760. static ssize_t display_name_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
  761. {
  762. struct galaxybook_fw_attr *fw_attr =
  763. container_of(attr, struct galaxybook_fw_attr, display_name);
  764. return sysfs_emit(buf, "%s\n", galaxybook_fw_attr_desc[fw_attr->fw_attr_id]);
  765. }
  766. static ssize_t current_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
  767. {
  768. struct galaxybook_fw_attr *fw_attr =
  769. container_of(attr, struct galaxybook_fw_attr, current_value);
  770. bool value;
  771. int err;
  772. err = fw_attr->get_value(fw_attr->galaxybook, &value);
  773. if (err)
  774. return err;
  775. return sysfs_emit(buf, "%u\n", value);
  776. }
  777. static ssize_t current_value_store(struct kobject *kobj, struct kobj_attribute *attr,
  778. const char *buf, size_t count)
  779. {
  780. struct galaxybook_fw_attr *fw_attr =
  781. container_of(attr, struct galaxybook_fw_attr, current_value);
  782. struct samsung_galaxybook *galaxybook = fw_attr->galaxybook;
  783. bool value;
  784. int err;
  785. if (!count)
  786. return -EINVAL;
  787. err = kstrtobool(buf, &value);
  788. if (err)
  789. return err;
  790. guard(mutex)(&galaxybook->fw_attr_lock);
  791. err = fw_attr->set_value(galaxybook, value);
  792. if (err)
  793. return err;
  794. return count;
  795. }
  796. #define NUM_FW_ATTR_ENUM_ATTRS 6
  797. static int galaxybook_fw_attr_init(struct samsung_galaxybook *galaxybook,
  798. const enum galaxybook_fw_attr_id fw_attr_id,
  799. int (*get_value)(struct samsung_galaxybook *galaxybook,
  800. bool *value),
  801. int (*set_value)(struct samsung_galaxybook *galaxybook,
  802. const bool value))
  803. {
  804. struct galaxybook_fw_attr *fw_attr;
  805. struct attribute **attrs;
  806. fw_attr = devm_kzalloc(&galaxybook->platform->dev, sizeof(*fw_attr), GFP_KERNEL);
  807. if (!fw_attr)
  808. return -ENOMEM;
  809. attrs = devm_kcalloc(&galaxybook->platform->dev, NUM_FW_ATTR_ENUM_ATTRS + 1,
  810. sizeof(*attrs), GFP_KERNEL);
  811. if (!attrs)
  812. return -ENOMEM;
  813. attrs[0] = &fw_attr_type.attr;
  814. attrs[1] = &fw_attr_default_value.attr;
  815. attrs[2] = &fw_attr_possible_values.attr;
  816. attrs[3] = &fw_attr_display_name_language_code.attr;
  817. sysfs_attr_init(&fw_attr->display_name.attr);
  818. fw_attr->display_name.attr.name = "display_name";
  819. fw_attr->display_name.attr.mode = 0444;
  820. fw_attr->display_name.show = display_name_show;
  821. attrs[4] = &fw_attr->display_name.attr;
  822. sysfs_attr_init(&fw_attr->current_value.attr);
  823. fw_attr->current_value.attr.name = "current_value";
  824. fw_attr->current_value.attr.mode = 0644;
  825. fw_attr->current_value.show = current_value_show;
  826. fw_attr->current_value.store = current_value_store;
  827. attrs[5] = &fw_attr->current_value.attr;
  828. attrs[6] = NULL;
  829. fw_attr->galaxybook = galaxybook;
  830. fw_attr->fw_attr_id = fw_attr_id;
  831. fw_attr->attr_group.name = galaxybook_fw_attr_name[fw_attr_id];
  832. fw_attr->attr_group.attrs = attrs;
  833. fw_attr->get_value = get_value;
  834. fw_attr->set_value = set_value;
  835. return sysfs_create_group(&galaxybook->fw_attrs_kset->kobj, &fw_attr->attr_group);
  836. }
  837. static void galaxybook_kset_unregister(void *data)
  838. {
  839. struct kset *kset = data;
  840. kset_unregister(kset);
  841. }
  842. static void galaxybook_fw_attrs_dev_unregister(void *data)
  843. {
  844. struct device *fw_attrs_dev = data;
  845. device_unregister(fw_attrs_dev);
  846. }
  847. static int galaxybook_fw_attrs_init(struct samsung_galaxybook *galaxybook)
  848. {
  849. bool value;
  850. int err;
  851. err = devm_mutex_init(&galaxybook->platform->dev, &galaxybook->fw_attr_lock);
  852. if (err)
  853. return err;
  854. galaxybook->fw_attrs_dev = device_create(&firmware_attributes_class, NULL, MKDEV(0, 0),
  855. NULL, "%s", DRIVER_NAME);
  856. if (IS_ERR(galaxybook->fw_attrs_dev))
  857. return PTR_ERR(galaxybook->fw_attrs_dev);
  858. err = devm_add_action_or_reset(&galaxybook->platform->dev,
  859. galaxybook_fw_attrs_dev_unregister,
  860. galaxybook->fw_attrs_dev);
  861. if (err)
  862. return err;
  863. galaxybook->fw_attrs_kset = kset_create_and_add("attributes", NULL,
  864. &galaxybook->fw_attrs_dev->kobj);
  865. if (!galaxybook->fw_attrs_kset)
  866. return -ENOMEM;
  867. err = devm_add_action_or_reset(&galaxybook->platform->dev,
  868. galaxybook_kset_unregister, galaxybook->fw_attrs_kset);
  869. if (err)
  870. return err;
  871. err = power_on_lid_open_acpi_get(galaxybook, &value);
  872. if (!err) {
  873. err = galaxybook_fw_attr_init(galaxybook,
  874. GB_ATTR_POWER_ON_LID_OPEN,
  875. &power_on_lid_open_acpi_get,
  876. &power_on_lid_open_acpi_set);
  877. if (err)
  878. return err;
  879. }
  880. err = usb_charging_acpi_get(galaxybook, &value);
  881. if (!err) {
  882. err = galaxybook_fw_attr_init(galaxybook,
  883. GB_ATTR_USB_CHARGING,
  884. &usb_charging_acpi_get,
  885. &usb_charging_acpi_set);
  886. if (err)
  887. return err;
  888. }
  889. err = galaxybook_block_recording_init(galaxybook);
  890. if (err == GB_NOT_SUPPORTED)
  891. return 0;
  892. else if (err)
  893. return err;
  894. galaxybook->has_block_recording = true;
  895. return galaxybook_fw_attr_init(galaxybook,
  896. GB_ATTR_BLOCK_RECORDING,
  897. &block_recording_acpi_get,
  898. &block_recording_acpi_set);
  899. }
  900. /*
  901. * Hotkeys and notifications
  902. */
  903. static void galaxybook_kbd_backlight_hotkey_work(struct work_struct *work)
  904. {
  905. struct samsung_galaxybook *galaxybook =
  906. from_work(galaxybook, work, kbd_backlight_hotkey_work);
  907. int brightness;
  908. int err;
  909. guard(mutex)(&galaxybook->kbd_backlight_lock);
  910. brightness = galaxybook->kbd_backlight.brightness;
  911. if (brightness < galaxybook->kbd_backlight.max_brightness)
  912. brightness++;
  913. else
  914. brightness = 0;
  915. err = led_set_brightness_sync(&galaxybook->kbd_backlight, brightness);
  916. if (err) {
  917. dev_err(&galaxybook->platform->dev,
  918. "failed to set kbd_backlight brightness, error %d\n", err);
  919. return;
  920. }
  921. led_classdev_notify_brightness_hw_changed(&galaxybook->kbd_backlight, brightness);
  922. }
  923. static void galaxybook_block_recording_hotkey_work(struct work_struct *work)
  924. {
  925. struct samsung_galaxybook *galaxybook =
  926. from_work(galaxybook, work, block_recording_hotkey_work);
  927. bool value;
  928. int err;
  929. guard(mutex)(&galaxybook->fw_attr_lock);
  930. err = block_recording_acpi_get(galaxybook, &value);
  931. if (err) {
  932. dev_err(&galaxybook->platform->dev,
  933. "failed to get block_recording, error %d\n", err);
  934. return;
  935. }
  936. err = block_recording_acpi_set(galaxybook, !value);
  937. if (err)
  938. dev_err(&galaxybook->platform->dev,
  939. "failed to set block_recording, error %d\n", err);
  940. }
  941. static bool galaxybook_i8042_filter(unsigned char data, unsigned char str, struct serio *port,
  942. void *context)
  943. {
  944. struct samsung_galaxybook *galaxybook = context;
  945. static bool extended;
  946. if (str & I8042_STR_AUXDATA)
  947. return false;
  948. if (data == 0xe0) {
  949. extended = true;
  950. return true;
  951. } else if (extended) {
  952. extended = false;
  953. switch (data) {
  954. case GB_KEY_KBD_BACKLIGHT_KEYDOWN:
  955. return true;
  956. case GB_KEY_KBD_BACKLIGHT_KEYUP:
  957. if (galaxybook->has_kbd_backlight)
  958. schedule_work(&galaxybook->kbd_backlight_hotkey_work);
  959. return true;
  960. case GB_KEY_BLOCK_RECORDING_KEYDOWN:
  961. return true;
  962. case GB_KEY_BLOCK_RECORDING_KEYUP:
  963. if (galaxybook->has_block_recording)
  964. schedule_work(&galaxybook->block_recording_hotkey_work);
  965. return true;
  966. /* battery notification already sent to battery + SCAI device */
  967. case GB_KEY_BATTERY_NOTIFY_KEYUP:
  968. case GB_KEY_BATTERY_NOTIFY_KEYDOWN:
  969. return true;
  970. default:
  971. /*
  972. * Report the previously filtered e0 before continuing
  973. * with the next non-filtered byte.
  974. */
  975. serio_interrupt(port, 0xe0, 0);
  976. return false;
  977. }
  978. }
  979. return false;
  980. }
  981. static void galaxybook_i8042_filter_remove(void *data)
  982. {
  983. struct samsung_galaxybook *galaxybook = data;
  984. i8042_remove_filter(galaxybook_i8042_filter);
  985. cancel_work_sync(&galaxybook->kbd_backlight_hotkey_work);
  986. cancel_work_sync(&galaxybook->block_recording_hotkey_work);
  987. }
  988. static int galaxybook_i8042_filter_install(struct samsung_galaxybook *galaxybook)
  989. {
  990. int err;
  991. if (!galaxybook->has_kbd_backlight && !galaxybook->has_block_recording)
  992. return 0;
  993. INIT_WORK(&galaxybook->kbd_backlight_hotkey_work,
  994. galaxybook_kbd_backlight_hotkey_work);
  995. INIT_WORK(&galaxybook->block_recording_hotkey_work,
  996. galaxybook_block_recording_hotkey_work);
  997. err = i8042_install_filter(galaxybook_i8042_filter, galaxybook);
  998. if (err)
  999. return err;
  1000. return devm_add_action_or_reset(&galaxybook->platform->dev,
  1001. galaxybook_i8042_filter_remove, galaxybook);
  1002. }
  1003. /*
  1004. * ACPI device setup
  1005. */
  1006. static void galaxybook_acpi_notify(acpi_handle handle, u32 event, void *data)
  1007. {
  1008. struct samsung_galaxybook *galaxybook = data;
  1009. switch (event) {
  1010. case GB_ACPI_NOTIFY_BATTERY_STATE_CHANGED:
  1011. case GB_ACPI_NOTIFY_DEVICE_ON_TABLE:
  1012. case GB_ACPI_NOTIFY_DEVICE_OFF_TABLE:
  1013. break;
  1014. case GB_ACPI_NOTIFY_HOTKEY_PERFORMANCE_MODE:
  1015. if (galaxybook->has_performance_mode)
  1016. platform_profile_cycle();
  1017. break;
  1018. default:
  1019. dev_warn(&galaxybook->platform->dev,
  1020. "unknown ACPI notification event: 0x%x\n", event);
  1021. }
  1022. acpi_bus_generate_netlink_event(DRIVER_NAME, dev_name(&galaxybook->platform->dev),
  1023. event, 1);
  1024. }
  1025. static int galaxybook_enable_acpi_notify(struct samsung_galaxybook *galaxybook)
  1026. {
  1027. struct sawb buf = {};
  1028. int err;
  1029. err = galaxybook_enable_acpi_feature(galaxybook, GB_SASB_NOTIFICATIONS);
  1030. if (err)
  1031. return err;
  1032. buf.safn = GB_SAFN;
  1033. buf.sasb = GB_SASB_NOTIFICATIONS;
  1034. buf.gunm = GB_GUNM_ACPI_NOTIFY_ENABLE;
  1035. buf.guds[0] = GB_GUDS_ACPI_NOTIFY_ENABLE;
  1036. return galaxybook_acpi_method(galaxybook, GB_ACPI_METHOD_SETTINGS,
  1037. &buf, GB_SAWB_LEN_SETTINGS);
  1038. }
  1039. static void galaxybook_acpi_remove_notify_handler(void *data)
  1040. {
  1041. struct samsung_galaxybook *galaxybook = data;
  1042. acpi_remove_notify_handler(galaxybook->acpi->handle, ACPI_ALL_NOTIFY,
  1043. galaxybook_acpi_notify);
  1044. }
  1045. static void galaxybook_acpi_disable(void *data)
  1046. {
  1047. struct samsung_galaxybook *galaxybook = data;
  1048. acpi_execute_simple_method(galaxybook->acpi->handle,
  1049. GB_ACPI_METHOD_ENABLE, GB_ACPI_METHOD_ENABLE_OFF);
  1050. }
  1051. static int galaxybook_acpi_init(struct samsung_galaxybook *galaxybook)
  1052. {
  1053. acpi_status status;
  1054. int err;
  1055. status = acpi_execute_simple_method(galaxybook->acpi->handle, GB_ACPI_METHOD_ENABLE,
  1056. GB_ACPI_METHOD_ENABLE_ON);
  1057. if (ACPI_FAILURE(status))
  1058. return -EIO;
  1059. err = devm_add_action_or_reset(&galaxybook->platform->dev,
  1060. galaxybook_acpi_disable, galaxybook);
  1061. if (err)
  1062. return err;
  1063. status = acpi_install_notify_handler(galaxybook->acpi->handle, ACPI_ALL_NOTIFY,
  1064. galaxybook_acpi_notify, galaxybook);
  1065. if (ACPI_FAILURE(status))
  1066. return -EIO;
  1067. err = devm_add_action_or_reset(&galaxybook->platform->dev,
  1068. galaxybook_acpi_remove_notify_handler, galaxybook);
  1069. if (err)
  1070. return err;
  1071. err = galaxybook_enable_acpi_notify(galaxybook);
  1072. if (err)
  1073. dev_dbg(&galaxybook->platform->dev, "failed to enable ACPI notifications; "
  1074. "some hotkeys will not be supported\n");
  1075. err = galaxybook_enable_acpi_feature(galaxybook, GB_SASB_POWER_MANAGEMENT);
  1076. if (err)
  1077. dev_dbg(&galaxybook->platform->dev,
  1078. "failed to initialize ACPI power management features; "
  1079. "many features of this driver will not be available\n");
  1080. return 0;
  1081. }
  1082. /*
  1083. * Platform driver
  1084. */
  1085. static int galaxybook_probe(struct platform_device *pdev)
  1086. {
  1087. struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
  1088. struct samsung_galaxybook *galaxybook;
  1089. int err;
  1090. if (!adev)
  1091. return -ENODEV;
  1092. galaxybook = devm_kzalloc(&pdev->dev, sizeof(*galaxybook), GFP_KERNEL);
  1093. if (!galaxybook)
  1094. return -ENOMEM;
  1095. galaxybook->platform = pdev;
  1096. galaxybook->acpi = adev;
  1097. /*
  1098. * Features must be enabled and initialized in the following order to
  1099. * avoid failures seen on certain devices:
  1100. * - GB_SASB_POWER_MANAGEMENT (including performance mode)
  1101. * - GB_SASB_KBD_BACKLIGHT
  1102. * - GB_SASB_BLOCK_RECORDING (as part of fw_attrs init)
  1103. */
  1104. err = galaxybook_acpi_init(galaxybook);
  1105. if (err)
  1106. return dev_err_probe(&galaxybook->platform->dev, err,
  1107. "failed to initialize ACPI device\n");
  1108. err = galaxybook_platform_profile_init(galaxybook);
  1109. if (!err)
  1110. galaxybook->has_performance_mode = true;
  1111. else if (err != GB_NOT_SUPPORTED)
  1112. return dev_err_probe(&galaxybook->platform->dev, err,
  1113. "failed to initialize platform profile\n");
  1114. err = galaxybook_battery_threshold_init(galaxybook);
  1115. if (err)
  1116. return dev_err_probe(&galaxybook->platform->dev, err,
  1117. "failed to initialize battery threshold\n");
  1118. err = galaxybook_kbd_backlight_init(galaxybook);
  1119. if (!err)
  1120. galaxybook->has_kbd_backlight = true;
  1121. else if (err != GB_NOT_SUPPORTED)
  1122. return dev_err_probe(&galaxybook->platform->dev, err,
  1123. "failed to initialize kbd_backlight\n");
  1124. err = galaxybook_fw_attrs_init(galaxybook);
  1125. if (err)
  1126. return dev_err_probe(&galaxybook->platform->dev, err,
  1127. "failed to initialize firmware-attributes\n");
  1128. err = galaxybook_i8042_filter_install(galaxybook);
  1129. if (err)
  1130. return dev_err_probe(&galaxybook->platform->dev, err,
  1131. "failed to initialize i8042_filter\n");
  1132. return 0;
  1133. }
  1134. static const struct acpi_device_id galaxybook_device_ids[] = {
  1135. { "SAM0426" },
  1136. { "SAM0427" },
  1137. { "SAM0428" },
  1138. { "SAM0429" },
  1139. { "SAM0430" },
  1140. {}
  1141. };
  1142. MODULE_DEVICE_TABLE(acpi, galaxybook_device_ids);
  1143. static struct platform_driver galaxybook_platform_driver = {
  1144. .driver = {
  1145. .name = DRIVER_NAME,
  1146. .acpi_match_table = galaxybook_device_ids,
  1147. },
  1148. .probe = galaxybook_probe,
  1149. };
  1150. module_platform_driver(galaxybook_platform_driver);
  1151. MODULE_AUTHOR("Joshua Grisham <josh@joshuagrisham.com>");
  1152. MODULE_DESCRIPTION("Samsung Galaxy Book driver");
  1153. MODULE_LICENSE("GPL");