131ea7ff0SJaya Kumar /* 231ea7ff0SJaya Kumar * atlas_btns.c - Atlas Wallmount Touchscreen ACPI Extras 331ea7ff0SJaya Kumar * 431ea7ff0SJaya Kumar * Copyright (C) 2006 Jaya Kumar 531ea7ff0SJaya Kumar * Based on Toshiba ACPI by John Belmonte and ASUS ACPI 631ea7ff0SJaya Kumar * This work was sponsored by CIS(M) Sdn Bhd. 731ea7ff0SJaya Kumar * 831ea7ff0SJaya Kumar * This program is free software; you can redistribute it and/or modify 931ea7ff0SJaya Kumar * it under the terms of the GNU General Public License as published by 1031ea7ff0SJaya Kumar * the Free Software Foundation; either version 2 of the License, or 1131ea7ff0SJaya Kumar * (at your option) any later version. 1231ea7ff0SJaya Kumar * 1331ea7ff0SJaya Kumar * This program is distributed in the hope that it will be useful, 1431ea7ff0SJaya Kumar * but WITHOUT ANY WARRANTY; without even the implied warranty of 1531ea7ff0SJaya Kumar * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1631ea7ff0SJaya Kumar * GNU General Public License for more details. 1731ea7ff0SJaya Kumar * 1831ea7ff0SJaya Kumar * You should have received a copy of the GNU General Public License 1931ea7ff0SJaya Kumar * along with this program; if not, write to the Free Software 2031ea7ff0SJaya Kumar * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 2131ea7ff0SJaya Kumar * 2231ea7ff0SJaya Kumar */ 2331ea7ff0SJaya Kumar 24bf77499fSDmitry Torokhov #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 25bf77499fSDmitry Torokhov 2631ea7ff0SJaya Kumar #include <linux/kernel.h> 2731ea7ff0SJaya Kumar #include <linux/module.h> 2831ea7ff0SJaya Kumar #include <linux/init.h> 2931ea7ff0SJaya Kumar #include <linux/input.h> 3031ea7ff0SJaya Kumar #include <linux/types.h> 3131ea7ff0SJaya Kumar #include <asm/uaccess.h> 3231ea7ff0SJaya Kumar #include <acpi/acpi_drivers.h> 3331ea7ff0SJaya Kumar 3431ea7ff0SJaya Kumar #define ACPI_ATLAS_NAME "Atlas ACPI" 3531ea7ff0SJaya Kumar #define ACPI_ATLAS_CLASS "Atlas" 3631ea7ff0SJaya Kumar 3772341eeaSDmitry Torokhov static unsigned short atlas_keymap[16]; 3831ea7ff0SJaya Kumar static struct input_dev *input_dev; 3931ea7ff0SJaya Kumar 4031ea7ff0SJaya Kumar /* button handling code */ 4131ea7ff0SJaya Kumar static acpi_status acpi_atlas_button_setup(acpi_handle region_handle, 4231ea7ff0SJaya Kumar u32 function, void *handler_context, void **return_context) 4331ea7ff0SJaya Kumar { 4431ea7ff0SJaya Kumar *return_context = 4531ea7ff0SJaya Kumar (function != ACPI_REGION_DEACTIVATE) ? handler_context : NULL; 4631ea7ff0SJaya Kumar 4731ea7ff0SJaya Kumar return AE_OK; 4831ea7ff0SJaya Kumar } 4931ea7ff0SJaya Kumar 5031ea7ff0SJaya Kumar static acpi_status acpi_atlas_button_handler(u32 function, 5131ea7ff0SJaya Kumar acpi_physical_address address, 52439913ffSLin Ming u32 bit_width, u64 *value, 5331ea7ff0SJaya Kumar void *handler_context, void *region_context) 5431ea7ff0SJaya Kumar { 5531ea7ff0SJaya Kumar acpi_status status; 5631ea7ff0SJaya Kumar 5731ea7ff0SJaya Kumar if (function == ACPI_WRITE) { 5872341eeaSDmitry Torokhov int code = address & 0x0f; 5972341eeaSDmitry Torokhov int key_down = !(address & 0x10); 6072341eeaSDmitry Torokhov 6172341eeaSDmitry Torokhov input_event(input_dev, EV_MSC, MSC_SCAN, code); 6272341eeaSDmitry Torokhov input_report_key(input_dev, atlas_keymap[code], key_down); 6331ea7ff0SJaya Kumar input_sync(input_dev); 6472341eeaSDmitry Torokhov 6502b5fac1SAxel Lin status = AE_OK; 6631ea7ff0SJaya Kumar } else { 67bf77499fSDmitry Torokhov pr_warn("shrugged on unexpected function: function=%x,address=%lx,value=%x\n", 6831ea7ff0SJaya Kumar function, (unsigned long)address, (u32)*value); 6902b5fac1SAxel Lin status = AE_BAD_PARAMETER; 7031ea7ff0SJaya Kumar } 7131ea7ff0SJaya Kumar 7231ea7ff0SJaya Kumar return status; 7331ea7ff0SJaya Kumar } 7431ea7ff0SJaya Kumar 7531ea7ff0SJaya Kumar static int atlas_acpi_button_add(struct acpi_device *device) 7631ea7ff0SJaya Kumar { 7731ea7ff0SJaya Kumar acpi_status status; 7872341eeaSDmitry Torokhov int i; 7931ea7ff0SJaya Kumar int err; 8031ea7ff0SJaya Kumar 8131ea7ff0SJaya Kumar input_dev = input_allocate_device(); 8231ea7ff0SJaya Kumar if (!input_dev) { 83bf77499fSDmitry Torokhov pr_err("unable to allocate input device\n"); 8431ea7ff0SJaya Kumar return -ENOMEM; 8531ea7ff0SJaya Kumar } 8631ea7ff0SJaya Kumar 8731ea7ff0SJaya Kumar input_dev->name = "Atlas ACPI button driver"; 8831ea7ff0SJaya Kumar input_dev->phys = "ASIM0000/atlas/input0"; 8931ea7ff0SJaya Kumar input_dev->id.bustype = BUS_HOST; 9072341eeaSDmitry Torokhov input_dev->keycode = atlas_keymap; 9172341eeaSDmitry Torokhov input_dev->keycodesize = sizeof(unsigned short); 9272341eeaSDmitry Torokhov input_dev->keycodemax = ARRAY_SIZE(atlas_keymap); 9331ea7ff0SJaya Kumar 9472341eeaSDmitry Torokhov input_set_capability(input_dev, EV_MSC, MSC_SCAN); 9572341eeaSDmitry Torokhov __set_bit(EV_KEY, input_dev->evbit); 9672341eeaSDmitry Torokhov for (i = 0; i < ARRAY_SIZE(atlas_keymap); i++) { 9772341eeaSDmitry Torokhov if (i < 9) { 9872341eeaSDmitry Torokhov atlas_keymap[i] = KEY_F1 + i; 9972341eeaSDmitry Torokhov __set_bit(KEY_F1 + i, input_dev->keybit); 10072341eeaSDmitry Torokhov } else 10172341eeaSDmitry Torokhov atlas_keymap[i] = KEY_RESERVED; 10272341eeaSDmitry Torokhov } 10331ea7ff0SJaya Kumar 10431ea7ff0SJaya Kumar err = input_register_device(input_dev); 10531ea7ff0SJaya Kumar if (err) { 106bf77499fSDmitry Torokhov pr_err("couldn't register input device\n"); 10731ea7ff0SJaya Kumar input_free_device(input_dev); 10831ea7ff0SJaya Kumar return err; 10931ea7ff0SJaya Kumar } 11031ea7ff0SJaya Kumar 11131ea7ff0SJaya Kumar /* hookup button handler */ 11231ea7ff0SJaya Kumar status = acpi_install_address_space_handler(device->handle, 11331ea7ff0SJaya Kumar 0x81, &acpi_atlas_button_handler, 11431ea7ff0SJaya Kumar &acpi_atlas_button_setup, device); 11531ea7ff0SJaya Kumar if (ACPI_FAILURE(status)) { 116bf77499fSDmitry Torokhov pr_err("error installing addr spc handler\n"); 11731ea7ff0SJaya Kumar input_unregister_device(input_dev); 11802b5fac1SAxel Lin err = -EINVAL; 11931ea7ff0SJaya Kumar } 12031ea7ff0SJaya Kumar 12102b5fac1SAxel Lin return err; 12231ea7ff0SJaya Kumar } 12331ea7ff0SJaya Kumar 12431ea7ff0SJaya Kumar static int atlas_acpi_button_remove(struct acpi_device *device, int type) 12531ea7ff0SJaya Kumar { 12631ea7ff0SJaya Kumar acpi_status status; 12731ea7ff0SJaya Kumar 12831ea7ff0SJaya Kumar status = acpi_remove_address_space_handler(device->handle, 12931ea7ff0SJaya Kumar 0x81, &acpi_atlas_button_handler); 13002b5fac1SAxel Lin if (ACPI_FAILURE(status)) 131bf77499fSDmitry Torokhov pr_err("error removing addr spc handler\n"); 13231ea7ff0SJaya Kumar 13331ea7ff0SJaya Kumar input_unregister_device(input_dev); 13431ea7ff0SJaya Kumar 13502b5fac1SAxel Lin return 0; 13631ea7ff0SJaya Kumar } 13731ea7ff0SJaya Kumar 1381ba90e3aSThomas Renninger static const struct acpi_device_id atlas_device_ids[] = { 1391ba90e3aSThomas Renninger {"ASIM0000", 0}, 1401ba90e3aSThomas Renninger {"", 0}, 1411ba90e3aSThomas Renninger }; 1421ba90e3aSThomas Renninger MODULE_DEVICE_TABLE(acpi, atlas_device_ids); 1431ba90e3aSThomas Renninger 14431ea7ff0SJaya Kumar static struct acpi_driver atlas_acpi_driver = { 14531ea7ff0SJaya Kumar .name = ACPI_ATLAS_NAME, 14631ea7ff0SJaya Kumar .class = ACPI_ATLAS_CLASS, 14707d19ffcSAxel Lin .owner = THIS_MODULE, 1481ba90e3aSThomas Renninger .ids = atlas_device_ids, 14931ea7ff0SJaya Kumar .ops = { 15031ea7ff0SJaya Kumar .add = atlas_acpi_button_add, 15131ea7ff0SJaya Kumar .remove = atlas_acpi_button_remove, 15231ea7ff0SJaya Kumar }, 15331ea7ff0SJaya Kumar }; 154f06efccfSMika Westerberg module_acpi_driver(atlas_acpi_driver); 15531ea7ff0SJaya Kumar 15631ea7ff0SJaya Kumar MODULE_AUTHOR("Jaya Kumar"); 15731ea7ff0SJaya Kumar MODULE_LICENSE("GPL"); 15831ea7ff0SJaya Kumar MODULE_DESCRIPTION("Atlas button driver"); 15931ea7ff0SJaya Kumar 160