18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Topstar Laptop ACPI Extras driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2009 Herton Ronaldo Krzesinski <herton@mandriva.com.br> 68c2ecf20Sopenharmony_ci * Copyright (c) 2018 Guillaume Douézan-Grard 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Implementation inspired by existing x86 platform drivers, in special 98c2ecf20Sopenharmony_ci * asus/eepc/fujitsu-laptop, thanks to their authors. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/init.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/acpi.h> 198c2ecf20Sopenharmony_ci#include <linux/dmi.h> 208c2ecf20Sopenharmony_ci#include <linux/input.h> 218c2ecf20Sopenharmony_ci#include <linux/input/sparse-keymap.h> 228c2ecf20Sopenharmony_ci#include <linux/leds.h> 238c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define TOPSTAR_LAPTOP_CLASS "topstar" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistruct topstar_laptop { 288c2ecf20Sopenharmony_ci struct acpi_device *device; 298c2ecf20Sopenharmony_ci struct platform_device *platform; 308c2ecf20Sopenharmony_ci struct input_dev *input; 318c2ecf20Sopenharmony_ci struct led_classdev led; 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* 358c2ecf20Sopenharmony_ci * LED 368c2ecf20Sopenharmony_ci */ 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic enum led_brightness topstar_led_get(struct led_classdev *led) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci return led->brightness; 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic int topstar_led_set(struct led_classdev *led, 448c2ecf20Sopenharmony_ci enum led_brightness state) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci struct topstar_laptop *topstar = container_of(led, 478c2ecf20Sopenharmony_ci struct topstar_laptop, led); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci struct acpi_object_list params; 508c2ecf20Sopenharmony_ci union acpi_object in_obj; 518c2ecf20Sopenharmony_ci unsigned long long int ret; 528c2ecf20Sopenharmony_ci acpi_status status; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci params.count = 1; 558c2ecf20Sopenharmony_ci params.pointer = &in_obj; 568c2ecf20Sopenharmony_ci in_obj.type = ACPI_TYPE_INTEGER; 578c2ecf20Sopenharmony_ci in_obj.integer.value = 0x83; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci /* 608c2ecf20Sopenharmony_ci * Topstar ACPI returns 0x30001 when the LED is ON and 0x30000 when it 618c2ecf20Sopenharmony_ci * is OFF. 628c2ecf20Sopenharmony_ci */ 638c2ecf20Sopenharmony_ci status = acpi_evaluate_integer(topstar->device->handle, 648c2ecf20Sopenharmony_ci "GETX", ¶ms, &ret); 658c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 668c2ecf20Sopenharmony_ci return -1; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci /* 698c2ecf20Sopenharmony_ci * FNCX(0x83) toggles the LED (more precisely, it is supposed to 708c2ecf20Sopenharmony_ci * act as an hardware switch and disconnect the WLAN adapter but 718c2ecf20Sopenharmony_ci * it seems to be faulty on some models like the Topstar U931 728c2ecf20Sopenharmony_ci * Notebook). 738c2ecf20Sopenharmony_ci */ 748c2ecf20Sopenharmony_ci if ((ret == 0x30001 && state == LED_OFF) 758c2ecf20Sopenharmony_ci || (ret == 0x30000 && state != LED_OFF)) { 768c2ecf20Sopenharmony_ci status = acpi_execute_simple_method(topstar->device->handle, 778c2ecf20Sopenharmony_ci "FNCX", 0x83); 788c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 798c2ecf20Sopenharmony_ci return -1; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci return 0; 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic int topstar_led_init(struct topstar_laptop *topstar) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci topstar->led = (struct led_classdev) { 888c2ecf20Sopenharmony_ci .default_trigger = "rfkill0", 898c2ecf20Sopenharmony_ci .brightness_get = topstar_led_get, 908c2ecf20Sopenharmony_ci .brightness_set_blocking = topstar_led_set, 918c2ecf20Sopenharmony_ci .name = TOPSTAR_LAPTOP_CLASS "::wlan", 928c2ecf20Sopenharmony_ci }; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci return led_classdev_register(&topstar->platform->dev, &topstar->led); 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic void topstar_led_exit(struct topstar_laptop *topstar) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci led_classdev_unregister(&topstar->led); 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci/* 1038c2ecf20Sopenharmony_ci * Input 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic const struct key_entry topstar_keymap[] = { 1078c2ecf20Sopenharmony_ci { KE_KEY, 0x80, { KEY_BRIGHTNESSUP } }, 1088c2ecf20Sopenharmony_ci { KE_KEY, 0x81, { KEY_BRIGHTNESSDOWN } }, 1098c2ecf20Sopenharmony_ci { KE_KEY, 0x83, { KEY_VOLUMEUP } }, 1108c2ecf20Sopenharmony_ci { KE_KEY, 0x84, { KEY_VOLUMEDOWN } }, 1118c2ecf20Sopenharmony_ci { KE_KEY, 0x85, { KEY_MUTE } }, 1128c2ecf20Sopenharmony_ci { KE_KEY, 0x86, { KEY_SWITCHVIDEOMODE } }, 1138c2ecf20Sopenharmony_ci { KE_KEY, 0x87, { KEY_F13 } }, /* touchpad enable/disable key */ 1148c2ecf20Sopenharmony_ci { KE_KEY, 0x88, { KEY_WLAN } }, 1158c2ecf20Sopenharmony_ci { KE_KEY, 0x8a, { KEY_WWW } }, 1168c2ecf20Sopenharmony_ci { KE_KEY, 0x8b, { KEY_MAIL } }, 1178c2ecf20Sopenharmony_ci { KE_KEY, 0x8c, { KEY_MEDIA } }, 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci /* Known non hotkey events don't handled or that we don't care yet */ 1208c2ecf20Sopenharmony_ci { KE_IGNORE, 0x82, }, /* backlight event */ 1218c2ecf20Sopenharmony_ci { KE_IGNORE, 0x8e, }, 1228c2ecf20Sopenharmony_ci { KE_IGNORE, 0x8f, }, 1238c2ecf20Sopenharmony_ci { KE_IGNORE, 0x90, }, 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* 1268c2ecf20Sopenharmony_ci * 'G key' generate two event codes, convert to only 1278c2ecf20Sopenharmony_ci * one event/key code for now, consider replacing by 1288c2ecf20Sopenharmony_ci * a switch (3G switch - SW_3G?) 1298c2ecf20Sopenharmony_ci */ 1308c2ecf20Sopenharmony_ci { KE_KEY, 0x96, { KEY_F14 } }, 1318c2ecf20Sopenharmony_ci { KE_KEY, 0x97, { KEY_F14 } }, 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci { KE_END, 0 } 1348c2ecf20Sopenharmony_ci}; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic void topstar_input_notify(struct topstar_laptop *topstar, int event) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci if (!sparse_keymap_report_event(topstar->input, event, 1, true)) 1398c2ecf20Sopenharmony_ci pr_info("unknown event = 0x%02x\n", event); 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic int topstar_input_init(struct topstar_laptop *topstar) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct input_dev *input; 1458c2ecf20Sopenharmony_ci int err; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci input = input_allocate_device(); 1488c2ecf20Sopenharmony_ci if (!input) 1498c2ecf20Sopenharmony_ci return -ENOMEM; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci input->name = "Topstar Laptop extra buttons"; 1528c2ecf20Sopenharmony_ci input->phys = TOPSTAR_LAPTOP_CLASS "/input0"; 1538c2ecf20Sopenharmony_ci input->id.bustype = BUS_HOST; 1548c2ecf20Sopenharmony_ci input->dev.parent = &topstar->platform->dev; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci err = sparse_keymap_setup(input, topstar_keymap, NULL); 1578c2ecf20Sopenharmony_ci if (err) { 1588c2ecf20Sopenharmony_ci pr_err("Unable to setup input device keymap\n"); 1598c2ecf20Sopenharmony_ci goto err_free_dev; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci err = input_register_device(input); 1638c2ecf20Sopenharmony_ci if (err) { 1648c2ecf20Sopenharmony_ci pr_err("Unable to register input device\n"); 1658c2ecf20Sopenharmony_ci goto err_free_dev; 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci topstar->input = input; 1698c2ecf20Sopenharmony_ci return 0; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cierr_free_dev: 1728c2ecf20Sopenharmony_ci input_free_device(input); 1738c2ecf20Sopenharmony_ci return err; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic void topstar_input_exit(struct topstar_laptop *topstar) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci input_unregister_device(topstar->input); 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci/* 1828c2ecf20Sopenharmony_ci * Platform 1838c2ecf20Sopenharmony_ci */ 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic struct platform_driver topstar_platform_driver = { 1868c2ecf20Sopenharmony_ci .driver = { 1878c2ecf20Sopenharmony_ci .name = TOPSTAR_LAPTOP_CLASS, 1888c2ecf20Sopenharmony_ci }, 1898c2ecf20Sopenharmony_ci}; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic int topstar_platform_init(struct topstar_laptop *topstar) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci int err; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci topstar->platform = platform_device_alloc(TOPSTAR_LAPTOP_CLASS, -1); 1968c2ecf20Sopenharmony_ci if (!topstar->platform) 1978c2ecf20Sopenharmony_ci return -ENOMEM; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci platform_set_drvdata(topstar->platform, topstar); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci err = platform_device_add(topstar->platform); 2028c2ecf20Sopenharmony_ci if (err) 2038c2ecf20Sopenharmony_ci goto err_device_put; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci return 0; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cierr_device_put: 2088c2ecf20Sopenharmony_ci platform_device_put(topstar->platform); 2098c2ecf20Sopenharmony_ci return err; 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic void topstar_platform_exit(struct topstar_laptop *topstar) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci platform_device_unregister(topstar->platform); 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci/* 2188c2ecf20Sopenharmony_ci * ACPI 2198c2ecf20Sopenharmony_ci */ 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic int topstar_acpi_fncx_switch(struct acpi_device *device, bool state) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci acpi_status status; 2248c2ecf20Sopenharmony_ci u64 arg = state ? 0x86 : 0x87; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci status = acpi_execute_simple_method(device->handle, "FNCX", arg); 2278c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 2288c2ecf20Sopenharmony_ci pr_err("Unable to switch FNCX notifications\n"); 2298c2ecf20Sopenharmony_ci return -ENODEV; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci return 0; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic void topstar_acpi_notify(struct acpi_device *device, u32 event) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci struct topstar_laptop *topstar = acpi_driver_data(device); 2388c2ecf20Sopenharmony_ci static bool dup_evnt[2]; 2398c2ecf20Sopenharmony_ci bool *dup; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci /* 0x83 and 0x84 key events comes duplicated... */ 2428c2ecf20Sopenharmony_ci if (event == 0x83 || event == 0x84) { 2438c2ecf20Sopenharmony_ci dup = &dup_evnt[event - 0x83]; 2448c2ecf20Sopenharmony_ci if (*dup) { 2458c2ecf20Sopenharmony_ci *dup = false; 2468c2ecf20Sopenharmony_ci return; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci *dup = true; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci topstar_input_notify(topstar, event); 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic int topstar_acpi_init(struct topstar_laptop *topstar) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci return topstar_acpi_fncx_switch(topstar->device, true); 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic void topstar_acpi_exit(struct topstar_laptop *topstar) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci topstar_acpi_fncx_switch(topstar->device, false); 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci/* 2658c2ecf20Sopenharmony_ci * Enable software-based WLAN LED control on systems with defective 2668c2ecf20Sopenharmony_ci * hardware switch. 2678c2ecf20Sopenharmony_ci */ 2688c2ecf20Sopenharmony_cistatic bool led_workaround; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic int dmi_led_workaround(const struct dmi_system_id *id) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci led_workaround = true; 2738c2ecf20Sopenharmony_ci return 0; 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic const struct dmi_system_id topstar_dmi_ids[] = { 2778c2ecf20Sopenharmony_ci { 2788c2ecf20Sopenharmony_ci .callback = dmi_led_workaround, 2798c2ecf20Sopenharmony_ci .ident = "Topstar U931/RVP7", 2808c2ecf20Sopenharmony_ci .matches = { 2818c2ecf20Sopenharmony_ci DMI_MATCH(DMI_BOARD_NAME, "U931"), 2828c2ecf20Sopenharmony_ci DMI_MATCH(DMI_BOARD_VERSION, "RVP7"), 2838c2ecf20Sopenharmony_ci }, 2848c2ecf20Sopenharmony_ci }, 2858c2ecf20Sopenharmony_ci {} 2868c2ecf20Sopenharmony_ci}; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic int topstar_acpi_add(struct acpi_device *device) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci struct topstar_laptop *topstar; 2918c2ecf20Sopenharmony_ci int err; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci dmi_check_system(topstar_dmi_ids); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci topstar = kzalloc(sizeof(struct topstar_laptop), GFP_KERNEL); 2968c2ecf20Sopenharmony_ci if (!topstar) 2978c2ecf20Sopenharmony_ci return -ENOMEM; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci strcpy(acpi_device_name(device), "Topstar TPSACPI"); 3008c2ecf20Sopenharmony_ci strcpy(acpi_device_class(device), TOPSTAR_LAPTOP_CLASS); 3018c2ecf20Sopenharmony_ci device->driver_data = topstar; 3028c2ecf20Sopenharmony_ci topstar->device = device; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci err = topstar_acpi_init(topstar); 3058c2ecf20Sopenharmony_ci if (err) 3068c2ecf20Sopenharmony_ci goto err_free; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci err = topstar_platform_init(topstar); 3098c2ecf20Sopenharmony_ci if (err) 3108c2ecf20Sopenharmony_ci goto err_acpi_exit; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci err = topstar_input_init(topstar); 3138c2ecf20Sopenharmony_ci if (err) 3148c2ecf20Sopenharmony_ci goto err_platform_exit; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if (led_workaround) { 3178c2ecf20Sopenharmony_ci err = topstar_led_init(topstar); 3188c2ecf20Sopenharmony_ci if (err) 3198c2ecf20Sopenharmony_ci goto err_input_exit; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci return 0; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cierr_input_exit: 3258c2ecf20Sopenharmony_ci topstar_input_exit(topstar); 3268c2ecf20Sopenharmony_cierr_platform_exit: 3278c2ecf20Sopenharmony_ci topstar_platform_exit(topstar); 3288c2ecf20Sopenharmony_cierr_acpi_exit: 3298c2ecf20Sopenharmony_ci topstar_acpi_exit(topstar); 3308c2ecf20Sopenharmony_cierr_free: 3318c2ecf20Sopenharmony_ci kfree(topstar); 3328c2ecf20Sopenharmony_ci return err; 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic int topstar_acpi_remove(struct acpi_device *device) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci struct topstar_laptop *topstar = acpi_driver_data(device); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci if (led_workaround) 3408c2ecf20Sopenharmony_ci topstar_led_exit(topstar); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci topstar_input_exit(topstar); 3438c2ecf20Sopenharmony_ci topstar_platform_exit(topstar); 3448c2ecf20Sopenharmony_ci topstar_acpi_exit(topstar); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci kfree(topstar); 3478c2ecf20Sopenharmony_ci return 0; 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cistatic const struct acpi_device_id topstar_device_ids[] = { 3518c2ecf20Sopenharmony_ci { "TPS0001", 0 }, 3528c2ecf20Sopenharmony_ci { "TPSACPI01", 0 }, 3538c2ecf20Sopenharmony_ci { "", 0 }, 3548c2ecf20Sopenharmony_ci}; 3558c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, topstar_device_ids); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic struct acpi_driver topstar_acpi_driver = { 3588c2ecf20Sopenharmony_ci .name = "Topstar laptop ACPI driver", 3598c2ecf20Sopenharmony_ci .class = TOPSTAR_LAPTOP_CLASS, 3608c2ecf20Sopenharmony_ci .ids = topstar_device_ids, 3618c2ecf20Sopenharmony_ci .ops = { 3628c2ecf20Sopenharmony_ci .add = topstar_acpi_add, 3638c2ecf20Sopenharmony_ci .remove = topstar_acpi_remove, 3648c2ecf20Sopenharmony_ci .notify = topstar_acpi_notify, 3658c2ecf20Sopenharmony_ci }, 3668c2ecf20Sopenharmony_ci}; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic int __init topstar_laptop_init(void) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci int ret; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci ret = platform_driver_register(&topstar_platform_driver); 3738c2ecf20Sopenharmony_ci if (ret < 0) 3748c2ecf20Sopenharmony_ci return ret; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci ret = acpi_bus_register_driver(&topstar_acpi_driver); 3778c2ecf20Sopenharmony_ci if (ret < 0) 3788c2ecf20Sopenharmony_ci goto err_driver_unreg; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci pr_info("ACPI extras driver loaded\n"); 3818c2ecf20Sopenharmony_ci return 0; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cierr_driver_unreg: 3848c2ecf20Sopenharmony_ci platform_driver_unregister(&topstar_platform_driver); 3858c2ecf20Sopenharmony_ci return ret; 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistatic void __exit topstar_laptop_exit(void) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci acpi_bus_unregister_driver(&topstar_acpi_driver); 3918c2ecf20Sopenharmony_ci platform_driver_unregister(&topstar_platform_driver); 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cimodule_init(topstar_laptop_init); 3958c2ecf20Sopenharmony_cimodule_exit(topstar_laptop_exit); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ciMODULE_AUTHOR("Herton Ronaldo Krzesinski"); 3988c2ecf20Sopenharmony_ciMODULE_AUTHOR("Guillaume Douézan-Grard"); 3998c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Topstar Laptop ACPI Extras driver"); 4008c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 401