/* * acpi.c -- ACPI methods low-level access code for TSM70 class laptops * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2, or (at your option) any * later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * Written by Mathieu BĂ©rard , 2006 * */ #include "omnibook.h" #include "hardware.h" #ifdef CONFIG_ACPI #include #include /* copied from drivers/input/serio/i8042-io.h */ #define I8042_KBD_PHYS_DESC "isa0060/serio0" /* * ACPI backend masks and strings */ #define GET_WIRELESS_METHOD "ANTR" #define SET_WIRELESS_METHOD "ANTW" #define WLEX_MASK 0x4 #define WLAT_MASK 0x1 #define BTEX_MASK 0x8 #define BTAT_MASK 0x2 #define KLSW_MASK 0x10 #define GET_DISPLAY_METHOD "DOSS" #define SET_DISPLAY_METHOD "DOSW" /* Display reading masks CADL = detected, CSTE = enabled */ #define LCD_CADL 0x10 #define CRT_CADL 0x20 #define TVO_CADL 0x40 #define DVI_CADL 0x80 #define LCD_CSTE 0x1 #define CRT_CSTE 0x2 #define TVO_CSTE 0x4 #define DVI_CSTE 0x8 /* TSX205 Video-Out methods and return values */ #define TSX205_SET_DISPLAY_METHOD "STBL" #define TSX205_SLI_DISPLAY_METHOD "SL01.VGA1.STBL" /* NOTE: Method DSSW seems to be some sort of auto-detect method */ #define TSX205_AUTO_DISPLAY_METHOD "DSSW" #define TSX205_DSPY_DE 0x1F /* DE - Detected and Enabled */ #define TSX205_DSPY_DN 0x1D /* DN - Detected and Not enabled */ #define TSX205_DSPY_NE 0x0F /* NE - Not detected and Enabled */ #define TSX205_DSPY_NN 0x0D /* NN - Not detected and Not enabled */ #define GET_THROTTLE_METHOD "THRO" #define SET_THROTTLE_METHOD "CLCK" static char ec_dev_list[][20] = { "\\_SB.PCI0.LPCB.EC0", "\\_SB.PCI0.LPC0.EC0", }; /* TSX205 HCI and display handles */ static char tsx205_dev_list[][20] = { "\\_SB.VALZ", "\\_SB.PCI0.PEGP.VGA" }; /* TSX205 GET video-out methods */ static char tsx205_video_list[][20] = { "LCD._DCS", "CRT._DCS", "TV._DCS", "DVI._DCS", "SL01.VGA1.LCD._DCS", "SL01.VGA1.CRT._DCS", "SL01.VGA1.TV._DCS", "SL01.VGA1.DVI._DCS", }; #define TOSHIBA_ACPI_BT_CLASS "bluetooth" #define TOSHIBA_ACPI_DEVICE_NAME "bluetooth adapter" #define TOSH_BT_ACTIVATE_USB "AUSB" #define TOSH_BT_DISABLE_USB "DUSB" #define TOSH_BT_POWER_ON "BTPO" #define TOSH_BT_POWER_OFF "BTPF" #define TOSH_BT_STATUS "BTST" #define TOSH_BT_KSST_MASK 0x1 #define TOSH_BT_USB_MASK 0x40 #define TOSH_BT_POWER_MASK 0x80 /* * ACPI driver for Toshiba Bluetooth device */ static int omnibook_acpi_bt_add(struct acpi_device *device); static int omnibook_acpi_bt_remove(struct acpi_device *device, int type); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) static const struct acpi_device_id omnibook_bt_ids[] = { {"TOS6205", 0}, {"", 0}, }; static struct acpi_driver omnibook_bt_driver = { .name = OMNIBOOK_MODULE_NAME, .class = TOSHIBA_ACPI_BT_CLASS, .ids = omnibook_bt_ids, .ops = { .add = omnibook_acpi_bt_add, .remove = omnibook_acpi_bt_remove, }, }; #else /* 2.6.23 */ static struct acpi_driver omnibook_bt_driver = { .name = OMNIBOOK_MODULE_NAME, .class = TOSHIBA_ACPI_BT_CLASS, .ids = "TOS6205", .ops = { .add = omnibook_acpi_bt_add, .remove = omnibook_acpi_bt_remove, }, }; #endif /* 2.6.23 */ /* * ACPI backend private data structure */ struct acpi_backend_data { acpi_handle ec_handle; /* Handle on ACPI EC device */ acpi_handle bt_handle; /* Handle on ACPI BT device */ acpi_handle hci_handle; /* Handle on ACPI HCI device */ acpi_handle dis_handle; /* Handle on ACPI Display device */ unsigned has_antr_antw:1; /* Are there ANTR/ANTW methods in the EC device ? */ unsigned has_doss_dosw:1; /* Are there DOSS/DOSW methods in the EC device ? */ unsigned has_sli:1; /* Does the laptop has SLI enabled ? */ struct input_dev *acpi_input_dev; struct work_struct fnkey_work; }; /* * Hotkeys workflow: * 1. Fn+Foo pressed * 2. Scancode 0x6e generated by kbd controller * 3. Scancode 0x6e caught by omnibook input handler * 4. INFO method has keycode of last actually pressed Fn key * 5. acpi_scan_table used to associate a detected keycode with a generated one * 6. Generated keycode issued using the omnibook input device */ /* * The input handler should only bind with the standard AT keyboard. * XXX: Scancode 0x6e won't be detected if the keyboard has already been * grabbed (the Xorg event input driver do that) */ #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,21)) static int hook_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id) #elif (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)) static struct input_handle *hook_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id) #else static struct input_handle *hook_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id) #endif { struct input_handle *handle; int error; /* the 0x0001 vendor magic number is found in atkbd.c */ if(!(dev->id.bustype == BUS_I8042 && dev->id.vendor == 0x0001)) goto out_nobind; if(!strstr(dev->phys, I8042_KBD_PHYS_DESC)) goto out_nobind; dprintk("hook_connect for device %s.\n", dev->name); if(dev->grab) printk(O_WARN "Input device is grabbed by %s, Fn hotkeys won't work.\n", dev->grab->name); handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); if (!handle) #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,21)) return -ENOMEM; #else return NULL; #endif handle->dev = dev; handle->handler = handler; handle->name = "omnibook_scancode_hook"; handle->private = handler->private; #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,21)) error = input_register_handle(handle); if (error) { dprintk("register_handle failed\n"); goto out_nobind_free; } error = input_open_device(handle); if (error) { dprintk("register_handle failed\n"); input_unregister_handle(handle); goto out_nobind_free; } #else status=input_open_device(handle); if (error==0) dprintk("Input device opened\n"); else { dprintk("opening input device failed\n"); goto out_nobind_free; } #endif #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,21)) return 0; out_nobind_free: kfree(handle); out_nobind: return -ENODEV; #else return handle; out_nobind_free: kfree(handle); out_nobind: return NULL; #endif } static void hook_disconnect(struct input_handle *handle) { dprintk("hook_disconnect.\n"); input_close_device(handle); #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,21)) input_unregister_handle(handle); #endif kfree(handle); } /* * Hook for scancode 0x6e. Actual handling is done in a workqueue. */ static void hook_event(struct input_handle *handle, unsigned int event_type, unsigned int event_code, int value) { if (event_type == EV_MSC && event_code == MSC_SCAN && value == ACPI_FN_SCAN) schedule_work(&((struct acpi_backend_data *)handle->private)->fnkey_work); } #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)) static const struct input_device_id hook_ids[] = { #else static struct input_device_id hook_ids[] = { #endif { .flags = INPUT_DEVICE_ID_MATCH_EVBIT, .evbit = { BIT(EV_KEY) }, }, { }, /* Terminating entry */ }; static struct input_handler hook_handler = { .event = hook_event, .connect = hook_connect, .disconnect = hook_disconnect, .name = OMNIBOOK_MODULE_NAME, .id_table = hook_ids, }; /* * Detected scancode to keycode table */ static const struct { unsigned int scancode; unsigned int keycode; } acpi_scan_table[] = { { HCI_FN_RELEASED, KEY_FN}, { HCI_MUTE, KEY_MUTE}, { HCI_BREAK, KEY_COFFEE}, { HCI_1, KEY_ZOOMOUT}, { HCI_2, KEY_ZOOMIN}, { HCI_SPACE, KEY_ZOOMRESET}, { HCI_BSM, KEY_BATTERY}, { HCI_SUSPEND, KEY_SLEEP}, { HCI_HIBERNATE, KEY_SUSPEND}, { HCI_VIDEOOUT, KEY_SWITCHVIDEOMODE}, { HCI_BRIGHTNESSDOWN, KEY_BRIGHTNESSDOWN}, { HCI_BRIGHTNESSUP, KEY_BRIGHTNESSUP}, { HCI_WLAN, KEY_WLAN}, { HCI_TOUCHPAD, KEY_PROG1}, { HCI_FN_PRESSED, KEY_FN}, { 0, 0}, }; #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,19)) static void omnibook_handle_fnkey(struct work_struct *work); #else static void omnibook_handle_fnkey(void* data); #endif /* * Register the input handler and the input device in the input subsystem */ static int register_input_subsystem(struct acpi_backend_data *priv_data) { int i, retval = 0; struct input_dev *acpi_input_dev; acpi_input_dev = input_allocate_device(); if (!acpi_input_dev) { retval = -ENOMEM; goto out; } acpi_input_dev->name = "Omnibook ACPI scancode generator"; acpi_input_dev->phys = "omnibook/input0"; acpi_input_dev->id.bustype = BUS_HOST; set_bit(EV_KEY, acpi_input_dev->evbit); for(i=0 ; i < ARRAY_SIZE(acpi_scan_table); i++) set_bit(acpi_scan_table[i].keycode, acpi_input_dev->keybit); retval = input_register_device(acpi_input_dev); if (retval) { input_free_device(acpi_input_dev); goto out; } priv_data->acpi_input_dev = acpi_input_dev; #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,19)) INIT_WORK(&priv_data->fnkey_work, *omnibook_handle_fnkey); #else INIT_WORK(&priv_data->fnkey_work, *omnibook_handle_fnkey, priv_data); #endif hook_handler.private = priv_data; #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)) retval = input_register_handler(&hook_handler); #else input_register_handler(&hook_handler); #endif out: return retval; } /* * Execute an ACPI method which return either an integer or nothing * and that require 0 or 1 numerical argument * (acpi_evaluate_object wrapper) */ static int omnibook_acpi_execute(acpi_handle dev_handle, char *method, const int *param, int *result) { struct acpi_object_list args_list; struct acpi_buffer buff; union acpi_object arg, out_objs[1]; if (param) { args_list.count = 1; args_list.pointer = &arg; arg.type = ACPI_TYPE_INTEGER; arg.integer.value = *param; } else args_list.count = 0; buff.length = sizeof(out_objs); buff.pointer = out_objs; if (acpi_evaluate_object(dev_handle, method, &args_list, &buff) != AE_OK) { printk(O_ERR "ACPI method execution failed\n"); return -EIO; } if (!result) /* We don't care what the method returned here */ return 0; if (out_objs[0].type != ACPI_TYPE_INTEGER) { printk(O_ERR "ACPI method result is not a number\n"); return -EINVAL; } *result = out_objs[0].integer.value; return 0; } /* * Probe for expected ACPI devices */ static int omnibook_acpi_init(const struct omnibook_operation *io_op) { int retval = 0; acpi_handle dev_handle, method_handle, hci_handle, dis_handle; int i; int has_sli = 0; struct acpi_backend_data *priv_data; if (unlikely(acpi_disabled)) { printk(O_ERR "ACPI is disabled: feature unavailable.\n"); return -ENODEV; } if (!io_op->backend->data) { dprintk("Try to init ACPI backend\n"); mutex_init(&io_op->backend->mutex); mutex_lock(&io_op->backend->mutex); kref_init(&io_op->backend->kref); priv_data = kzalloc(sizeof(struct acpi_backend_data), GFP_KERNEL); if (!priv_data) { retval = -ENOMEM; goto error0; } /* Locate ACPI EC device, acpi_get_handle set dev_handle to NULL if not found */ for (i = 0; i < ARRAY_SIZE(ec_dev_list); i++) { if (acpi_get_handle(NULL, ec_dev_list[i], &dev_handle) == AE_OK) { dprintk("ACPI EC device found\n"); priv_data->ec_handle = dev_handle; break; } } if (!dev_handle) { printk(O_ERR "Can't get handle on ACPI EC device.\n"); retval = -ENODEV; goto error1; } /* Probe for HCI and Display devices only on TSX205 models */ if (omnibook_ectype & TSX205) { if (acpi_get_handle(NULL, tsx205_dev_list[0], &hci_handle) == AE_OK) { dprintk("Toshiba X205 HCI device found\n"); priv_data->hci_handle = hci_handle; } if (!hci_handle) { printk(O_ERR "Couldn't get HCI handle.\n"); retval = -ENODEV; goto error1; } if (acpi_get_handle(NULL, tsx205_dev_list[1], &dis_handle) == AE_OK) priv_data->dis_handle = dis_handle; if (!dis_handle) { printk(O_ERR "Couldn't get X205 Display handle.\n"); retval = -ENODEV; goto error1; } /* Does the laptop has SLI enabled? */ omnibook_acpi_execute(dis_handle, (char *)TSX205_SLIVDO_METHOD, NULL, &has_sli); if (has_sli) dprintk("Toshiba X205 Display device found (SLI).\n"); else dprintk("Toshiba X205 Display device found.\n"); priv_data->has_sli = has_sli; } if ((acpi_get_handle( dev_handle, GET_WIRELESS_METHOD, &method_handle) == AE_OK) && (acpi_get_handle( dev_handle, SET_WIRELESS_METHOD, &method_handle) == AE_OK)) priv_data->has_antr_antw = 1; if (omnibook_ectype & TSX205) { if ((acpi_get_handle(dis_handle, TSX205_AUTO_DISPLAY_METHOD, &method_handle) == AE_OK) && (acpi_get_handle(dis_handle, TSX205_AUTO_DISPLAY_METHOD, &method_handle) == AE_OK)) priv_data->has_doss_dosw = 1; } else { if ((acpi_get_handle( dev_handle, GET_DISPLAY_METHOD, &method_handle) == AE_OK) && (acpi_get_handle( dev_handle, SET_DISPLAY_METHOD, &method_handle) == AE_OK)) priv_data->has_doss_dosw = 1; } retval = register_input_subsystem(priv_data); if(retval) goto error1; io_op->backend->data = (void *) priv_data; mutex_unlock(&io_op->backend->mutex); /* attempt to register Toshiba bluetooth ACPI driver */ acpi_bus_register_driver(&omnibook_bt_driver); dprintk("ACPI backend init OK\n"); return 0; } else { dprintk("ACPI backend has already been initialized\n"); kref_get(&io_op->backend->kref); return 0; } error1: kfree(priv_data); io_op->backend->data = NULL; error0: mutex_unlock(&io_op->backend->mutex); mutex_destroy(&io_op->backend->mutex); return retval; } static void omnibook_acpi_free(struct kref *ref) { struct omnibook_backend *backend; struct acpi_backend_data *priv_data; backend = container_of(ref, struct omnibook_backend, kref); priv_data = backend->data; dprintk("ACPI backend not used anymore: disposing\n"); dprintk("ptr addr: %p driver name: %s\n",&omnibook_bt_driver, omnibook_bt_driver.name); acpi_bus_unregister_driver(&omnibook_bt_driver); flush_scheduled_work(); input_unregister_handler(&hook_handler); input_unregister_device(priv_data->acpi_input_dev); mutex_lock(&backend->mutex); kfree(backend->data); backend->data = NULL; mutex_unlock(&backend->mutex); mutex_destroy(&backend->mutex); } static void omnibook_acpi_exit(const struct omnibook_operation *io_op) { dprintk("Trying to dispose ACPI backend\n"); kref_put(&io_op->backend->kref, omnibook_acpi_free); } /* forward declaration */ struct omnibook_backend acpi_backend; /* Function taken from toshiba_acpi */ static acpi_status hci_raw(const u32 in[HCI_WORDS], u32 out[HCI_WORDS]) { struct acpi_backend_data *priv_data = acpi_backend.data; struct acpi_object_list params; union acpi_object in_objs[HCI_WORDS]; struct acpi_buffer results; union acpi_object out_objs[HCI_WORDS + 1]; acpi_status status; int i; params.count = HCI_WORDS; params.pointer = in_objs; for (i = 0; i < HCI_WORDS; ++i) { in_objs[i].type = ACPI_TYPE_INTEGER; in_objs[i].integer.value = in[i]; } results.length = sizeof(out_objs); results.pointer = out_objs; status = acpi_evaluate_object(priv_data->hci_handle, (char *)HCI_METHOD, ¶ms, &results); if ((status == AE_OK) && (out_objs->package.count <= HCI_WORDS)) { for (i = 0; i < out_objs->package.count; ++i) { out[i] = out_objs->package.elements[i].integer.value; } } return status; } /* * Set Bluetooth device state using the Toshiba BT device */ static int set_bt_status(const struct acpi_backend_data *priv_data, unsigned int state) { int retval = 0; if (state) { retval = omnibook_acpi_execute(priv_data->bt_handle, TOSH_BT_ACTIVATE_USB, NULL, NULL); if (retval) goto out; retval = omnibook_acpi_execute(priv_data->bt_handle, TOSH_BT_POWER_ON, NULL, NULL); if (retval) goto out; } else { retval = omnibook_acpi_execute(priv_data->bt_handle, TOSH_BT_DISABLE_USB, NULL, NULL); if (retval) goto out; retval = omnibook_acpi_execute(priv_data->bt_handle, TOSH_BT_POWER_OFF, NULL, NULL); if (retval) goto out; } out: return retval; } static int omnibook_acpi_bt_add(struct acpi_device *device) { int retval; struct acpi_backend_data *priv_data = acpi_backend.data; dprintk("Enabling Toshiba Bluetooth ACPI device.\n"); strcpy(acpi_device_name(device), TOSHIBA_ACPI_DEVICE_NAME); strcpy(acpi_device_class(device), TOSHIBA_ACPI_BT_CLASS); /* Save handle in backend private data structure. ugly. */ mutex_lock(&acpi_backend.mutex); priv_data->bt_handle = device->handle; retval = set_bt_status(priv_data, 1); mutex_unlock(&acpi_backend.mutex); return retval; } static int omnibook_acpi_bt_remove(struct acpi_device *device, int type) { int retval; struct acpi_backend_data *priv_data = acpi_backend.data; mutex_lock(&acpi_backend.mutex); dprintk("Disabling Toshiba Bluetooth ACPI device.\n"); retval = set_bt_status(priv_data, 0); priv_data->bt_handle = NULL; mutex_unlock(&acpi_backend.mutex); return retval; } /* * Get Bluetooth status using the BTST method */ static int get_bt_status(const struct acpi_backend_data *priv_data, unsigned int *state) { int retval = 0; int raw_state; if ((retval = omnibook_acpi_execute(priv_data->bt_handle, TOSH_BT_STATUS, NULL, &raw_state))) return retval; dprintk("BTST raw_state: %x\n", raw_state); *state = BT_EX; *state |= ((raw_state & TOSH_BT_USB_MASK) && (raw_state & TOSH_BT_POWER_MASK)) ? BT_STA : 0; return retval; } /* * Get the Bluetooth + Wireless status using the ANTR method * FIXME: what if ANTR and BTST disagree ? we thrust ANTR for now */ static int get_wireless_status(const struct acpi_backend_data *priv_data, unsigned int *state) { int retval = 0; int raw_state; if ((retval = omnibook_acpi_execute(priv_data->ec_handle, GET_WIRELESS_METHOD, NULL, &raw_state))) return retval; dprintk("get_wireless raw_state: %x\n", raw_state); *state = (raw_state & WLEX_MASK) ? WIFI_EX : 0; *state |= (raw_state & WLAT_MASK) ? WIFI_STA : 0; *state |= (raw_state & KLSW_MASK) ? KILLSWITCH : 0; *state |= (raw_state & BTEX_MASK) ? BT_EX : 0; *state |= (raw_state & BTAT_MASK) ? BT_STA : 0; return retval; } static int get_tsx205_wireless_status(const struct acpi_backend_data *priv_data, unsigned int *state) { int retval = 0; int raw_state; u32 in[HCI_WORDS] = { HCI_GET, HCI_RF_CONTROL, 0, HCI_WIRELESS_CHECK, 0, 0 }; u32 out[HCI_WORDS]; hci_raw(in, out); /* Now let's check the killswitch */ if ((retval = omnibook_acpi_execute(priv_data->ec_handle, TSX205_KILLSW_METHOD, NULL, &raw_state))) return retval; dprintk("get_wireless raw_state: %x\n", out[2]); *state = ((out[2] & 0xff)) ? WIFI_EX : 0; *state |= (raw_state) ? WIFI_STA : 0; *state |= (!raw_state) ? KILLSWITCH : 0; /* And finally BT */ if ((retval = omnibook_acpi_execute(priv_data->bt_handle, TOSH_BT_STATUS, NULL, &raw_state))) return retval; *state |= BT_EX; *state |= ((raw_state & TOSH_BT_USB_MASK) && (raw_state & TOSH_BT_POWER_MASK)) ? BT_STA : 0; return retval; } static int omnibook_acpi_get_wireless(const struct omnibook_operation *io_op, unsigned int *state) { int retval; struct acpi_backend_data *priv_data = io_op->backend->data; /* use BTST (BT device) if we don't have ANTR/ANTW (EC device) */ if (omnibook_ectype & TSX205) retval = get_tsx205_wireless_status(priv_data, state); else if (priv_data->has_antr_antw) retval = get_wireless_status(priv_data, state); else if(priv_data->bt_handle) retval = get_bt_status(priv_data, state); else retval = -ENODEV; return retval; } /* * Set the Bluetooth + Wireless status using the ANTW method */ static int set_wireless_status(const struct acpi_backend_data *priv_data, unsigned int state) { int retval; int raw_state; raw_state = !!(state & WIFI_STA); /* bit 0 */ raw_state |= !!(state & BT_STA) << 0x1; /* bit 1 */ dprintk("set_wireless raw_state: %x\n", raw_state); retval = omnibook_acpi_execute(priv_data->ec_handle, SET_WIRELESS_METHOD, &raw_state, NULL); return retval; } static int set_tsx205_wireless_status(const struct acpi_backend_data *priv_data, unsigned int state) { int retval; int raw_state = !!(state & WIFI_STA); dprintk("set_wireless raw_state: %x\n", raw_state); u32 in[HCI_WORDS] = { HCI_SET, HCI_RF_CONTROL, raw_state, HCI_WIRELESS_POWER, 0, 0 }; u32 out[HCI_WORDS]; hci_raw(in, out); raw_state |= !!(state & BT_STA) << 0x1; /* bit 1 */ /* BT status */ retval = set_bt_status(priv_data->bt_handle, state); return retval; } static int omnibook_acpi_set_wireless(const struct omnibook_operation *io_op, unsigned int state) { int retval = -ENODEV; struct acpi_backend_data *priv_data = io_op->backend->data; /* First try the TSX205 methods */ if(omnibook_ectype & TSX205) retval = set_tsx205_wireless_status(priv_data, state); /* Then try the ANTR/ANTW methods */ if(priv_data->has_antr_antw) retval = set_wireless_status(priv_data, state); /* Then try the bluetooth ACPI device if present */ if(priv_data->bt_handle) retval = set_bt_status(priv_data, (state & BT_STA)); return retval; } static int tsx205_get_display(const struct acpi_backend_data *priv_data, unsigned int *state, unsigned int device) { int retval = 0; int raw_state = 0; retval = omnibook_acpi_execute(priv_data->dis_handle, tsx205_video_list[device], NULL, &raw_state); if (retval < 0) { dprintk(O_ERR "Failed to get video device (%d) state.\n", device); return retval; } /* Ugly, but better than nothing... */ switch (device) { case 0: case 4: /* LCD device */ dprintk("get_display LCD (%d) raw_state: %x\n", device, raw_state); if (raw_state == TSX205_DSPY_DE) { *state |= DISPLAY_LCD_DET; *state |= DISPLAY_LCD_ON; } else if (raw_state == TSX205_DSPY_DN) *state |= DISPLAY_LCD_DET; else if (raw_state == TSX205_DSPY_NE) *state |= DISPLAY_LCD_ON; break; case 1: case 5: /* CRT device */ dprintk("get_display CRT (%d) raw_state: %x\n", device, raw_state); if (raw_state == TSX205_DSPY_DE) { *state |= DISPLAY_CRT_DET; *state |= DISPLAY_CRT_ON; } else if (raw_state == TSX205_DSPY_DN) *state |= DISPLAY_CRT_DET; else if (raw_state == TSX205_DSPY_NE) *state |= DISPLAY_CRT_ON; break; case 2: case 6: /* TV-OUT device */ dprintk("get_display TV-OUT (%d) raw_state: %x\n", device, raw_state); if (raw_state == TSX205_DSPY_DE) { *state |= DISPLAY_TVO_DET; *state |= DISPLAY_TVO_ON; } else if (raw_state == TSX205_DSPY_DN) *state |= DISPLAY_TVO_DET; else if (raw_state == TSX205_DSPY_NE) *state |= DISPLAY_TVO_ON; break; case 3: case 7: /* DVI device */ dprintk("get_display DVI (%d) raw_state: %x\n", device, raw_state); if (raw_state == TSX205_DSPY_DE) { *state |= DISPLAY_DVI_DET; *state |= DISPLAY_DVI_ON; } else if (raw_state == TSX205_DSPY_DN) *state |= DISPLAY_DVI_DET; else if (raw_state == TSX205_DSPY_NE) *state |= DISPLAY_DVI_ON; break; } return retval; } static int omnibook_acpi_get_display(const struct omnibook_operation *io_op, unsigned int *state) { int retval = 0; int raw_state = 0; struct acpi_backend_data *priv_data = io_op->backend->data; if(!priv_data->has_doss_dosw) return -ENODEV; if (omnibook_ectype & TSX205) { int i; /* Loop 'tru the different Video-Out devices */ if (priv_data->has_sli) for (i = 4; i < ARRAY_SIZE(tsx205_video_list); i++) retval = tsx205_get_display(priv_data, state, i); else for (i = 0; i < 4; i++) retval = tsx205_get_display(priv_data, state, i); if (retval < 0) return -EIO; goto vidout; } retval = omnibook_acpi_execute(priv_data->ec_handle, GET_DISPLAY_METHOD, NULL, &raw_state); if (retval < 0) return retval; dprintk("get_display raw_state: %x\n", raw_state); /* Backend specific to backend-neutral conversion */ *state = (raw_state & LCD_CSTE) ? DISPLAY_LCD_ON : 0; *state |= (raw_state & CRT_CSTE) ? DISPLAY_CRT_ON : 0; *state |= (raw_state & TVO_CSTE) ? DISPLAY_TVO_ON : 0; *state |= (raw_state & DVI_CSTE) ? DISPLAY_DVI_ON : 0; *state |= (raw_state & LCD_CADL) ? DISPLAY_LCD_DET : 0; *state |= (raw_state & CRT_CADL) ? DISPLAY_CRT_DET : 0; *state |= (raw_state & TVO_CADL) ? DISPLAY_TVO_DET : 0; *state |= (raw_state & DVI_CADL) ? DISPLAY_DVI_DET : 0; vidout: return DISPLAY_LCD_ON | DISPLAY_CRT_ON | DISPLAY_TVO_ON | DISPLAY_DVI_ON | DISPLAY_LCD_DET | DISPLAY_CRT_DET | DISPLAY_TVO_DET | DISPLAY_DVI_DET; } static const unsigned int acpi_display_mode_list[] = { DISPLAY_LCD_ON, DISPLAY_CRT_ON, DISPLAY_LCD_ON | DISPLAY_CRT_ON, DISPLAY_TVO_ON, DISPLAY_LCD_ON | DISPLAY_TVO_ON, DISPLAY_CRT_ON | DISPLAY_TVO_ON, DISPLAY_LCD_ON | DISPLAY_CRT_ON | DISPLAY_TVO_ON, DISPLAY_DVI_ON, DISPLAY_LCD_ON | DISPLAY_DVI_ON, }; static int omnibook_acpi_set_display(const struct omnibook_operation *io_op, unsigned int state) { int retval = 0; int i; int matched = -1; struct acpi_backend_data *priv_data = io_op->backend->data; if(!priv_data->has_doss_dosw) return -ENODEV; for (i = 0; i < ARRAY_SIZE(acpi_display_mode_list); i++) { if (acpi_display_mode_list[i] == state) { matched = i + 1; /* raw state is array row number + 1 */ break; } } if (matched == -1) { printk("Display mode %x is unsupported.\n", state); return -EINVAL; } dprintk("set_display raw_state: %x\n", matched); if (omnibook_ectype & TSX205) { if (priv_data->has_sli) retval = omnibook_acpi_execute(priv_data->dis_handle, TSX205_SLI_DISPLAY_METHOD, &matched, NULL); else retval = omnibook_acpi_execute(priv_data->dis_handle, TSX205_SET_DISPLAY_METHOD, &matched, NULL); } else retval = omnibook_acpi_execute(priv_data->ec_handle, SET_DISPLAY_METHOD, &matched, NULL); if (retval < 0) return retval; return DISPLAY_LCD_ON | DISPLAY_CRT_ON | DISPLAY_TVO_ON | DISPLAY_DVI_ON; } static int omnibook_acpi_get_throttle(const struct omnibook_operation *io_op, unsigned int *state) { int retval; int thtl_en = 0, thtl_dty = 0; int param; struct acpi_backend_data *priv_data = io_op->backend->data; param = 0; /* Read THEN aka THTL_EN in ICH6M datasheets */ retval = omnibook_acpi_execute(priv_data->ec_handle, GET_THROTTLE_METHOD, ¶m, &thtl_en); if ( thtl_en == 0 ) { *state = 0; return retval; } param = 1; /* Read DUTY aka THTL_DTY in ICH6M datasheets */ retval = omnibook_acpi_execute(priv_data->ec_handle, GET_THROTTLE_METHOD, ¶m, &thtl_dty); WARN_ON(thtl_dty > 7); /* We shouldn't encounter more than 7 throttling level */ *state = 8 - thtl_dty; /* THTL_DTY and ACPI T-state are reverse mapped */ return retval; } static int omnibook_acpi_set_throttle(const struct omnibook_operation *io_op, unsigned int state) { struct acpi_backend_data *priv_data = io_op->backend->data; /* THTL_DTY and ACPI T-state are reverse mapped */ /* throttling.c already clamped state between 0 and 7 */ if (state) state = 8 - state; return omnibook_acpi_execute(priv_data->ec_handle, SET_THROTTLE_METHOD, &state, NULL); } /* * Fn+foo hotkeys handling */ static int omnibook_hci_get_hotkeys(const struct omnibook_operation *io_op, unsigned int *state) { u32 in[HCI_WORDS] = { HCI_GET, HCI_HOTKEY_EVENT, 0, 0, 0, 0 }; u32 out[HCI_WORDS]; acpi_status status = hci_raw(in, out); if (status != AE_OK) return HCI_FAILURE; dprintk("get_hotkeys raw_state: %x\n", out[2]); *state = (out[2] & ACPI_FN_MASK) ? HKEY_FN : 0; return 0; } static int omnibook_hci_set_hotkeys(const struct omnibook_operation *io_op, unsigned int state) { u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 }; u32 out[HCI_WORDS]; in[0] = HCI_SET; in[1] = HCI_HOTKEY_EVENT; in[2] = (state & HKEY_FN) ? 1 : 0; acpi_status status = hci_raw(in, out); dprintk("set_hotkeys (Fn interface) raw_state: %x\n", in[2]); return (status == AE_OK) ? out[0] : HCI_FAILURE; } static int omnibook_acpi_get_events(unsigned int *state) { acpi_status status; struct acpi_backend_data *priv_data = acpi_backend.data; /* We need to call the NTFY method first so it can activate the TECF variable */ status = omnibook_acpi_execute(priv_data->ec_handle, TSX205_NOTIFY_METHOD, NULL, NULL); if (status != AE_OK) { dprintk(O_ERR "Failed to activate NTFY method.\n"); return -EIO; } /* Now we can poll the INFO method to get last pressed hotkey */ status = omnibook_acpi_execute(priv_data->hci_handle, TSX205_EVENTS_METHOD, NULL, state); if (status != AE_OK) { dprintk(O_ERR "Failed to get Hotkey event.\n"); return -EIO; } /* We only care about a key press, so just report the Fn key Press/Release */ if ( ((*state & ~0x80) == 0x100) || ((*state & ~0x80) == 0x17f) ) *state &= ~0x80; return status; } /* * Adjust the lcd backlight level by delta. * Used for Fn+F6/F7 keypress */ static int adjust_brighness(int delta) { struct omnibook_feature *lcd_feature = omnibook_find_feature("lcd"); struct omnibook_operation *io_op; int retval = 0; u8 brgt; if(!lcd_feature) return -ENODEV; io_op = lcd_feature->io_op; mutex_lock(&io_op->backend->mutex); if(( retval = __backend_byte_read(io_op, &brgt))) goto out; dprintk("Fn-F6/F7 pressed: adjusting brightness.\n"); if (((int) brgt + delta) < 0) brgt = 0; else if ((brgt + delta) > omnibook_max_brightness) brgt = omnibook_max_brightness; else brgt += delta; retval = __backend_byte_write(io_op, brgt); out: mutex_unlock(&io_op->backend->mutex); return retval; } /* * Workqueue handler for Fn hotkeys */ #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,19)) static void omnibook_handle_fnkey(struct work_struct *work) #else static void omnibook_handle_fnkey(void* data) #endif { int i; u32 gen_scan; struct input_dev *input_dev; acpi_status status; status = omnibook_acpi_get_events(&gen_scan); if (status != AE_OK) return; dprintk("detected scancode 0x%x.\n", gen_scan); switch(gen_scan) { case HCI_BRIGHTNESSDOWN: adjust_brighness(-1); break; case HCI_BRIGHTNESSUP: adjust_brighness(+1); break; } for (i = 0 ; i < ARRAY_SIZE(acpi_scan_table); i++) { if (gen_scan == acpi_scan_table[i].scancode) { dprintk("generating keycode %i.\n", acpi_scan_table[i].keycode); #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,19)) input_dev = container_of(work, struct acpi_backend_data, fnkey_work)->acpi_input_dev; #else input_dev = ((struct acpi_backend_data *) data)->acpi_input_dev; #endif omnibook_report_key(input_dev, acpi_scan_table[i].keycode); break; } } } struct omnibook_backend acpi_backend = { .name = "acpi", .hotkeys_read_cap = HKEY_FN, .hotkeys_write_cap = HKEY_FN, .init = omnibook_acpi_init, .exit = omnibook_acpi_exit, .aerial_get = omnibook_acpi_get_wireless, .aerial_set = omnibook_acpi_set_wireless, .display_get = omnibook_acpi_get_display, .display_set = omnibook_acpi_set_display, .throttle_get = omnibook_acpi_get_throttle, .throttle_set = omnibook_acpi_set_throttle, .hotkeys_get = omnibook_hci_get_hotkeys, .hotkeys_set = omnibook_hci_set_hotkeys, }; #else /* CONFIG_ACPI */ /* dummy backend for non-ACPI systems */ static int _fail_probe(const struct omnibook_operation *io_op) { return -ENODEV; } struct omnibook_backend acpi_backend = { .name = "acpi", .init = _fail_probe, }; #endif /* CONFIG_ACPI */