1b4f9fe12SLen Brown /* 2b4f9fe12SLen Brown * toshiba_acpi.c - Toshiba Laptop ACPI Extras 3b4f9fe12SLen Brown * 4b4f9fe12SLen Brown * 5b4f9fe12SLen Brown * Copyright (C) 2002-2004 John Belmonte 6b4f9fe12SLen Brown * Copyright (C) 2008 Philip Langdale 76c3f6e6cSPierre Ducroquet * Copyright (C) 2010 Pierre Ducroquet 8b4f9fe12SLen Brown * 9b4f9fe12SLen Brown * This program is free software; you can redistribute it and/or modify 10b4f9fe12SLen Brown * it under the terms of the GNU General Public License as published by 11b4f9fe12SLen Brown * the Free Software Foundation; either version 2 of the License, or 12b4f9fe12SLen Brown * (at your option) any later version. 13b4f9fe12SLen Brown * 14b4f9fe12SLen Brown * This program is distributed in the hope that it will be useful, 15b4f9fe12SLen Brown * but WITHOUT ANY WARRANTY; without even the implied warranty of 16b4f9fe12SLen Brown * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17b4f9fe12SLen Brown * GNU General Public License for more details. 18b4f9fe12SLen Brown * 19b4f9fe12SLen Brown * You should have received a copy of the GNU General Public License 20b4f9fe12SLen Brown * along with this program; if not, write to the Free Software 21b4f9fe12SLen Brown * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 22b4f9fe12SLen Brown * 23b4f9fe12SLen Brown * 24b4f9fe12SLen Brown * The devolpment page for this driver is located at 25b4f9fe12SLen Brown * http://memebeam.org/toys/ToshibaAcpiDriver. 26b4f9fe12SLen Brown * 27b4f9fe12SLen Brown * Credits: 28b4f9fe12SLen Brown * Jonathan A. Buzzard - Toshiba HCI info, and critical tips on reverse 29b4f9fe12SLen Brown * engineering the Windows drivers 30b4f9fe12SLen Brown * Yasushi Nagato - changes for linux kernel 2.4 -> 2.5 31b4f9fe12SLen Brown * Rob Miller - TV out and hotkeys help 32b4f9fe12SLen Brown * 33b4f9fe12SLen Brown * 34b4f9fe12SLen Brown * TODO 35b4f9fe12SLen Brown * 36b4f9fe12SLen Brown */ 37b4f9fe12SLen Brown 387e33460dSJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 397e33460dSJoe Perches 40b4f9fe12SLen Brown #define TOSHIBA_ACPI_VERSION "0.19" 41b4f9fe12SLen Brown #define PROC_INTERFACE_VERSION 1 42b4f9fe12SLen Brown 43b4f9fe12SLen Brown #include <linux/kernel.h> 44b4f9fe12SLen Brown #include <linux/module.h> 45b4f9fe12SLen Brown #include <linux/init.h> 46b4f9fe12SLen Brown #include <linux/types.h> 47b4f9fe12SLen Brown #include <linux/proc_fs.h> 48936c8bcdSAlexey Dobriyan #include <linux/seq_file.h> 49b4f9fe12SLen Brown #include <linux/backlight.h> 50b4f9fe12SLen Brown #include <linux/rfkill.h> 516335e4d5SMatthew Garrett #include <linux/input.h> 52384a7cd9SDmitry Torokhov #include <linux/input/sparse-keymap.h> 536c3f6e6cSPierre Ducroquet #include <linux/leds.h> 545a0e3ad6STejun Heo #include <linux/slab.h> 5529cd293fSSeth Forshee #include <linux/workqueue.h> 5629cd293fSSeth Forshee #include <linux/i8042.h> 578b48463fSLv Zheng #include <linux/acpi.h> 58b4f9fe12SLen Brown #include <asm/uaccess.h> 59b4f9fe12SLen Brown 60b4f9fe12SLen Brown MODULE_AUTHOR("John Belmonte"); 61b4f9fe12SLen Brown MODULE_DESCRIPTION("Toshiba Laptop ACPI Extras Driver"); 62b4f9fe12SLen Brown MODULE_LICENSE("GPL"); 63b4f9fe12SLen Brown 64f11f999eSSeth Forshee #define TOSHIBA_WMI_EVENT_GUID "59142400-C6A3-40FA-BADB-8A2652834100" 65f11f999eSSeth Forshee 6629cd293fSSeth Forshee /* Scan code for Fn key on TOS1900 models */ 6729cd293fSSeth Forshee #define TOS1900_FN_SCAN 0x6e 6829cd293fSSeth Forshee 69b4f9fe12SLen Brown /* Toshiba ACPI method paths */ 70b4f9fe12SLen Brown #define METHOD_VIDEO_OUT "\\_SB_.VALX.DSSX" 71b4f9fe12SLen Brown 72b4f9fe12SLen Brown /* Toshiba HCI interface definitions 73b4f9fe12SLen Brown * 74b4f9fe12SLen Brown * HCI is Toshiba's "Hardware Control Interface" which is supposed to 75b4f9fe12SLen Brown * be uniform across all their models. Ideally we would just call 76b4f9fe12SLen Brown * dedicated ACPI methods instead of using this primitive interface. 77b4f9fe12SLen Brown * However the ACPI methods seem to be incomplete in some areas (for 78b4f9fe12SLen Brown * example they allow setting, but not reading, the LCD brightness value), 79b4f9fe12SLen Brown * so this is still useful. 80*84a6273fSAzael Avalos * 81*84a6273fSAzael Avalos * SCI stands for "System Configuration Interface" which aim is to 82*84a6273fSAzael Avalos * conceal differences in hardware between different models. 83b4f9fe12SLen Brown */ 84b4f9fe12SLen Brown 85b4f9fe12SLen Brown #define HCI_WORDS 6 86b4f9fe12SLen Brown 87b4f9fe12SLen Brown /* operations */ 88b4f9fe12SLen Brown #define HCI_SET 0xff00 89b4f9fe12SLen Brown #define HCI_GET 0xfe00 90*84a6273fSAzael Avalos #define SCI_OPEN 0xf100 91*84a6273fSAzael Avalos #define SCI_CLOSE 0xf200 92*84a6273fSAzael Avalos #define SCI_GET 0xf300 93*84a6273fSAzael Avalos #define SCI_SET 0xf400 94b4f9fe12SLen Brown 95b4f9fe12SLen Brown /* return codes */ 96b4f9fe12SLen Brown #define HCI_SUCCESS 0x0000 97b4f9fe12SLen Brown #define HCI_FAILURE 0x1000 98b4f9fe12SLen Brown #define HCI_NOT_SUPPORTED 0x8000 99b4f9fe12SLen Brown #define HCI_EMPTY 0x8c00 100*84a6273fSAzael Avalos #define SCI_OPEN_CLOSE_OK 0x0044 101*84a6273fSAzael Avalos #define SCI_ALREADY_OPEN 0x8100 102*84a6273fSAzael Avalos #define SCI_NOT_OPENED 0x8200 103*84a6273fSAzael Avalos #define SCI_NOT_PRESENT 0x8600 104b4f9fe12SLen Brown 105b4f9fe12SLen Brown /* registers */ 106b4f9fe12SLen Brown #define HCI_FAN 0x0004 107121b7b0dSAkio Idehara #define HCI_TR_BACKLIGHT 0x0005 108b4f9fe12SLen Brown #define HCI_SYSTEM_EVENT 0x0016 109b4f9fe12SLen Brown #define HCI_VIDEO_OUT 0x001c 110b4f9fe12SLen Brown #define HCI_HOTKEY_EVENT 0x001e 111b4f9fe12SLen Brown #define HCI_LCD_BRIGHTNESS 0x002a 112b4f9fe12SLen Brown #define HCI_WIRELESS 0x0056 113b4f9fe12SLen Brown 114b4f9fe12SLen Brown /* field definitions */ 11529cd293fSSeth Forshee #define HCI_HOTKEY_DISABLE 0x0b 11629cd293fSSeth Forshee #define HCI_HOTKEY_ENABLE 0x09 117b4f9fe12SLen Brown #define HCI_LCD_BRIGHTNESS_BITS 3 118b4f9fe12SLen Brown #define HCI_LCD_BRIGHTNESS_SHIFT (16-HCI_LCD_BRIGHTNESS_BITS) 119b4f9fe12SLen Brown #define HCI_LCD_BRIGHTNESS_LEVELS (1 << HCI_LCD_BRIGHTNESS_BITS) 120b4f9fe12SLen Brown #define HCI_VIDEO_OUT_LCD 0x1 121b4f9fe12SLen Brown #define HCI_VIDEO_OUT_CRT 0x2 122b4f9fe12SLen Brown #define HCI_VIDEO_OUT_TV 0x4 123b4f9fe12SLen Brown #define HCI_WIRELESS_KILL_SWITCH 0x01 124b4f9fe12SLen Brown #define HCI_WIRELESS_BT_PRESENT 0x0f 125b4f9fe12SLen Brown #define HCI_WIRELESS_BT_ATTACH 0x40 126b4f9fe12SLen Brown #define HCI_WIRELESS_BT_POWER 0x80 127b4f9fe12SLen Brown 128135740deSSeth Forshee struct toshiba_acpi_dev { 129135740deSSeth Forshee struct acpi_device *acpi_dev; 130135740deSSeth Forshee const char *method_hci; 131135740deSSeth Forshee struct rfkill *bt_rfk; 132135740deSSeth Forshee struct input_dev *hotkey_dev; 13329cd293fSSeth Forshee struct work_struct hotkey_work; 134135740deSSeth Forshee struct backlight_device *backlight_dev; 135135740deSSeth Forshee struct led_classdev led_dev; 13636d03f93SSeth Forshee 137135740deSSeth Forshee int force_fan; 138135740deSSeth Forshee int last_key_event; 139135740deSSeth Forshee int key_event_valid; 140135740deSSeth Forshee 141592b746cSDan Carpenter unsigned int illumination_supported:1; 142592b746cSDan Carpenter unsigned int video_supported:1; 143592b746cSDan Carpenter unsigned int fan_supported:1; 144592b746cSDan Carpenter unsigned int system_event_supported:1; 14529cd293fSSeth Forshee unsigned int ntfy_supported:1; 14629cd293fSSeth Forshee unsigned int info_supported:1; 147121b7b0dSAkio Idehara unsigned int tr_backlight_supported:1; 14836d03f93SSeth Forshee 149135740deSSeth Forshee struct mutex mutex; 150135740deSSeth Forshee }; 151135740deSSeth Forshee 15229cd293fSSeth Forshee static struct toshiba_acpi_dev *toshiba_acpi; 15329cd293fSSeth Forshee 154b4f9fe12SLen Brown static const struct acpi_device_id toshiba_device_ids[] = { 155b4f9fe12SLen Brown {"TOS6200", 0}, 156b4f9fe12SLen Brown {"TOS6208", 0}, 157b4f9fe12SLen Brown {"TOS1900", 0}, 158b4f9fe12SLen Brown {"", 0}, 159b4f9fe12SLen Brown }; 160b4f9fe12SLen Brown MODULE_DEVICE_TABLE(acpi, toshiba_device_ids); 161b4f9fe12SLen Brown 162b859f159SGreg Kroah-Hartman static const struct key_entry toshiba_acpi_keymap[] = { 163fec278a1SUnai Uribarri { KE_KEY, 0x9e, { KEY_RFKILL } }, 164384a7cd9SDmitry Torokhov { KE_KEY, 0x101, { KEY_MUTE } }, 165384a7cd9SDmitry Torokhov { KE_KEY, 0x102, { KEY_ZOOMOUT } }, 166384a7cd9SDmitry Torokhov { KE_KEY, 0x103, { KEY_ZOOMIN } }, 167af502837SAzael Avalos { KE_KEY, 0x12c, { KEY_KBDILLUMTOGGLE } }, 168af502837SAzael Avalos { KE_KEY, 0x139, { KEY_ZOOMRESET } }, 169384a7cd9SDmitry Torokhov { KE_KEY, 0x13b, { KEY_COFFEE } }, 170384a7cd9SDmitry Torokhov { KE_KEY, 0x13c, { KEY_BATTERY } }, 171384a7cd9SDmitry Torokhov { KE_KEY, 0x13d, { KEY_SLEEP } }, 172384a7cd9SDmitry Torokhov { KE_KEY, 0x13e, { KEY_SUSPEND } }, 173384a7cd9SDmitry Torokhov { KE_KEY, 0x13f, { KEY_SWITCHVIDEOMODE } }, 174384a7cd9SDmitry Torokhov { KE_KEY, 0x140, { KEY_BRIGHTNESSDOWN } }, 175384a7cd9SDmitry Torokhov { KE_KEY, 0x141, { KEY_BRIGHTNESSUP } }, 176384a7cd9SDmitry Torokhov { KE_KEY, 0x142, { KEY_WLAN } }, 177af502837SAzael Avalos { KE_KEY, 0x143, { KEY_TOUCHPAD_TOGGLE } }, 178a49010f5SJon Dowland { KE_KEY, 0x17f, { KEY_FN } }, 179384a7cd9SDmitry Torokhov { KE_KEY, 0xb05, { KEY_PROG2 } }, 180384a7cd9SDmitry Torokhov { KE_KEY, 0xb06, { KEY_WWW } }, 181384a7cd9SDmitry Torokhov { KE_KEY, 0xb07, { KEY_MAIL } }, 182384a7cd9SDmitry Torokhov { KE_KEY, 0xb30, { KEY_STOP } }, 183384a7cd9SDmitry Torokhov { KE_KEY, 0xb31, { KEY_PREVIOUSSONG } }, 184384a7cd9SDmitry Torokhov { KE_KEY, 0xb32, { KEY_NEXTSONG } }, 185384a7cd9SDmitry Torokhov { KE_KEY, 0xb33, { KEY_PLAYPAUSE } }, 186384a7cd9SDmitry Torokhov { KE_KEY, 0xb5a, { KEY_MEDIA } }, 187af502837SAzael Avalos { KE_IGNORE, 0x1430, { KEY_RESERVED } }, 188384a7cd9SDmitry Torokhov { KE_END, 0 }, 1896335e4d5SMatthew Garrett }; 1906335e4d5SMatthew Garrett 191b4f9fe12SLen Brown /* utility 192b4f9fe12SLen Brown */ 193b4f9fe12SLen Brown 194b4f9fe12SLen Brown static __inline__ void _set_bit(u32 * word, u32 mask, int value) 195b4f9fe12SLen Brown { 196b4f9fe12SLen Brown *word = (*word & ~mask) | (mask * value); 197b4f9fe12SLen Brown } 198b4f9fe12SLen Brown 199b4f9fe12SLen Brown /* acpi interface wrappers 200b4f9fe12SLen Brown */ 201b4f9fe12SLen Brown 202b4f9fe12SLen Brown static int write_acpi_int(const char *methodName, int val) 203b4f9fe12SLen Brown { 204b4f9fe12SLen Brown acpi_status status; 205b4f9fe12SLen Brown 206619400daSZhang Rui status = acpi_execute_simple_method(NULL, (char *)methodName, val); 20732bcd5cbSSeth Forshee return (status == AE_OK) ? 0 : -EIO; 208b4f9fe12SLen Brown } 209b4f9fe12SLen Brown 210b4f9fe12SLen Brown /* Perform a raw HCI call. Here we don't care about input or output buffer 211b4f9fe12SLen Brown * format. 212b4f9fe12SLen Brown */ 213135740deSSeth Forshee static acpi_status hci_raw(struct toshiba_acpi_dev *dev, 214135740deSSeth Forshee const u32 in[HCI_WORDS], u32 out[HCI_WORDS]) 215b4f9fe12SLen Brown { 216b4f9fe12SLen Brown struct acpi_object_list params; 217b4f9fe12SLen Brown union acpi_object in_objs[HCI_WORDS]; 218b4f9fe12SLen Brown struct acpi_buffer results; 219b4f9fe12SLen Brown union acpi_object out_objs[HCI_WORDS + 1]; 220b4f9fe12SLen Brown acpi_status status; 221b4f9fe12SLen Brown int i; 222b4f9fe12SLen Brown 223b4f9fe12SLen Brown params.count = HCI_WORDS; 224b4f9fe12SLen Brown params.pointer = in_objs; 225b4f9fe12SLen Brown for (i = 0; i < HCI_WORDS; ++i) { 226b4f9fe12SLen Brown in_objs[i].type = ACPI_TYPE_INTEGER; 227b4f9fe12SLen Brown in_objs[i].integer.value = in[i]; 228b4f9fe12SLen Brown } 229b4f9fe12SLen Brown 230b4f9fe12SLen Brown results.length = sizeof(out_objs); 231b4f9fe12SLen Brown results.pointer = out_objs; 232b4f9fe12SLen Brown 2336e02cc7eSSeth Forshee status = acpi_evaluate_object(dev->acpi_dev->handle, 2346e02cc7eSSeth Forshee (char *)dev->method_hci, ¶ms, 235b4f9fe12SLen Brown &results); 236b4f9fe12SLen Brown if ((status == AE_OK) && (out_objs->package.count <= HCI_WORDS)) { 237b4f9fe12SLen Brown for (i = 0; i < out_objs->package.count; ++i) { 238b4f9fe12SLen Brown out[i] = out_objs->package.elements[i].integer.value; 239b4f9fe12SLen Brown } 240b4f9fe12SLen Brown } 241b4f9fe12SLen Brown 242b4f9fe12SLen Brown return status; 243b4f9fe12SLen Brown } 244b4f9fe12SLen Brown 245b4f9fe12SLen Brown /* common hci tasks (get or set one or two value) 246b4f9fe12SLen Brown * 247b4f9fe12SLen Brown * In addition to the ACPI status, the HCI system returns a result which 248b4f9fe12SLen Brown * may be useful (such as "not supported"). 249b4f9fe12SLen Brown */ 250b4f9fe12SLen Brown 251135740deSSeth Forshee static acpi_status hci_write1(struct toshiba_acpi_dev *dev, u32 reg, 252135740deSSeth Forshee u32 in1, u32 *result) 253b4f9fe12SLen Brown { 254b4f9fe12SLen Brown u32 in[HCI_WORDS] = { HCI_SET, reg, in1, 0, 0, 0 }; 255b4f9fe12SLen Brown u32 out[HCI_WORDS]; 256135740deSSeth Forshee acpi_status status = hci_raw(dev, in, out); 257b4f9fe12SLen Brown *result = (status == AE_OK) ? out[0] : HCI_FAILURE; 258b4f9fe12SLen Brown return status; 259b4f9fe12SLen Brown } 260b4f9fe12SLen Brown 261135740deSSeth Forshee static acpi_status hci_read1(struct toshiba_acpi_dev *dev, u32 reg, 262135740deSSeth Forshee u32 *out1, u32 *result) 263b4f9fe12SLen Brown { 264b4f9fe12SLen Brown u32 in[HCI_WORDS] = { HCI_GET, reg, 0, 0, 0, 0 }; 265b4f9fe12SLen Brown u32 out[HCI_WORDS]; 266135740deSSeth Forshee acpi_status status = hci_raw(dev, in, out); 267b4f9fe12SLen Brown *out1 = out[2]; 268b4f9fe12SLen Brown *result = (status == AE_OK) ? out[0] : HCI_FAILURE; 269b4f9fe12SLen Brown return status; 270b4f9fe12SLen Brown } 271b4f9fe12SLen Brown 272135740deSSeth Forshee static acpi_status hci_write2(struct toshiba_acpi_dev *dev, u32 reg, 273135740deSSeth Forshee u32 in1, u32 in2, u32 *result) 274b4f9fe12SLen Brown { 275b4f9fe12SLen Brown u32 in[HCI_WORDS] = { HCI_SET, reg, in1, in2, 0, 0 }; 276b4f9fe12SLen Brown u32 out[HCI_WORDS]; 277135740deSSeth Forshee acpi_status status = hci_raw(dev, in, out); 278b4f9fe12SLen Brown *result = (status == AE_OK) ? out[0] : HCI_FAILURE; 279b4f9fe12SLen Brown return status; 280b4f9fe12SLen Brown } 281b4f9fe12SLen Brown 282135740deSSeth Forshee static acpi_status hci_read2(struct toshiba_acpi_dev *dev, u32 reg, 283135740deSSeth Forshee u32 *out1, u32 *out2, u32 *result) 284b4f9fe12SLen Brown { 285b4f9fe12SLen Brown u32 in[HCI_WORDS] = { HCI_GET, reg, *out1, *out2, 0, 0 }; 286b4f9fe12SLen Brown u32 out[HCI_WORDS]; 287135740deSSeth Forshee acpi_status status = hci_raw(dev, in, out); 288b4f9fe12SLen Brown *out1 = out[2]; 289b4f9fe12SLen Brown *out2 = out[3]; 290b4f9fe12SLen Brown *result = (status == AE_OK) ? out[0] : HCI_FAILURE; 291b4f9fe12SLen Brown return status; 292b4f9fe12SLen Brown } 293b4f9fe12SLen Brown 294*84a6273fSAzael Avalos /* common sci tasks 295*84a6273fSAzael Avalos */ 296*84a6273fSAzael Avalos 297*84a6273fSAzael Avalos static int sci_open(struct toshiba_acpi_dev *dev) 298*84a6273fSAzael Avalos { 299*84a6273fSAzael Avalos u32 in[HCI_WORDS] = { SCI_OPEN, 0, 0, 0, 0, 0 }; 300*84a6273fSAzael Avalos u32 out[HCI_WORDS]; 301*84a6273fSAzael Avalos acpi_status status; 302*84a6273fSAzael Avalos 303*84a6273fSAzael Avalos status = hci_raw(dev, in, out); 304*84a6273fSAzael Avalos if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) { 305*84a6273fSAzael Avalos pr_err("ACPI call to open SCI failed\n"); 306*84a6273fSAzael Avalos return 0; 307*84a6273fSAzael Avalos } 308*84a6273fSAzael Avalos 309*84a6273fSAzael Avalos if (out[0] == SCI_OPEN_CLOSE_OK) { 310*84a6273fSAzael Avalos return 1; 311*84a6273fSAzael Avalos } else if (out[0] == SCI_ALREADY_OPEN) { 312*84a6273fSAzael Avalos pr_info("Toshiba SCI already opened\n"); 313*84a6273fSAzael Avalos return 1; 314*84a6273fSAzael Avalos } else if (out[0] == SCI_NOT_PRESENT) { 315*84a6273fSAzael Avalos pr_info("Toshiba SCI is not present\n"); 316*84a6273fSAzael Avalos } 317*84a6273fSAzael Avalos 318*84a6273fSAzael Avalos return 0; 319*84a6273fSAzael Avalos } 320*84a6273fSAzael Avalos 321*84a6273fSAzael Avalos static void sci_close(struct toshiba_acpi_dev *dev) 322*84a6273fSAzael Avalos { 323*84a6273fSAzael Avalos u32 in[HCI_WORDS] = { SCI_CLOSE, 0, 0, 0, 0, 0 }; 324*84a6273fSAzael Avalos u32 out[HCI_WORDS]; 325*84a6273fSAzael Avalos acpi_status status; 326*84a6273fSAzael Avalos 327*84a6273fSAzael Avalos status = hci_raw(dev, in, out); 328*84a6273fSAzael Avalos if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) { 329*84a6273fSAzael Avalos pr_err("ACPI call to close SCI failed\n"); 330*84a6273fSAzael Avalos return; 331*84a6273fSAzael Avalos } 332*84a6273fSAzael Avalos 333*84a6273fSAzael Avalos if (out[0] == SCI_OPEN_CLOSE_OK) 334*84a6273fSAzael Avalos return; 335*84a6273fSAzael Avalos else if (out[0] == SCI_NOT_OPENED) 336*84a6273fSAzael Avalos pr_info("Toshiba SCI not opened\n"); 337*84a6273fSAzael Avalos else if (out[0] == SCI_NOT_PRESENT) 338*84a6273fSAzael Avalos pr_info("Toshiba SCI is not present\n"); 339*84a6273fSAzael Avalos } 340*84a6273fSAzael Avalos 341*84a6273fSAzael Avalos static acpi_status sci_read(struct toshiba_acpi_dev *dev, u32 reg, 342*84a6273fSAzael Avalos u32 *out1, u32 *result) 343*84a6273fSAzael Avalos { 344*84a6273fSAzael Avalos u32 in[HCI_WORDS] = { SCI_GET, reg, 0, 0, 0, 0 }; 345*84a6273fSAzael Avalos u32 out[HCI_WORDS]; 346*84a6273fSAzael Avalos acpi_status status = hci_raw(dev, in, out); 347*84a6273fSAzael Avalos *out1 = out[2]; 348*84a6273fSAzael Avalos *result = (ACPI_SUCCESS(status)) ? out[0] : HCI_FAILURE; 349*84a6273fSAzael Avalos return status; 350*84a6273fSAzael Avalos } 351*84a6273fSAzael Avalos 352*84a6273fSAzael Avalos static acpi_status sci_write(struct toshiba_acpi_dev *dev, u32 reg, 353*84a6273fSAzael Avalos u32 in1, u32 *result) 354*84a6273fSAzael Avalos { 355*84a6273fSAzael Avalos u32 in[HCI_WORDS] = { SCI_SET, reg, in1, 0, 0, 0 }; 356*84a6273fSAzael Avalos u32 out[HCI_WORDS]; 357*84a6273fSAzael Avalos acpi_status status = hci_raw(dev, in, out); 358*84a6273fSAzael Avalos *result = (ACPI_SUCCESS(status)) ? out[0] : HCI_FAILURE; 359*84a6273fSAzael Avalos return status; 360*84a6273fSAzael Avalos } 361*84a6273fSAzael Avalos 3626c3f6e6cSPierre Ducroquet /* Illumination support */ 363135740deSSeth Forshee static int toshiba_illumination_available(struct toshiba_acpi_dev *dev) 3646c3f6e6cSPierre Ducroquet { 3656c3f6e6cSPierre Ducroquet u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 }; 3666c3f6e6cSPierre Ducroquet u32 out[HCI_WORDS]; 3676c3f6e6cSPierre Ducroquet acpi_status status; 3686c3f6e6cSPierre Ducroquet 3696c3f6e6cSPierre Ducroquet in[0] = 0xf100; 370135740deSSeth Forshee status = hci_raw(dev, in, out); 3716c3f6e6cSPierre Ducroquet if (ACPI_FAILURE(status)) { 3727e33460dSJoe Perches pr_info("Illumination device not available\n"); 3736c3f6e6cSPierre Ducroquet return 0; 3746c3f6e6cSPierre Ducroquet } 3756c3f6e6cSPierre Ducroquet in[0] = 0xf400; 376135740deSSeth Forshee status = hci_raw(dev, in, out); 3776c3f6e6cSPierre Ducroquet return 1; 3786c3f6e6cSPierre Ducroquet } 3796c3f6e6cSPierre Ducroquet 3806c3f6e6cSPierre Ducroquet static void toshiba_illumination_set(struct led_classdev *cdev, 3816c3f6e6cSPierre Ducroquet enum led_brightness brightness) 3826c3f6e6cSPierre Ducroquet { 383135740deSSeth Forshee struct toshiba_acpi_dev *dev = container_of(cdev, 384135740deSSeth Forshee struct toshiba_acpi_dev, led_dev); 3856c3f6e6cSPierre Ducroquet u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 }; 3866c3f6e6cSPierre Ducroquet u32 out[HCI_WORDS]; 3876c3f6e6cSPierre Ducroquet acpi_status status; 3886c3f6e6cSPierre Ducroquet 3896c3f6e6cSPierre Ducroquet /* First request : initialize communication. */ 3906c3f6e6cSPierre Ducroquet in[0] = 0xf100; 391135740deSSeth Forshee status = hci_raw(dev, in, out); 3926c3f6e6cSPierre Ducroquet if (ACPI_FAILURE(status)) { 3937e33460dSJoe Perches pr_info("Illumination device not available\n"); 3946c3f6e6cSPierre Ducroquet return; 3956c3f6e6cSPierre Ducroquet } 3966c3f6e6cSPierre Ducroquet 3976c3f6e6cSPierre Ducroquet if (brightness) { 3986c3f6e6cSPierre Ducroquet /* Switch the illumination on */ 3996c3f6e6cSPierre Ducroquet in[0] = 0xf400; 4006c3f6e6cSPierre Ducroquet in[1] = 0x14e; 4016c3f6e6cSPierre Ducroquet in[2] = 1; 402135740deSSeth Forshee status = hci_raw(dev, in, out); 4036c3f6e6cSPierre Ducroquet if (ACPI_FAILURE(status)) { 4047e33460dSJoe Perches pr_info("ACPI call for illumination failed\n"); 4056c3f6e6cSPierre Ducroquet return; 4066c3f6e6cSPierre Ducroquet } 4076c3f6e6cSPierre Ducroquet } else { 4086c3f6e6cSPierre Ducroquet /* Switch the illumination off */ 4096c3f6e6cSPierre Ducroquet in[0] = 0xf400; 4106c3f6e6cSPierre Ducroquet in[1] = 0x14e; 4116c3f6e6cSPierre Ducroquet in[2] = 0; 412135740deSSeth Forshee status = hci_raw(dev, in, out); 4136c3f6e6cSPierre Ducroquet if (ACPI_FAILURE(status)) { 4147e33460dSJoe Perches pr_info("ACPI call for illumination failed.\n"); 4156c3f6e6cSPierre Ducroquet return; 4166c3f6e6cSPierre Ducroquet } 4176c3f6e6cSPierre Ducroquet } 4186c3f6e6cSPierre Ducroquet 4196c3f6e6cSPierre Ducroquet /* Last request : close communication. */ 4206c3f6e6cSPierre Ducroquet in[0] = 0xf200; 4216c3f6e6cSPierre Ducroquet in[1] = 0; 4226c3f6e6cSPierre Ducroquet in[2] = 0; 423135740deSSeth Forshee hci_raw(dev, in, out); 4246c3f6e6cSPierre Ducroquet } 4256c3f6e6cSPierre Ducroquet 4266c3f6e6cSPierre Ducroquet static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev) 4276c3f6e6cSPierre Ducroquet { 428135740deSSeth Forshee struct toshiba_acpi_dev *dev = container_of(cdev, 429135740deSSeth Forshee struct toshiba_acpi_dev, led_dev); 4306c3f6e6cSPierre Ducroquet u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 }; 4316c3f6e6cSPierre Ducroquet u32 out[HCI_WORDS]; 4326c3f6e6cSPierre Ducroquet acpi_status status; 4336c3f6e6cSPierre Ducroquet enum led_brightness result; 4346c3f6e6cSPierre Ducroquet 4356c3f6e6cSPierre Ducroquet /* First request : initialize communication. */ 4366c3f6e6cSPierre Ducroquet in[0] = 0xf100; 437135740deSSeth Forshee status = hci_raw(dev, in, out); 4386c3f6e6cSPierre Ducroquet if (ACPI_FAILURE(status)) { 4397e33460dSJoe Perches pr_info("Illumination device not available\n"); 4406c3f6e6cSPierre Ducroquet return LED_OFF; 4416c3f6e6cSPierre Ducroquet } 4426c3f6e6cSPierre Ducroquet 4436c3f6e6cSPierre Ducroquet /* Check the illumination */ 4446c3f6e6cSPierre Ducroquet in[0] = 0xf300; 4456c3f6e6cSPierre Ducroquet in[1] = 0x14e; 446135740deSSeth Forshee status = hci_raw(dev, in, out); 4476c3f6e6cSPierre Ducroquet if (ACPI_FAILURE(status)) { 4487e33460dSJoe Perches pr_info("ACPI call for illumination failed.\n"); 4496c3f6e6cSPierre Ducroquet return LED_OFF; 4506c3f6e6cSPierre Ducroquet } 4516c3f6e6cSPierre Ducroquet 4526c3f6e6cSPierre Ducroquet result = out[2] ? LED_FULL : LED_OFF; 4536c3f6e6cSPierre Ducroquet 4546c3f6e6cSPierre Ducroquet /* Last request : close communication. */ 4556c3f6e6cSPierre Ducroquet in[0] = 0xf200; 4566c3f6e6cSPierre Ducroquet in[1] = 0; 4576c3f6e6cSPierre Ducroquet in[2] = 0; 458135740deSSeth Forshee hci_raw(dev, in, out); 4596c3f6e6cSPierre Ducroquet 4606c3f6e6cSPierre Ducroquet return result; 4616c3f6e6cSPierre Ducroquet } 4626c3f6e6cSPierre Ducroquet 463b4f9fe12SLen Brown /* Bluetooth rfkill handlers */ 464b4f9fe12SLen Brown 465135740deSSeth Forshee static u32 hci_get_bt_present(struct toshiba_acpi_dev *dev, bool *present) 466b4f9fe12SLen Brown { 467b4f9fe12SLen Brown u32 hci_result; 468b4f9fe12SLen Brown u32 value, value2; 469b4f9fe12SLen Brown 470b4f9fe12SLen Brown value = 0; 471b4f9fe12SLen Brown value2 = 0; 472135740deSSeth Forshee hci_read2(dev, HCI_WIRELESS, &value, &value2, &hci_result); 473b4f9fe12SLen Brown if (hci_result == HCI_SUCCESS) 474b4f9fe12SLen Brown *present = (value & HCI_WIRELESS_BT_PRESENT) ? true : false; 475b4f9fe12SLen Brown 476b4f9fe12SLen Brown return hci_result; 477b4f9fe12SLen Brown } 478b4f9fe12SLen Brown 479135740deSSeth Forshee static u32 hci_get_radio_state(struct toshiba_acpi_dev *dev, bool *radio_state) 480b4f9fe12SLen Brown { 481b4f9fe12SLen Brown u32 hci_result; 482b4f9fe12SLen Brown u32 value, value2; 483b4f9fe12SLen Brown 484b4f9fe12SLen Brown value = 0; 485b4f9fe12SLen Brown value2 = 0x0001; 486135740deSSeth Forshee hci_read2(dev, HCI_WIRELESS, &value, &value2, &hci_result); 487b4f9fe12SLen Brown 488b4f9fe12SLen Brown *radio_state = value & HCI_WIRELESS_KILL_SWITCH; 489b4f9fe12SLen Brown return hci_result; 490b4f9fe12SLen Brown } 491b4f9fe12SLen Brown 49219d337dfSJohannes Berg static int bt_rfkill_set_block(void *data, bool blocked) 493b4f9fe12SLen Brown { 49419d337dfSJohannes Berg struct toshiba_acpi_dev *dev = data; 495b4f9fe12SLen Brown u32 result1, result2; 496b4f9fe12SLen Brown u32 value; 49719d337dfSJohannes Berg int err; 498b4f9fe12SLen Brown bool radio_state; 499b4f9fe12SLen Brown 50019d337dfSJohannes Berg value = (blocked == false); 501b4f9fe12SLen Brown 502b4f9fe12SLen Brown mutex_lock(&dev->mutex); 503135740deSSeth Forshee if (hci_get_radio_state(dev, &radio_state) != HCI_SUCCESS) { 50432bcd5cbSSeth Forshee err = -EIO; 50519d337dfSJohannes Berg goto out; 506b4f9fe12SLen Brown } 507b4f9fe12SLen Brown 50819d337dfSJohannes Berg if (!radio_state) { 50919d337dfSJohannes Berg err = 0; 51019d337dfSJohannes Berg goto out; 51119d337dfSJohannes Berg } 51219d337dfSJohannes Berg 513135740deSSeth Forshee hci_write2(dev, HCI_WIRELESS, value, HCI_WIRELESS_BT_POWER, &result1); 514135740deSSeth Forshee hci_write2(dev, HCI_WIRELESS, value, HCI_WIRELESS_BT_ATTACH, &result2); 51519d337dfSJohannes Berg 51619d337dfSJohannes Berg if (result1 != HCI_SUCCESS || result2 != HCI_SUCCESS) 51732bcd5cbSSeth Forshee err = -EIO; 51819d337dfSJohannes Berg else 51919d337dfSJohannes Berg err = 0; 52019d337dfSJohannes Berg out: 52119d337dfSJohannes Berg mutex_unlock(&dev->mutex); 52219d337dfSJohannes Berg return err; 52319d337dfSJohannes Berg } 52419d337dfSJohannes Berg 52519d337dfSJohannes Berg static void bt_rfkill_poll(struct rfkill *rfkill, void *data) 526b4f9fe12SLen Brown { 527b4f9fe12SLen Brown bool new_rfk_state; 528b4f9fe12SLen Brown bool value; 529b4f9fe12SLen Brown u32 hci_result; 53019d337dfSJohannes Berg struct toshiba_acpi_dev *dev = data; 53119d337dfSJohannes Berg 53219d337dfSJohannes Berg mutex_lock(&dev->mutex); 533b4f9fe12SLen Brown 534135740deSSeth Forshee hci_result = hci_get_radio_state(dev, &value); 53519d337dfSJohannes Berg if (hci_result != HCI_SUCCESS) { 53619d337dfSJohannes Berg /* Can't do anything useful */ 53719d337dfSJohannes Berg mutex_unlock(&dev->mutex); 53882e7784fSJiri Slaby return; 53919d337dfSJohannes Berg } 540b4f9fe12SLen Brown 541b4f9fe12SLen Brown new_rfk_state = value; 542b4f9fe12SLen Brown 543b4f9fe12SLen Brown mutex_unlock(&dev->mutex); 544b4f9fe12SLen Brown 54519d337dfSJohannes Berg if (rfkill_set_hw_state(rfkill, !new_rfk_state)) 54619d337dfSJohannes Berg bt_rfkill_set_block(data, true); 547b4f9fe12SLen Brown } 54819d337dfSJohannes Berg 54919d337dfSJohannes Berg static const struct rfkill_ops toshiba_rfk_ops = { 55019d337dfSJohannes Berg .set_block = bt_rfkill_set_block, 55119d337dfSJohannes Berg .poll = bt_rfkill_poll, 55219d337dfSJohannes Berg }; 553b4f9fe12SLen Brown 554121b7b0dSAkio Idehara static int get_tr_backlight_status(struct toshiba_acpi_dev *dev, bool *enabled) 555121b7b0dSAkio Idehara { 556121b7b0dSAkio Idehara u32 hci_result; 557121b7b0dSAkio Idehara u32 status; 558121b7b0dSAkio Idehara 559121b7b0dSAkio Idehara hci_read1(dev, HCI_TR_BACKLIGHT, &status, &hci_result); 560121b7b0dSAkio Idehara *enabled = !status; 561121b7b0dSAkio Idehara return hci_result == HCI_SUCCESS ? 0 : -EIO; 562121b7b0dSAkio Idehara } 563121b7b0dSAkio Idehara 564121b7b0dSAkio Idehara static int set_tr_backlight_status(struct toshiba_acpi_dev *dev, bool enable) 565121b7b0dSAkio Idehara { 566121b7b0dSAkio Idehara u32 hci_result; 567121b7b0dSAkio Idehara u32 value = !enable; 568121b7b0dSAkio Idehara 569121b7b0dSAkio Idehara hci_write1(dev, HCI_TR_BACKLIGHT, value, &hci_result); 570121b7b0dSAkio Idehara return hci_result == HCI_SUCCESS ? 0 : -EIO; 571121b7b0dSAkio Idehara } 572121b7b0dSAkio Idehara 573b4f9fe12SLen Brown static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ; 574b4f9fe12SLen Brown 57562cce752SSeth Forshee static int __get_lcd_brightness(struct toshiba_acpi_dev *dev) 576b4f9fe12SLen Brown { 577b4f9fe12SLen Brown u32 hci_result; 578b4f9fe12SLen Brown u32 value; 579121b7b0dSAkio Idehara int brightness = 0; 580121b7b0dSAkio Idehara 581121b7b0dSAkio Idehara if (dev->tr_backlight_supported) { 582121b7b0dSAkio Idehara bool enabled; 583121b7b0dSAkio Idehara int ret = get_tr_backlight_status(dev, &enabled); 584121b7b0dSAkio Idehara if (ret) 585121b7b0dSAkio Idehara return ret; 586121b7b0dSAkio Idehara if (enabled) 587121b7b0dSAkio Idehara return 0; 588121b7b0dSAkio Idehara brightness++; 589121b7b0dSAkio Idehara } 590b4f9fe12SLen Brown 591135740deSSeth Forshee hci_read1(dev, HCI_LCD_BRIGHTNESS, &value, &hci_result); 59232bcd5cbSSeth Forshee if (hci_result == HCI_SUCCESS) 593121b7b0dSAkio Idehara return brightness + (value >> HCI_LCD_BRIGHTNESS_SHIFT); 59432bcd5cbSSeth Forshee 59532bcd5cbSSeth Forshee return -EIO; 596b4f9fe12SLen Brown } 597b4f9fe12SLen Brown 59862cce752SSeth Forshee static int get_lcd_brightness(struct backlight_device *bd) 59962cce752SSeth Forshee { 60062cce752SSeth Forshee struct toshiba_acpi_dev *dev = bl_get_data(bd); 60162cce752SSeth Forshee return __get_lcd_brightness(dev); 60262cce752SSeth Forshee } 60362cce752SSeth Forshee 604936c8bcdSAlexey Dobriyan static int lcd_proc_show(struct seq_file *m, void *v) 605b4f9fe12SLen Brown { 606135740deSSeth Forshee struct toshiba_acpi_dev *dev = m->private; 607135740deSSeth Forshee int value; 608121b7b0dSAkio Idehara int levels; 609b4f9fe12SLen Brown 610135740deSSeth Forshee if (!dev->backlight_dev) 611135740deSSeth Forshee return -ENODEV; 612135740deSSeth Forshee 613121b7b0dSAkio Idehara levels = dev->backlight_dev->props.max_brightness + 1; 61462cce752SSeth Forshee value = get_lcd_brightness(dev->backlight_dev); 615b4f9fe12SLen Brown if (value >= 0) { 616936c8bcdSAlexey Dobriyan seq_printf(m, "brightness: %d\n", value); 617121b7b0dSAkio Idehara seq_printf(m, "brightness_levels: %d\n", levels); 61832bcd5cbSSeth Forshee return 0; 619b4f9fe12SLen Brown } 620b4f9fe12SLen Brown 62132bcd5cbSSeth Forshee pr_err("Error reading LCD brightness\n"); 62232bcd5cbSSeth Forshee return -EIO; 623936c8bcdSAlexey Dobriyan } 624936c8bcdSAlexey Dobriyan 625936c8bcdSAlexey Dobriyan static int lcd_proc_open(struct inode *inode, struct file *file) 626936c8bcdSAlexey Dobriyan { 627d9dda78bSAl Viro return single_open(file, lcd_proc_show, PDE_DATA(inode)); 628b4f9fe12SLen Brown } 629b4f9fe12SLen Brown 63062cce752SSeth Forshee static int set_lcd_brightness(struct toshiba_acpi_dev *dev, int value) 631b4f9fe12SLen Brown { 632b4f9fe12SLen Brown u32 hci_result; 633b4f9fe12SLen Brown 634121b7b0dSAkio Idehara if (dev->tr_backlight_supported) { 635121b7b0dSAkio Idehara bool enable = !value; 636121b7b0dSAkio Idehara int ret = set_tr_backlight_status(dev, enable); 637121b7b0dSAkio Idehara if (ret) 638121b7b0dSAkio Idehara return ret; 639121b7b0dSAkio Idehara if (value) 640121b7b0dSAkio Idehara value--; 641121b7b0dSAkio Idehara } 642121b7b0dSAkio Idehara 643b4f9fe12SLen Brown value = value << HCI_LCD_BRIGHTNESS_SHIFT; 644135740deSSeth Forshee hci_write1(dev, HCI_LCD_BRIGHTNESS, value, &hci_result); 64532bcd5cbSSeth Forshee return hci_result == HCI_SUCCESS ? 0 : -EIO; 646b4f9fe12SLen Brown } 647b4f9fe12SLen Brown 648b4f9fe12SLen Brown static int set_lcd_status(struct backlight_device *bd) 649b4f9fe12SLen Brown { 650135740deSSeth Forshee struct toshiba_acpi_dev *dev = bl_get_data(bd); 65162cce752SSeth Forshee return set_lcd_brightness(dev, bd->props.brightness); 652b4f9fe12SLen Brown } 653b4f9fe12SLen Brown 654936c8bcdSAlexey Dobriyan static ssize_t lcd_proc_write(struct file *file, const char __user *buf, 655936c8bcdSAlexey Dobriyan size_t count, loff_t *pos) 656b4f9fe12SLen Brown { 657d9dda78bSAl Viro struct toshiba_acpi_dev *dev = PDE_DATA(file_inode(file)); 658936c8bcdSAlexey Dobriyan char cmd[42]; 659936c8bcdSAlexey Dobriyan size_t len; 660b4f9fe12SLen Brown int value; 661b4f9fe12SLen Brown int ret; 662121b7b0dSAkio Idehara int levels = dev->backlight_dev->props.max_brightness + 1; 663b4f9fe12SLen Brown 664936c8bcdSAlexey Dobriyan len = min(count, sizeof(cmd) - 1); 665936c8bcdSAlexey Dobriyan if (copy_from_user(cmd, buf, len)) 666936c8bcdSAlexey Dobriyan return -EFAULT; 667936c8bcdSAlexey Dobriyan cmd[len] = '\0'; 668936c8bcdSAlexey Dobriyan 669936c8bcdSAlexey Dobriyan if (sscanf(cmd, " brightness : %i", &value) == 1 && 670121b7b0dSAkio Idehara value >= 0 && value < levels) { 67162cce752SSeth Forshee ret = set_lcd_brightness(dev, value); 672b4f9fe12SLen Brown if (ret == 0) 673b4f9fe12SLen Brown ret = count; 674b4f9fe12SLen Brown } else { 675b4f9fe12SLen Brown ret = -EINVAL; 676b4f9fe12SLen Brown } 677b4f9fe12SLen Brown return ret; 678b4f9fe12SLen Brown } 679b4f9fe12SLen Brown 680936c8bcdSAlexey Dobriyan static const struct file_operations lcd_proc_fops = { 681936c8bcdSAlexey Dobriyan .owner = THIS_MODULE, 682936c8bcdSAlexey Dobriyan .open = lcd_proc_open, 683936c8bcdSAlexey Dobriyan .read = seq_read, 684936c8bcdSAlexey Dobriyan .llseek = seq_lseek, 685936c8bcdSAlexey Dobriyan .release = single_release, 686936c8bcdSAlexey Dobriyan .write = lcd_proc_write, 687936c8bcdSAlexey Dobriyan }; 688936c8bcdSAlexey Dobriyan 68936d03f93SSeth Forshee static int get_video_status(struct toshiba_acpi_dev *dev, u32 *status) 69036d03f93SSeth Forshee { 69136d03f93SSeth Forshee u32 hci_result; 69236d03f93SSeth Forshee 69336d03f93SSeth Forshee hci_read1(dev, HCI_VIDEO_OUT, status, &hci_result); 69436d03f93SSeth Forshee return hci_result == HCI_SUCCESS ? 0 : -EIO; 69536d03f93SSeth Forshee } 69636d03f93SSeth Forshee 697936c8bcdSAlexey Dobriyan static int video_proc_show(struct seq_file *m, void *v) 698b4f9fe12SLen Brown { 699135740deSSeth Forshee struct toshiba_acpi_dev *dev = m->private; 700b4f9fe12SLen Brown u32 value; 70136d03f93SSeth Forshee int ret; 702b4f9fe12SLen Brown 70336d03f93SSeth Forshee ret = get_video_status(dev, &value); 70436d03f93SSeth Forshee if (!ret) { 705b4f9fe12SLen Brown int is_lcd = (value & HCI_VIDEO_OUT_LCD) ? 1 : 0; 706b4f9fe12SLen Brown int is_crt = (value & HCI_VIDEO_OUT_CRT) ? 1 : 0; 707b4f9fe12SLen Brown int is_tv = (value & HCI_VIDEO_OUT_TV) ? 1 : 0; 708936c8bcdSAlexey Dobriyan seq_printf(m, "lcd_out: %d\n", is_lcd); 709936c8bcdSAlexey Dobriyan seq_printf(m, "crt_out: %d\n", is_crt); 710936c8bcdSAlexey Dobriyan seq_printf(m, "tv_out: %d\n", is_tv); 711b4f9fe12SLen Brown } 712b4f9fe12SLen Brown 71336d03f93SSeth Forshee return ret; 714b4f9fe12SLen Brown } 715b4f9fe12SLen Brown 716936c8bcdSAlexey Dobriyan static int video_proc_open(struct inode *inode, struct file *file) 717b4f9fe12SLen Brown { 718d9dda78bSAl Viro return single_open(file, video_proc_show, PDE_DATA(inode)); 719936c8bcdSAlexey Dobriyan } 720936c8bcdSAlexey Dobriyan 721936c8bcdSAlexey Dobriyan static ssize_t video_proc_write(struct file *file, const char __user *buf, 722936c8bcdSAlexey Dobriyan size_t count, loff_t *pos) 723936c8bcdSAlexey Dobriyan { 724d9dda78bSAl Viro struct toshiba_acpi_dev *dev = PDE_DATA(file_inode(file)); 725936c8bcdSAlexey Dobriyan char *cmd, *buffer; 72636d03f93SSeth Forshee int ret; 727b4f9fe12SLen Brown int value; 728b4f9fe12SLen Brown int remain = count; 729b4f9fe12SLen Brown int lcd_out = -1; 730b4f9fe12SLen Brown int crt_out = -1; 731b4f9fe12SLen Brown int tv_out = -1; 732b4f9fe12SLen Brown u32 video_out; 733b4f9fe12SLen Brown 734936c8bcdSAlexey Dobriyan cmd = kmalloc(count + 1, GFP_KERNEL); 735936c8bcdSAlexey Dobriyan if (!cmd) 736936c8bcdSAlexey Dobriyan return -ENOMEM; 737936c8bcdSAlexey Dobriyan if (copy_from_user(cmd, buf, count)) { 738936c8bcdSAlexey Dobriyan kfree(cmd); 739936c8bcdSAlexey Dobriyan return -EFAULT; 740936c8bcdSAlexey Dobriyan } 741936c8bcdSAlexey Dobriyan cmd[count] = '\0'; 742936c8bcdSAlexey Dobriyan 743936c8bcdSAlexey Dobriyan buffer = cmd; 744936c8bcdSAlexey Dobriyan 745b4f9fe12SLen Brown /* scan expression. Multiple expressions may be delimited with ; 746b4f9fe12SLen Brown * 747b4f9fe12SLen Brown * NOTE: to keep scanning simple, invalid fields are ignored 748b4f9fe12SLen Brown */ 749b4f9fe12SLen Brown while (remain) { 750b4f9fe12SLen Brown if (sscanf(buffer, " lcd_out : %i", &value) == 1) 751b4f9fe12SLen Brown lcd_out = value & 1; 752b4f9fe12SLen Brown else if (sscanf(buffer, " crt_out : %i", &value) == 1) 753b4f9fe12SLen Brown crt_out = value & 1; 754b4f9fe12SLen Brown else if (sscanf(buffer, " tv_out : %i", &value) == 1) 755b4f9fe12SLen Brown tv_out = value & 1; 756b4f9fe12SLen Brown /* advance to one character past the next ; */ 757b4f9fe12SLen Brown do { 758b4f9fe12SLen Brown ++buffer; 759b4f9fe12SLen Brown --remain; 760b4f9fe12SLen Brown } 761b4f9fe12SLen Brown while (remain && *(buffer - 1) != ';'); 762b4f9fe12SLen Brown } 763b4f9fe12SLen Brown 764936c8bcdSAlexey Dobriyan kfree(cmd); 765936c8bcdSAlexey Dobriyan 76636d03f93SSeth Forshee ret = get_video_status(dev, &video_out); 76736d03f93SSeth Forshee if (!ret) { 768b4f9fe12SLen Brown unsigned int new_video_out = video_out; 769b4f9fe12SLen Brown if (lcd_out != -1) 770b4f9fe12SLen Brown _set_bit(&new_video_out, HCI_VIDEO_OUT_LCD, lcd_out); 771b4f9fe12SLen Brown if (crt_out != -1) 772b4f9fe12SLen Brown _set_bit(&new_video_out, HCI_VIDEO_OUT_CRT, crt_out); 773b4f9fe12SLen Brown if (tv_out != -1) 774b4f9fe12SLen Brown _set_bit(&new_video_out, HCI_VIDEO_OUT_TV, tv_out); 775b4f9fe12SLen Brown /* To avoid unnecessary video disruption, only write the new 776b4f9fe12SLen Brown * video setting if something changed. */ 777b4f9fe12SLen Brown if (new_video_out != video_out) 77832bcd5cbSSeth Forshee ret = write_acpi_int(METHOD_VIDEO_OUT, new_video_out); 779b4f9fe12SLen Brown } 780b4f9fe12SLen Brown 78132bcd5cbSSeth Forshee return ret ? ret : count; 782b4f9fe12SLen Brown } 783b4f9fe12SLen Brown 784936c8bcdSAlexey Dobriyan static const struct file_operations video_proc_fops = { 785936c8bcdSAlexey Dobriyan .owner = THIS_MODULE, 786936c8bcdSAlexey Dobriyan .open = video_proc_open, 787936c8bcdSAlexey Dobriyan .read = seq_read, 788936c8bcdSAlexey Dobriyan .llseek = seq_lseek, 789936c8bcdSAlexey Dobriyan .release = single_release, 790936c8bcdSAlexey Dobriyan .write = video_proc_write, 791936c8bcdSAlexey Dobriyan }; 792936c8bcdSAlexey Dobriyan 79336d03f93SSeth Forshee static int get_fan_status(struct toshiba_acpi_dev *dev, u32 *status) 79436d03f93SSeth Forshee { 79536d03f93SSeth Forshee u32 hci_result; 79636d03f93SSeth Forshee 79736d03f93SSeth Forshee hci_read1(dev, HCI_FAN, status, &hci_result); 79836d03f93SSeth Forshee return hci_result == HCI_SUCCESS ? 0 : -EIO; 79936d03f93SSeth Forshee } 80036d03f93SSeth Forshee 801936c8bcdSAlexey Dobriyan static int fan_proc_show(struct seq_file *m, void *v) 802b4f9fe12SLen Brown { 803135740deSSeth Forshee struct toshiba_acpi_dev *dev = m->private; 80436d03f93SSeth Forshee int ret; 805b4f9fe12SLen Brown u32 value; 806b4f9fe12SLen Brown 80736d03f93SSeth Forshee ret = get_fan_status(dev, &value); 80836d03f93SSeth Forshee if (!ret) { 809936c8bcdSAlexey Dobriyan seq_printf(m, "running: %d\n", (value > 0)); 810135740deSSeth Forshee seq_printf(m, "force_on: %d\n", dev->force_fan); 811b4f9fe12SLen Brown } 812b4f9fe12SLen Brown 81336d03f93SSeth Forshee return ret; 814b4f9fe12SLen Brown } 815b4f9fe12SLen Brown 816936c8bcdSAlexey Dobriyan static int fan_proc_open(struct inode *inode, struct file *file) 817b4f9fe12SLen Brown { 818d9dda78bSAl Viro return single_open(file, fan_proc_show, PDE_DATA(inode)); 819936c8bcdSAlexey Dobriyan } 820936c8bcdSAlexey Dobriyan 821936c8bcdSAlexey Dobriyan static ssize_t fan_proc_write(struct file *file, const char __user *buf, 822936c8bcdSAlexey Dobriyan size_t count, loff_t *pos) 823936c8bcdSAlexey Dobriyan { 824d9dda78bSAl Viro struct toshiba_acpi_dev *dev = PDE_DATA(file_inode(file)); 825936c8bcdSAlexey Dobriyan char cmd[42]; 826936c8bcdSAlexey Dobriyan size_t len; 827b4f9fe12SLen Brown int value; 828b4f9fe12SLen Brown u32 hci_result; 829b4f9fe12SLen Brown 830936c8bcdSAlexey Dobriyan len = min(count, sizeof(cmd) - 1); 831936c8bcdSAlexey Dobriyan if (copy_from_user(cmd, buf, len)) 832936c8bcdSAlexey Dobriyan return -EFAULT; 833936c8bcdSAlexey Dobriyan cmd[len] = '\0'; 834936c8bcdSAlexey Dobriyan 835936c8bcdSAlexey Dobriyan if (sscanf(cmd, " force_on : %i", &value) == 1 && 836b4f9fe12SLen Brown value >= 0 && value <= 1) { 837135740deSSeth Forshee hci_write1(dev, HCI_FAN, value, &hci_result); 838b4f9fe12SLen Brown if (hci_result != HCI_SUCCESS) 83932bcd5cbSSeth Forshee return -EIO; 840b4f9fe12SLen Brown else 841135740deSSeth Forshee dev->force_fan = value; 842b4f9fe12SLen Brown } else { 843b4f9fe12SLen Brown return -EINVAL; 844b4f9fe12SLen Brown } 845b4f9fe12SLen Brown 846b4f9fe12SLen Brown return count; 847b4f9fe12SLen Brown } 848b4f9fe12SLen Brown 849936c8bcdSAlexey Dobriyan static const struct file_operations fan_proc_fops = { 850936c8bcdSAlexey Dobriyan .owner = THIS_MODULE, 851936c8bcdSAlexey Dobriyan .open = fan_proc_open, 852936c8bcdSAlexey Dobriyan .read = seq_read, 853936c8bcdSAlexey Dobriyan .llseek = seq_lseek, 854936c8bcdSAlexey Dobriyan .release = single_release, 855936c8bcdSAlexey Dobriyan .write = fan_proc_write, 856936c8bcdSAlexey Dobriyan }; 857936c8bcdSAlexey Dobriyan 858936c8bcdSAlexey Dobriyan static int keys_proc_show(struct seq_file *m, void *v) 859b4f9fe12SLen Brown { 860135740deSSeth Forshee struct toshiba_acpi_dev *dev = m->private; 861b4f9fe12SLen Brown u32 hci_result; 862b4f9fe12SLen Brown u32 value; 863b4f9fe12SLen Brown 86411948b93SSeth Forshee if (!dev->key_event_valid && dev->system_event_supported) { 865135740deSSeth Forshee hci_read1(dev, HCI_SYSTEM_EVENT, &value, &hci_result); 866b4f9fe12SLen Brown if (hci_result == HCI_SUCCESS) { 867135740deSSeth Forshee dev->key_event_valid = 1; 868135740deSSeth Forshee dev->last_key_event = value; 869b4f9fe12SLen Brown } else if (hci_result == HCI_EMPTY) { 870b4f9fe12SLen Brown /* better luck next time */ 871b4f9fe12SLen Brown } else if (hci_result == HCI_NOT_SUPPORTED) { 872b4f9fe12SLen Brown /* This is a workaround for an unresolved issue on 873b4f9fe12SLen Brown * some machines where system events sporadically 874b4f9fe12SLen Brown * become disabled. */ 875135740deSSeth Forshee hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result); 8767e33460dSJoe Perches pr_notice("Re-enabled hotkeys\n"); 877b4f9fe12SLen Brown } else { 8787e33460dSJoe Perches pr_err("Error reading hotkey status\n"); 87932bcd5cbSSeth Forshee return -EIO; 880b4f9fe12SLen Brown } 881b4f9fe12SLen Brown } 882b4f9fe12SLen Brown 883135740deSSeth Forshee seq_printf(m, "hotkey_ready: %d\n", dev->key_event_valid); 884135740deSSeth Forshee seq_printf(m, "hotkey: 0x%04x\n", dev->last_key_event); 885936c8bcdSAlexey Dobriyan return 0; 886b4f9fe12SLen Brown } 887b4f9fe12SLen Brown 888936c8bcdSAlexey Dobriyan static int keys_proc_open(struct inode *inode, struct file *file) 889b4f9fe12SLen Brown { 890d9dda78bSAl Viro return single_open(file, keys_proc_show, PDE_DATA(inode)); 891936c8bcdSAlexey Dobriyan } 892936c8bcdSAlexey Dobriyan 893936c8bcdSAlexey Dobriyan static ssize_t keys_proc_write(struct file *file, const char __user *buf, 894936c8bcdSAlexey Dobriyan size_t count, loff_t *pos) 895936c8bcdSAlexey Dobriyan { 896d9dda78bSAl Viro struct toshiba_acpi_dev *dev = PDE_DATA(file_inode(file)); 897936c8bcdSAlexey Dobriyan char cmd[42]; 898936c8bcdSAlexey Dobriyan size_t len; 899b4f9fe12SLen Brown int value; 900b4f9fe12SLen Brown 901936c8bcdSAlexey Dobriyan len = min(count, sizeof(cmd) - 1); 902936c8bcdSAlexey Dobriyan if (copy_from_user(cmd, buf, len)) 903936c8bcdSAlexey Dobriyan return -EFAULT; 904936c8bcdSAlexey Dobriyan cmd[len] = '\0'; 905936c8bcdSAlexey Dobriyan 906936c8bcdSAlexey Dobriyan if (sscanf(cmd, " hotkey_ready : %i", &value) == 1 && value == 0) { 907135740deSSeth Forshee dev->key_event_valid = 0; 908b4f9fe12SLen Brown } else { 909b4f9fe12SLen Brown return -EINVAL; 910b4f9fe12SLen Brown } 911b4f9fe12SLen Brown 912b4f9fe12SLen Brown return count; 913b4f9fe12SLen Brown } 914b4f9fe12SLen Brown 915936c8bcdSAlexey Dobriyan static const struct file_operations keys_proc_fops = { 916936c8bcdSAlexey Dobriyan .owner = THIS_MODULE, 917936c8bcdSAlexey Dobriyan .open = keys_proc_open, 918936c8bcdSAlexey Dobriyan .read = seq_read, 919936c8bcdSAlexey Dobriyan .llseek = seq_lseek, 920936c8bcdSAlexey Dobriyan .release = single_release, 921936c8bcdSAlexey Dobriyan .write = keys_proc_write, 922936c8bcdSAlexey Dobriyan }; 923936c8bcdSAlexey Dobriyan 924936c8bcdSAlexey Dobriyan static int version_proc_show(struct seq_file *m, void *v) 925b4f9fe12SLen Brown { 926936c8bcdSAlexey Dobriyan seq_printf(m, "driver: %s\n", TOSHIBA_ACPI_VERSION); 927936c8bcdSAlexey Dobriyan seq_printf(m, "proc_interface: %d\n", PROC_INTERFACE_VERSION); 928936c8bcdSAlexey Dobriyan return 0; 929b4f9fe12SLen Brown } 930b4f9fe12SLen Brown 931936c8bcdSAlexey Dobriyan static int version_proc_open(struct inode *inode, struct file *file) 932936c8bcdSAlexey Dobriyan { 933d9dda78bSAl Viro return single_open(file, version_proc_show, PDE_DATA(inode)); 934936c8bcdSAlexey Dobriyan } 935936c8bcdSAlexey Dobriyan 936936c8bcdSAlexey Dobriyan static const struct file_operations version_proc_fops = { 937936c8bcdSAlexey Dobriyan .owner = THIS_MODULE, 938936c8bcdSAlexey Dobriyan .open = version_proc_open, 939936c8bcdSAlexey Dobriyan .read = seq_read, 940936c8bcdSAlexey Dobriyan .llseek = seq_lseek, 941936c8bcdSAlexey Dobriyan .release = single_release, 942936c8bcdSAlexey Dobriyan }; 943936c8bcdSAlexey Dobriyan 944b4f9fe12SLen Brown /* proc and module init 945b4f9fe12SLen Brown */ 946b4f9fe12SLen Brown 947b4f9fe12SLen Brown #define PROC_TOSHIBA "toshiba" 948b4f9fe12SLen Brown 949b859f159SGreg Kroah-Hartman static void create_toshiba_proc_entries(struct toshiba_acpi_dev *dev) 950b4f9fe12SLen Brown { 95136d03f93SSeth Forshee if (dev->backlight_dev) 952135740deSSeth Forshee proc_create_data("lcd", S_IRUGO | S_IWUSR, toshiba_proc_dir, 953135740deSSeth Forshee &lcd_proc_fops, dev); 95436d03f93SSeth Forshee if (dev->video_supported) 955135740deSSeth Forshee proc_create_data("video", S_IRUGO | S_IWUSR, toshiba_proc_dir, 956135740deSSeth Forshee &video_proc_fops, dev); 95736d03f93SSeth Forshee if (dev->fan_supported) 958135740deSSeth Forshee proc_create_data("fan", S_IRUGO | S_IWUSR, toshiba_proc_dir, 959135740deSSeth Forshee &fan_proc_fops, dev); 96036d03f93SSeth Forshee if (dev->hotkey_dev) 961135740deSSeth Forshee proc_create_data("keys", S_IRUGO | S_IWUSR, toshiba_proc_dir, 962135740deSSeth Forshee &keys_proc_fops, dev); 963135740deSSeth Forshee proc_create_data("version", S_IRUGO, toshiba_proc_dir, 964135740deSSeth Forshee &version_proc_fops, dev); 965b4f9fe12SLen Brown } 966b4f9fe12SLen Brown 96736d03f93SSeth Forshee static void remove_toshiba_proc_entries(struct toshiba_acpi_dev *dev) 968b4f9fe12SLen Brown { 96936d03f93SSeth Forshee if (dev->backlight_dev) 970936c8bcdSAlexey Dobriyan remove_proc_entry("lcd", toshiba_proc_dir); 97136d03f93SSeth Forshee if (dev->video_supported) 972936c8bcdSAlexey Dobriyan remove_proc_entry("video", toshiba_proc_dir); 97336d03f93SSeth Forshee if (dev->fan_supported) 974936c8bcdSAlexey Dobriyan remove_proc_entry("fan", toshiba_proc_dir); 97536d03f93SSeth Forshee if (dev->hotkey_dev) 976936c8bcdSAlexey Dobriyan remove_proc_entry("keys", toshiba_proc_dir); 977936c8bcdSAlexey Dobriyan remove_proc_entry("version", toshiba_proc_dir); 978b4f9fe12SLen Brown } 979b4f9fe12SLen Brown 980acc2472eSLionel Debroux static const struct backlight_ops toshiba_backlight_data = { 981121b7b0dSAkio Idehara .options = BL_CORE_SUSPENDRESUME, 98262cce752SSeth Forshee .get_brightness = get_lcd_brightness, 983b4f9fe12SLen Brown .update_status = set_lcd_status, 984b4f9fe12SLen Brown }; 985b4f9fe12SLen Brown 98629cd293fSSeth Forshee static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str, 98729cd293fSSeth Forshee struct serio *port) 98829cd293fSSeth Forshee { 98929cd293fSSeth Forshee if (str & 0x20) 99029cd293fSSeth Forshee return false; 99129cd293fSSeth Forshee 99229cd293fSSeth Forshee if (unlikely(data == 0xe0)) 99329cd293fSSeth Forshee return false; 99429cd293fSSeth Forshee 99529cd293fSSeth Forshee if ((data & 0x7f) == TOS1900_FN_SCAN) { 99629cd293fSSeth Forshee schedule_work(&toshiba_acpi->hotkey_work); 99729cd293fSSeth Forshee return true; 99829cd293fSSeth Forshee } 99929cd293fSSeth Forshee 100029cd293fSSeth Forshee return false; 100129cd293fSSeth Forshee } 100229cd293fSSeth Forshee 100329cd293fSSeth Forshee static void toshiba_acpi_hotkey_work(struct work_struct *work) 100429cd293fSSeth Forshee { 100529cd293fSSeth Forshee acpi_handle ec_handle = ec_get_handle(); 100629cd293fSSeth Forshee acpi_status status; 100729cd293fSSeth Forshee 100829cd293fSSeth Forshee if (!ec_handle) 100929cd293fSSeth Forshee return; 101029cd293fSSeth Forshee 101129cd293fSSeth Forshee status = acpi_evaluate_object(ec_handle, "NTFY", NULL, NULL); 101229cd293fSSeth Forshee if (ACPI_FAILURE(status)) 101329cd293fSSeth Forshee pr_err("ACPI NTFY method execution failed\n"); 101429cd293fSSeth Forshee } 101529cd293fSSeth Forshee 101629cd293fSSeth Forshee /* 101729cd293fSSeth Forshee * Returns hotkey scancode, or < 0 on failure. 101829cd293fSSeth Forshee */ 101929cd293fSSeth Forshee static int toshiba_acpi_query_hotkey(struct toshiba_acpi_dev *dev) 102029cd293fSSeth Forshee { 102174facaf7SZhang Rui unsigned long long value; 102229cd293fSSeth Forshee acpi_status status; 102329cd293fSSeth Forshee 102474facaf7SZhang Rui status = acpi_evaluate_integer(dev->acpi_dev->handle, "INFO", 102574facaf7SZhang Rui NULL, &value); 102674facaf7SZhang Rui if (ACPI_FAILURE(status)) { 102729cd293fSSeth Forshee pr_err("ACPI INFO method execution failed\n"); 102829cd293fSSeth Forshee return -EIO; 102929cd293fSSeth Forshee } 103029cd293fSSeth Forshee 103174facaf7SZhang Rui return value; 103229cd293fSSeth Forshee } 103329cd293fSSeth Forshee 103429cd293fSSeth Forshee static void toshiba_acpi_report_hotkey(struct toshiba_acpi_dev *dev, 103529cd293fSSeth Forshee int scancode) 103629cd293fSSeth Forshee { 103729cd293fSSeth Forshee if (scancode == 0x100) 103829cd293fSSeth Forshee return; 103929cd293fSSeth Forshee 104029cd293fSSeth Forshee /* act on key press; ignore key release */ 104129cd293fSSeth Forshee if (scancode & 0x80) 104229cd293fSSeth Forshee return; 104329cd293fSSeth Forshee 104429cd293fSSeth Forshee if (!sparse_keymap_report_event(dev->hotkey_dev, scancode, 1, true)) 104529cd293fSSeth Forshee pr_info("Unknown key %x\n", scancode); 104629cd293fSSeth Forshee } 104729cd293fSSeth Forshee 1048b859f159SGreg Kroah-Hartman static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) 10496335e4d5SMatthew Garrett { 1050135740deSSeth Forshee acpi_status status; 1051e2e19606SZhang Rui acpi_handle ec_handle; 1052135740deSSeth Forshee int error; 105329cd293fSSeth Forshee u32 hci_result; 1054135740deSSeth Forshee 1055135740deSSeth Forshee dev->hotkey_dev = input_allocate_device(); 1056b222cca6SJoe Perches if (!dev->hotkey_dev) 1057135740deSSeth Forshee return -ENOMEM; 1058135740deSSeth Forshee 1059135740deSSeth Forshee dev->hotkey_dev->name = "Toshiba input device"; 10606e02cc7eSSeth Forshee dev->hotkey_dev->phys = "toshiba_acpi/input0"; 1061135740deSSeth Forshee dev->hotkey_dev->id.bustype = BUS_HOST; 1062135740deSSeth Forshee 1063135740deSSeth Forshee error = sparse_keymap_setup(dev->hotkey_dev, toshiba_acpi_keymap, NULL); 1064135740deSSeth Forshee if (error) 1065135740deSSeth Forshee goto err_free_dev; 1066135740deSSeth Forshee 106729cd293fSSeth Forshee /* 106829cd293fSSeth Forshee * For some machines the SCI responsible for providing hotkey 106929cd293fSSeth Forshee * notification doesn't fire. We can trigger the notification 107029cd293fSSeth Forshee * whenever the Fn key is pressed using the NTFY method, if 107129cd293fSSeth Forshee * supported, so if it's present set up an i8042 key filter 107229cd293fSSeth Forshee * for this purpose. 107329cd293fSSeth Forshee */ 107429cd293fSSeth Forshee status = AE_ERROR; 107529cd293fSSeth Forshee ec_handle = ec_get_handle(); 1076e2e19606SZhang Rui if (ec_handle && acpi_has_method(ec_handle, "NTFY")) { 107729cd293fSSeth Forshee INIT_WORK(&dev->hotkey_work, toshiba_acpi_hotkey_work); 107829cd293fSSeth Forshee 107929cd293fSSeth Forshee error = i8042_install_filter(toshiba_acpi_i8042_filter); 108029cd293fSSeth Forshee if (error) { 108129cd293fSSeth Forshee pr_err("Error installing key filter\n"); 108229cd293fSSeth Forshee goto err_free_keymap; 108329cd293fSSeth Forshee } 108429cd293fSSeth Forshee 108529cd293fSSeth Forshee dev->ntfy_supported = 1; 108629cd293fSSeth Forshee } 108729cd293fSSeth Forshee 108829cd293fSSeth Forshee /* 108929cd293fSSeth Forshee * Determine hotkey query interface. Prefer using the INFO 109029cd293fSSeth Forshee * method when it is available. 109129cd293fSSeth Forshee */ 1092e2e19606SZhang Rui if (acpi_has_method(dev->acpi_dev->handle, "INFO")) 109329cd293fSSeth Forshee dev->info_supported = 1; 1094e2e19606SZhang Rui else { 109529cd293fSSeth Forshee hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result); 109629cd293fSSeth Forshee if (hci_result == HCI_SUCCESS) 109729cd293fSSeth Forshee dev->system_event_supported = 1; 109829cd293fSSeth Forshee } 109929cd293fSSeth Forshee 110029cd293fSSeth Forshee if (!dev->info_supported && !dev->system_event_supported) { 110129cd293fSSeth Forshee pr_warn("No hotkey query interface found\n"); 110229cd293fSSeth Forshee goto err_remove_filter; 110329cd293fSSeth Forshee } 110429cd293fSSeth Forshee 11056e02cc7eSSeth Forshee status = acpi_evaluate_object(dev->acpi_dev->handle, "ENAB", NULL, NULL); 1106135740deSSeth Forshee if (ACPI_FAILURE(status)) { 1107135740deSSeth Forshee pr_info("Unable to enable hotkeys\n"); 1108135740deSSeth Forshee error = -ENODEV; 110929cd293fSSeth Forshee goto err_remove_filter; 1110135740deSSeth Forshee } 1111135740deSSeth Forshee 1112135740deSSeth Forshee error = input_register_device(dev->hotkey_dev); 1113135740deSSeth Forshee if (error) { 1114135740deSSeth Forshee pr_info("Unable to register input device\n"); 111529cd293fSSeth Forshee goto err_remove_filter; 1116135740deSSeth Forshee } 1117135740deSSeth Forshee 111829cd293fSSeth Forshee hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE, &hci_result); 1119135740deSSeth Forshee return 0; 1120135740deSSeth Forshee 112129cd293fSSeth Forshee err_remove_filter: 112229cd293fSSeth Forshee if (dev->ntfy_supported) 112329cd293fSSeth Forshee i8042_remove_filter(toshiba_acpi_i8042_filter); 1124135740deSSeth Forshee err_free_keymap: 1125135740deSSeth Forshee sparse_keymap_free(dev->hotkey_dev); 1126135740deSSeth Forshee err_free_dev: 1127135740deSSeth Forshee input_free_device(dev->hotkey_dev); 1128135740deSSeth Forshee dev->hotkey_dev = NULL; 1129135740deSSeth Forshee return error; 1130135740deSSeth Forshee } 1131135740deSSeth Forshee 1132b859f159SGreg Kroah-Hartman static int toshiba_acpi_setup_backlight(struct toshiba_acpi_dev *dev) 113362cce752SSeth Forshee { 113462cce752SSeth Forshee struct backlight_properties props; 113562cce752SSeth Forshee int brightness; 113662cce752SSeth Forshee int ret; 1137121b7b0dSAkio Idehara bool enabled; 113862cce752SSeth Forshee 113962cce752SSeth Forshee /* 114062cce752SSeth Forshee * Some machines don't support the backlight methods at all, and 114162cce752SSeth Forshee * others support it read-only. Either of these is pretty useless, 114262cce752SSeth Forshee * so only register the backlight device if the backlight method 114362cce752SSeth Forshee * supports both reads and writes. 114462cce752SSeth Forshee */ 114562cce752SSeth Forshee brightness = __get_lcd_brightness(dev); 114662cce752SSeth Forshee if (brightness < 0) 114762cce752SSeth Forshee return 0; 114862cce752SSeth Forshee ret = set_lcd_brightness(dev, brightness); 114962cce752SSeth Forshee if (ret) { 115062cce752SSeth Forshee pr_debug("Backlight method is read-only, disabling backlight support\n"); 115162cce752SSeth Forshee return 0; 115262cce752SSeth Forshee } 115362cce752SSeth Forshee 1154121b7b0dSAkio Idehara /* Determine whether or not BIOS supports transflective backlight */ 1155121b7b0dSAkio Idehara ret = get_tr_backlight_status(dev, &enabled); 1156121b7b0dSAkio Idehara dev->tr_backlight_supported = !ret; 1157121b7b0dSAkio Idehara 115853039f22SMatthew Garrett memset(&props, 0, sizeof(props)); 115962cce752SSeth Forshee props.type = BACKLIGHT_PLATFORM; 116062cce752SSeth Forshee props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1; 116162cce752SSeth Forshee 1162121b7b0dSAkio Idehara /* adding an extra level and having 0 change to transflective mode */ 1163121b7b0dSAkio Idehara if (dev->tr_backlight_supported) 1164121b7b0dSAkio Idehara props.max_brightness++; 1165121b7b0dSAkio Idehara 116662cce752SSeth Forshee dev->backlight_dev = backlight_device_register("toshiba", 116762cce752SSeth Forshee &dev->acpi_dev->dev, 116862cce752SSeth Forshee dev, 116962cce752SSeth Forshee &toshiba_backlight_data, 117062cce752SSeth Forshee &props); 117162cce752SSeth Forshee if (IS_ERR(dev->backlight_dev)) { 117262cce752SSeth Forshee ret = PTR_ERR(dev->backlight_dev); 117362cce752SSeth Forshee pr_err("Could not register toshiba backlight device\n"); 117462cce752SSeth Forshee dev->backlight_dev = NULL; 117562cce752SSeth Forshee return ret; 117662cce752SSeth Forshee } 117762cce752SSeth Forshee 117862cce752SSeth Forshee dev->backlight_dev->props.brightness = brightness; 117962cce752SSeth Forshee return 0; 118062cce752SSeth Forshee } 118162cce752SSeth Forshee 118251fac838SRafael J. Wysocki static int toshiba_acpi_remove(struct acpi_device *acpi_dev) 1183135740deSSeth Forshee { 1184135740deSSeth Forshee struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); 1185135740deSSeth Forshee 118636d03f93SSeth Forshee remove_toshiba_proc_entries(dev); 1187135740deSSeth Forshee 118829cd293fSSeth Forshee if (dev->ntfy_supported) { 118929cd293fSSeth Forshee i8042_remove_filter(toshiba_acpi_i8042_filter); 119029cd293fSSeth Forshee cancel_work_sync(&dev->hotkey_work); 119129cd293fSSeth Forshee } 119229cd293fSSeth Forshee 1193135740deSSeth Forshee if (dev->hotkey_dev) { 1194135740deSSeth Forshee input_unregister_device(dev->hotkey_dev); 1195135740deSSeth Forshee sparse_keymap_free(dev->hotkey_dev); 1196135740deSSeth Forshee } 1197135740deSSeth Forshee 1198135740deSSeth Forshee if (dev->bt_rfk) { 1199135740deSSeth Forshee rfkill_unregister(dev->bt_rfk); 1200135740deSSeth Forshee rfkill_destroy(dev->bt_rfk); 1201135740deSSeth Forshee } 1202135740deSSeth Forshee 1203135740deSSeth Forshee if (dev->backlight_dev) 1204135740deSSeth Forshee backlight_device_unregister(dev->backlight_dev); 1205135740deSSeth Forshee 120636d03f93SSeth Forshee if (dev->illumination_supported) 1207135740deSSeth Forshee led_classdev_unregister(&dev->led_dev); 1208135740deSSeth Forshee 120929cd293fSSeth Forshee if (toshiba_acpi) 121029cd293fSSeth Forshee toshiba_acpi = NULL; 121129cd293fSSeth Forshee 1212135740deSSeth Forshee kfree(dev); 1213135740deSSeth Forshee 1214135740deSSeth Forshee return 0; 1215135740deSSeth Forshee } 1216135740deSSeth Forshee 1217b859f159SGreg Kroah-Hartman static const char *find_hci_method(acpi_handle handle) 1218a540d6b5SSeth Forshee { 1219e2e19606SZhang Rui if (acpi_has_method(handle, "GHCI")) 1220a540d6b5SSeth Forshee return "GHCI"; 1221a540d6b5SSeth Forshee 1222e2e19606SZhang Rui if (acpi_has_method(handle, "SPFC")) 1223a540d6b5SSeth Forshee return "SPFC"; 1224a540d6b5SSeth Forshee 1225a540d6b5SSeth Forshee return NULL; 1226a540d6b5SSeth Forshee } 1227a540d6b5SSeth Forshee 1228b859f159SGreg Kroah-Hartman static int toshiba_acpi_add(struct acpi_device *acpi_dev) 1229135740deSSeth Forshee { 1230135740deSSeth Forshee struct toshiba_acpi_dev *dev; 1231a540d6b5SSeth Forshee const char *hci_method; 123236d03f93SSeth Forshee u32 dummy; 1233135740deSSeth Forshee bool bt_present; 1234135740deSSeth Forshee int ret = 0; 1235135740deSSeth Forshee 123629cd293fSSeth Forshee if (toshiba_acpi) 123729cd293fSSeth Forshee return -EBUSY; 123829cd293fSSeth Forshee 1239135740deSSeth Forshee pr_info("Toshiba Laptop ACPI Extras version %s\n", 1240135740deSSeth Forshee TOSHIBA_ACPI_VERSION); 1241135740deSSeth Forshee 1242a540d6b5SSeth Forshee hci_method = find_hci_method(acpi_dev->handle); 1243a540d6b5SSeth Forshee if (!hci_method) { 1244a540d6b5SSeth Forshee pr_err("HCI interface not found\n"); 12456e02cc7eSSeth Forshee return -ENODEV; 1246a540d6b5SSeth Forshee } 12476e02cc7eSSeth Forshee 1248135740deSSeth Forshee dev = kzalloc(sizeof(*dev), GFP_KERNEL); 1249135740deSSeth Forshee if (!dev) 1250135740deSSeth Forshee return -ENOMEM; 1251135740deSSeth Forshee dev->acpi_dev = acpi_dev; 1252a540d6b5SSeth Forshee dev->method_hci = hci_method; 1253135740deSSeth Forshee acpi_dev->driver_data = dev; 1254135740deSSeth Forshee 12556e02cc7eSSeth Forshee if (toshiba_acpi_setup_keyboard(dev)) 1256135740deSSeth Forshee pr_info("Unable to activate hotkeys\n"); 1257135740deSSeth Forshee 1258135740deSSeth Forshee mutex_init(&dev->mutex); 1259135740deSSeth Forshee 126062cce752SSeth Forshee ret = toshiba_acpi_setup_backlight(dev); 126162cce752SSeth Forshee if (ret) 1262135740deSSeth Forshee goto error; 1263135740deSSeth Forshee 1264135740deSSeth Forshee /* Register rfkill switch for Bluetooth */ 1265135740deSSeth Forshee if (hci_get_bt_present(dev, &bt_present) == HCI_SUCCESS && bt_present) { 1266135740deSSeth Forshee dev->bt_rfk = rfkill_alloc("Toshiba Bluetooth", 1267135740deSSeth Forshee &acpi_dev->dev, 1268135740deSSeth Forshee RFKILL_TYPE_BLUETOOTH, 1269135740deSSeth Forshee &toshiba_rfk_ops, 1270135740deSSeth Forshee dev); 1271135740deSSeth Forshee if (!dev->bt_rfk) { 1272135740deSSeth Forshee pr_err("unable to allocate rfkill device\n"); 1273135740deSSeth Forshee ret = -ENOMEM; 1274135740deSSeth Forshee goto error; 1275135740deSSeth Forshee } 1276135740deSSeth Forshee 1277135740deSSeth Forshee ret = rfkill_register(dev->bt_rfk); 1278135740deSSeth Forshee if (ret) { 1279135740deSSeth Forshee pr_err("unable to register rfkill device\n"); 1280135740deSSeth Forshee rfkill_destroy(dev->bt_rfk); 1281135740deSSeth Forshee goto error; 1282135740deSSeth Forshee } 1283135740deSSeth Forshee } 1284135740deSSeth Forshee 1285135740deSSeth Forshee if (toshiba_illumination_available(dev)) { 1286135740deSSeth Forshee dev->led_dev.name = "toshiba::illumination"; 1287135740deSSeth Forshee dev->led_dev.max_brightness = 1; 1288135740deSSeth Forshee dev->led_dev.brightness_set = toshiba_illumination_set; 1289135740deSSeth Forshee dev->led_dev.brightness_get = toshiba_illumination_get; 1290135740deSSeth Forshee if (!led_classdev_register(&acpi_dev->dev, &dev->led_dev)) 129136d03f93SSeth Forshee dev->illumination_supported = 1; 1292135740deSSeth Forshee } 1293135740deSSeth Forshee 129436d03f93SSeth Forshee /* Determine whether or not BIOS supports fan and video interfaces */ 129536d03f93SSeth Forshee 129636d03f93SSeth Forshee ret = get_video_status(dev, &dummy); 129736d03f93SSeth Forshee dev->video_supported = !ret; 129836d03f93SSeth Forshee 129936d03f93SSeth Forshee ret = get_fan_status(dev, &dummy); 130036d03f93SSeth Forshee dev->fan_supported = !ret; 130136d03f93SSeth Forshee 130236d03f93SSeth Forshee create_toshiba_proc_entries(dev); 130336d03f93SSeth Forshee 130429cd293fSSeth Forshee toshiba_acpi = dev; 130529cd293fSSeth Forshee 1306135740deSSeth Forshee return 0; 1307135740deSSeth Forshee 1308135740deSSeth Forshee error: 130951fac838SRafael J. Wysocki toshiba_acpi_remove(acpi_dev); 1310135740deSSeth Forshee return ret; 1311135740deSSeth Forshee } 1312135740deSSeth Forshee 1313135740deSSeth Forshee static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event) 1314135740deSSeth Forshee { 1315135740deSSeth Forshee struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); 13166335e4d5SMatthew Garrett u32 hci_result, value; 131711948b93SSeth Forshee int retries = 3; 131829cd293fSSeth Forshee int scancode; 13196335e4d5SMatthew Garrett 132029cd293fSSeth Forshee if (event != 0x80) 13216335e4d5SMatthew Garrett return; 132211948b93SSeth Forshee 132329cd293fSSeth Forshee if (dev->info_supported) { 132429cd293fSSeth Forshee scancode = toshiba_acpi_query_hotkey(dev); 132529cd293fSSeth Forshee if (scancode < 0) 132629cd293fSSeth Forshee pr_err("Failed to query hotkey event\n"); 132729cd293fSSeth Forshee else if (scancode != 0) 132829cd293fSSeth Forshee toshiba_acpi_report_hotkey(dev, scancode); 132929cd293fSSeth Forshee } else if (dev->system_event_supported) { 13306335e4d5SMatthew Garrett do { 1331135740deSSeth Forshee hci_read1(dev, HCI_SYSTEM_EVENT, &value, &hci_result); 133211948b93SSeth Forshee switch (hci_result) { 133311948b93SSeth Forshee case HCI_SUCCESS: 133429cd293fSSeth Forshee toshiba_acpi_report_hotkey(dev, (int)value); 133511948b93SSeth Forshee break; 133611948b93SSeth Forshee case HCI_NOT_SUPPORTED: 133729cd293fSSeth Forshee /* 133829cd293fSSeth Forshee * This is a workaround for an unresolved 133929cd293fSSeth Forshee * issue on some machines where system events 134029cd293fSSeth Forshee * sporadically become disabled. 134129cd293fSSeth Forshee */ 134229cd293fSSeth Forshee hci_write1(dev, HCI_SYSTEM_EVENT, 1, 134329cd293fSSeth Forshee &hci_result); 13447e33460dSJoe Perches pr_notice("Re-enabled hotkeys\n"); 134511948b93SSeth Forshee /* fall through */ 134611948b93SSeth Forshee default: 134711948b93SSeth Forshee retries--; 134811948b93SSeth Forshee break; 13496335e4d5SMatthew Garrett } 135011948b93SSeth Forshee } while (retries && hci_result != HCI_EMPTY); 13516335e4d5SMatthew Garrett } 135229cd293fSSeth Forshee } 13536335e4d5SMatthew Garrett 13543567a4e2SRafael J. Wysocki #ifdef CONFIG_PM_SLEEP 135543d2fd3bSRafael J. Wysocki static int toshiba_acpi_suspend(struct device *device) 135629cd293fSSeth Forshee { 135743d2fd3bSRafael J. Wysocki struct toshiba_acpi_dev *dev = acpi_driver_data(to_acpi_device(device)); 135829cd293fSSeth Forshee u32 result; 135929cd293fSSeth Forshee 136029cd293fSSeth Forshee if (dev->hotkey_dev) 136129cd293fSSeth Forshee hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_DISABLE, &result); 136229cd293fSSeth Forshee 136329cd293fSSeth Forshee return 0; 136429cd293fSSeth Forshee } 136529cd293fSSeth Forshee 136643d2fd3bSRafael J. Wysocki static int toshiba_acpi_resume(struct device *device) 136729cd293fSSeth Forshee { 136843d2fd3bSRafael J. Wysocki struct toshiba_acpi_dev *dev = acpi_driver_data(to_acpi_device(device)); 136929cd293fSSeth Forshee u32 result; 137029cd293fSSeth Forshee 137129cd293fSSeth Forshee if (dev->hotkey_dev) 137229cd293fSSeth Forshee hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE, &result); 137329cd293fSSeth Forshee 137429cd293fSSeth Forshee return 0; 137529cd293fSSeth Forshee } 13763567a4e2SRafael J. Wysocki #endif 13776335e4d5SMatthew Garrett 137843d2fd3bSRafael J. Wysocki static SIMPLE_DEV_PM_OPS(toshiba_acpi_pm, 137943d2fd3bSRafael J. Wysocki toshiba_acpi_suspend, toshiba_acpi_resume); 138043d2fd3bSRafael J. Wysocki 1381135740deSSeth Forshee static struct acpi_driver toshiba_acpi_driver = { 1382135740deSSeth Forshee .name = "Toshiba ACPI driver", 1383135740deSSeth Forshee .owner = THIS_MODULE, 1384135740deSSeth Forshee .ids = toshiba_device_ids, 1385135740deSSeth Forshee .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, 1386135740deSSeth Forshee .ops = { 1387135740deSSeth Forshee .add = toshiba_acpi_add, 1388135740deSSeth Forshee .remove = toshiba_acpi_remove, 1389135740deSSeth Forshee .notify = toshiba_acpi_notify, 1390135740deSSeth Forshee }, 139143d2fd3bSRafael J. Wysocki .drv.pm = &toshiba_acpi_pm, 1392135740deSSeth Forshee }; 1393b4f9fe12SLen Brown 1394b4f9fe12SLen Brown static int __init toshiba_acpi_init(void) 1395b4f9fe12SLen Brown { 1396135740deSSeth Forshee int ret; 1397b4f9fe12SLen Brown 1398f11f999eSSeth Forshee /* 1399f11f999eSSeth Forshee * Machines with this WMI guid aren't supported due to bugs in 1400f11f999eSSeth Forshee * their AML. This check relies on wmi initializing before 1401f11f999eSSeth Forshee * toshiba_acpi to guarantee guids have been identified. 1402f11f999eSSeth Forshee */ 1403f11f999eSSeth Forshee if (wmi_has_guid(TOSHIBA_WMI_EVENT_GUID)) 1404f11f999eSSeth Forshee return -ENODEV; 1405f11f999eSSeth Forshee 1406b4f9fe12SLen Brown toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir); 1407b4f9fe12SLen Brown if (!toshiba_proc_dir) { 1408135740deSSeth Forshee pr_err("Unable to create proc dir " PROC_TOSHIBA "\n"); 1409b4f9fe12SLen Brown return -ENODEV; 1410b4f9fe12SLen Brown } 1411b4f9fe12SLen Brown 1412135740deSSeth Forshee ret = acpi_bus_register_driver(&toshiba_acpi_driver); 1413b4f9fe12SLen Brown if (ret) { 1414135740deSSeth Forshee pr_err("Failed to register ACPI driver: %d\n", ret); 1415135740deSSeth Forshee remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); 1416135740deSSeth Forshee } 1417135740deSSeth Forshee 1418b4f9fe12SLen Brown return ret; 1419b4f9fe12SLen Brown } 1420b4f9fe12SLen Brown 1421135740deSSeth Forshee static void __exit toshiba_acpi_exit(void) 1422135740deSSeth Forshee { 1423135740deSSeth Forshee acpi_bus_unregister_driver(&toshiba_acpi_driver); 1424135740deSSeth Forshee if (toshiba_proc_dir) 1425135740deSSeth Forshee remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); 1426b4f9fe12SLen Brown } 1427b4f9fe12SLen Brown 1428b4f9fe12SLen Brown module_init(toshiba_acpi_init); 1429b4f9fe12SLen Brown module_exit(toshiba_acpi_exit); 1430