diff options
Diffstat (limited to 'drivers/input/misc')
-rw-r--r-- | drivers/input/misc/Kconfig | 16 | ||||
-rw-r--r-- | drivers/input/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/misc/da9052_onkey.c | 29 | ||||
-rw-r--r-- | drivers/input/misc/ims-pcu.c | 258 | ||||
-rw-r--r-- | drivers/input/misc/ixp4xx-beeper.c | 2 | ||||
-rw-r--r-- | drivers/input/misc/pm8xxx-vibrator.c | 9 | ||||
-rw-r--r-- | drivers/input/misc/pmic8xxx-pwrkey.c | 33 | ||||
-rw-r--r-- | drivers/input/misc/sirfsoc-onkey.c | 111 | ||||
-rw-r--r-- | drivers/input/misc/soc_button_array.c | 218 | ||||
-rw-r--r-- | drivers/input/misc/uinput.c | 97 | ||||
-rw-r--r-- | drivers/input/misc/wistron_btns.c | 19 |
11 files changed, 698 insertions, 95 deletions
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 7904ab05527a..5928ea71dd69 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -156,7 +156,7 @@ config INPUT_MAX8925_ONKEY config INPUT_MAX8997_HAPTIC tristate "MAXIM MAX8997 haptic controller support" - depends on PWM && HAVE_PWM && MFD_MAX8997 + depends on PWM && MFD_MAX8997 select INPUT_FF_MEMLESS help This option enables device driver support for the haptic controller @@ -269,7 +269,7 @@ config INPUT_COBALT_BTNS config INPUT_WISTRON_BTNS tristate "x86 Wistron laptop button interface" - depends on X86 && !X86_64 + depends on X86_32 select INPUT_POLLDEV select INPUT_SPARSEKMAP select NEW_LEDS @@ -470,7 +470,7 @@ config INPUT_PCF8574 config INPUT_PWM_BEEPER tristate "PWM beeper support" - depends on PWM && HAVE_PWM + depends on PWM help Say Y here to get support for PWM based beeper devices. @@ -666,4 +666,14 @@ config INPUT_IDEAPAD_SLIDEBAR To compile this driver as a module, choose M here: the module will be called ideapad_slidebar. +config INPUT_SOC_BUTTON_ARRAY + tristate "Windows-compatible SoC Button Array" + depends on KEYBOARD_GPIO + help + Say Y here if you have a SoC-based tablet that originally + runs Windows 8. + + To compile this driver as a module, choose M here: the + module will be called soc_button_array. + endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index cda71fc52fb3..4955ad322a01 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -53,6 +53,7 @@ obj-$(CONFIG_INPUT_RETU_PWRBUTTON) += retu-pwrbutton.o obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o obj-$(CONFIG_INPUT_SIRFSOC_ONKEY) += sirfsoc-onkey.o +obj-$(CONFIG_INPUT_SOC_BUTTON_ARRAY) += soc_button_array.o obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o obj-$(CONFIG_INPUT_TWL4030_VIBRA) += twl4030-vibra.o diff --git a/drivers/input/misc/da9052_onkey.c b/drivers/input/misc/da9052_onkey.c index 1f695f229ea8..184c8f21ab59 100644 --- a/drivers/input/misc/da9052_onkey.c +++ b/drivers/input/misc/da9052_onkey.c @@ -27,29 +27,32 @@ struct da9052_onkey { static void da9052_onkey_query(struct da9052_onkey *onkey) { - int key_stat; + int ret; - key_stat = da9052_reg_read(onkey->da9052, DA9052_EVENT_B_REG); - if (key_stat < 0) { + ret = da9052_reg_read(onkey->da9052, DA9052_STATUS_A_REG); + if (ret < 0) { dev_err(onkey->da9052->dev, - "Failed to read onkey event %d\n", key_stat); + "Failed to read onkey event err=%d\n", ret); } else { /* * Since interrupt for deassertion of ONKEY pin is not * generated, onkey event state determines the onkey * button state. */ - key_stat &= DA9052_EVENTB_ENONKEY; - input_report_key(onkey->input, KEY_POWER, key_stat); + bool pressed = !(ret & DA9052_STATUSA_NONKEY); + + input_report_key(onkey->input, KEY_POWER, pressed); input_sync(onkey->input); - } - /* - * Interrupt is generated only when the ONKEY pin is asserted. - * Hence the deassertion of the pin is simulated through work queue. - */ - if (key_stat) - schedule_delayed_work(&onkey->work, msecs_to_jiffies(50)); + /* + * Interrupt is generated only when the ONKEY pin + * is asserted. Hence the deassertion of the pin + * is simulated through work queue. + */ + if (pressed) + schedule_delayed_work(&onkey->work, + msecs_to_jiffies(50)); + } } static void da9052_onkey_work(struct work_struct *work) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index e204f26b0011..5a736397d9c8 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -51,6 +51,8 @@ struct ims_pcu_backlight { #define IMS_PCU_BL_VERSION_LEN (9 + 1) #define IMS_PCU_BL_RESET_REASON_LEN (2 + 1) +#define IMS_PCU_PCU_B_DEVICE_ID 5 + #define IMS_PCU_BUF_SIZE 128 struct ims_pcu { @@ -68,6 +70,9 @@ struct ims_pcu { char bl_version[IMS_PCU_BL_VERSION_LEN]; char reset_reason[IMS_PCU_BL_RESET_REASON_LEN]; int update_firmware_status; + u8 device_id; + + u8 ofn_reg_addr; struct usb_interface *ctrl_intf; @@ -371,6 +376,8 @@ static void ims_pcu_destroy_gamepad(struct ims_pcu *pcu) #define IMS_PCU_CMD_GET_DEVICE_ID 0xae #define IMS_PCU_CMD_SPECIAL_INFO 0xb0 #define IMS_PCU_CMD_BOOTLOADER 0xb1 /* Pass data to bootloader */ +#define IMS_PCU_CMD_OFN_SET_CONFIG 0xb3 +#define IMS_PCU_CMD_OFN_GET_CONFIG 0xb4 /* PCU responses */ #define IMS_PCU_RSP_STATUS 0xc0 @@ -389,6 +396,9 @@ static void ims_pcu_destroy_gamepad(struct ims_pcu *pcu) #define IMS_PCU_RSP_GET_DEVICE_ID 0xce #define IMS_PCU_RSP_SPECIAL_INFO 0xd0 #define IMS_PCU_RSP_BOOTLOADER 0xd1 /* Bootloader response */ +#define IMS_PCU_RSP_OFN_SET_CONFIG 0xd2 +#define IMS_PCU_RSP_OFN_GET_CONFIG 0xd3 + #define IMS_PCU_RSP_EVNT_BUTTONS 0xe0 /* Unsolicited, button state */ #define IMS_PCU_GAMEPAD_MASK 0x0001ff80UL /* Bits 7 through 16 */ @@ -1256,6 +1266,225 @@ static struct attribute_group ims_pcu_attr_group = { .attrs = ims_pcu_attrs, }; +/* Support for a separate OFN attribute group */ + +#define OFN_REG_RESULT_OFFSET 2 + +static int ims_pcu_read_ofn_config(struct ims_pcu *pcu, u8 addr, u8 *data) +{ + int error; + s16 result; + + error = ims_pcu_execute_command(pcu, OFN_GET_CONFIG, + &addr, sizeof(addr)); + if (error) + return error; + + result = (s16)get_unaligned_le16(pcu->cmd_buf + OFN_REG_RESULT_OFFSET); + if (result < 0) + return -EIO; + + /* We only need LSB */ + *data = pcu->cmd_buf[OFN_REG_RESULT_OFFSET]; + return 0; +} + +static int ims_pcu_write_ofn_config(struct ims_pcu *pcu, u8 addr, u8 data) +{ + u8 buffer[] = { addr, data }; + int error; + s16 result; + + error = ims_pcu_execute_command(pcu, OFN_SET_CONFIG, + &buffer, sizeof(buffer)); + if (error) + return error; + + result = (s16)get_unaligned_le16(pcu->cmd_buf + OFN_REG_RESULT_OFFSET); + if (result < 0) + return -EIO; + + return 0; +} + +static ssize_t ims_pcu_ofn_reg_data_show(struct device *dev, + struct device_attribute *dattr, + char *buf) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct ims_pcu *pcu = usb_get_intfdata(intf); + int error; + u8 data; + + mutex_lock(&pcu->cmd_mutex); + error = ims_pcu_read_ofn_config(pcu, pcu->ofn_reg_addr, &data); + mutex_unlock(&pcu->cmd_mutex); + + if (error) + return error; + + return scnprintf(buf, PAGE_SIZE, "%x\n", data); +} + +static ssize_t ims_pcu_ofn_reg_data_store(struct device *dev, + struct device_attribute *dattr, + const char *buf, size_t count) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct ims_pcu *pcu = usb_get_intfdata(intf); + int error; + u8 value; + + error = kstrtou8(buf, 0, &value); + if (error) + return error; + + mutex_lock(&pcu->cmd_mutex); + error = ims_pcu_write_ofn_config(pcu, pcu->ofn_reg_addr, value); + mutex_unlock(&pcu->cmd_mutex); + + return error ?: count; +} + +static DEVICE_ATTR(reg_data, S_IRUGO | S_IWUSR, + ims_pcu_ofn_reg_data_show, ims_pcu_ofn_reg_data_store); + +static ssize_t ims_pcu_ofn_reg_addr_show(struct device *dev, + struct device_attribute *dattr, + char *buf) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct ims_pcu *pcu = usb_get_intfdata(intf); + int error; + + mutex_lock(&pcu->cmd_mutex); + error = scnprintf(buf, PAGE_SIZE, "%x\n", pcu->ofn_reg_addr); + mutex_unlock(&pcu->cmd_mutex); + + return error; +} + +static ssize_t ims_pcu_ofn_reg_addr_store(struct device *dev, + struct device_attribute *dattr, + const char *buf, size_t count) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct ims_pcu *pcu = usb_get_intfdata(intf); + int error; + u8 value; + + error = kstrtou8(buf, 0, &value); + if (error) + return error; + + mutex_lock(&pcu->cmd_mutex); + pcu->ofn_reg_addr = value; + mutex_unlock(&pcu->cmd_mutex); + + return error ?: count; +} + +static DEVICE_ATTR(reg_addr, S_IRUGO | S_IWUSR, + ims_pcu_ofn_reg_addr_show, ims_pcu_ofn_reg_addr_store); + +struct ims_pcu_ofn_bit_attribute { + struct device_attribute dattr; + u8 addr; + u8 nr; +}; + +static ssize_t ims_pcu_ofn_bit_show(struct device *dev, + struct device_attribute *dattr, + char *buf) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct ims_pcu *pcu = usb_get_intfdata(intf); + struct ims_pcu_ofn_bit_attribute *attr = + container_of(dattr, struct ims_pcu_ofn_bit_attribute, dattr); + int error; + u8 data; + + mutex_lock(&pcu->cmd_mutex); + error = ims_pcu_read_ofn_config(pcu, attr->addr, &data); + mutex_unlock(&pcu->cmd_mutex); + + if (error) + return error; + + return scnprintf(buf, PAGE_SIZE, "%d\n", !!(data & (1 << attr->nr))); +} + +static ssize_t ims_pcu_ofn_bit_store(struct device *dev, + struct device_attribute *dattr, + const char *buf, size_t count) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct ims_pcu *pcu = usb_get_intfdata(intf); + struct ims_pcu_ofn_bit_attribute *attr = + container_of(dattr, struct ims_pcu_ofn_bit_attribute, dattr); + int error; + int value; + u8 data; + + error = kstrtoint(buf, 0, &value); + if (error) + return error; + + if (value > 1) + return -EINVAL; + + mutex_lock(&pcu->cmd_mutex); + + error = ims_pcu_read_ofn_config(pcu, attr->addr, &data); + if (!error) { + if (value) + data |= 1U << attr->nr; + else + data &= ~(1U << attr->nr); + + error = ims_pcu_write_ofn_config(pcu, attr->addr, data); + } + + mutex_unlock(&pcu->cmd_mutex); + + return error ?: count; +} + +#define IMS_PCU_OFN_BIT_ATTR(_field, _addr, _nr) \ +struct ims_pcu_ofn_bit_attribute ims_pcu_ofn_attr_##_field = { \ + .dattr = __ATTR(_field, S_IWUSR | S_IRUGO, \ + ims_pcu_ofn_bit_show, ims_pcu_ofn_bit_store), \ + .addr = _addr, \ + .nr = _nr, \ +} + +static IMS_PCU_OFN_BIT_ATTR(engine_enable, 0x60, 7); +static IMS_PCU_OFN_BIT_ATTR(speed_enable, 0x60, 6); +static IMS_PCU_OFN_BIT_ATTR(assert_enable, 0x60, 5); +static IMS_PCU_OFN_BIT_ATTR(xyquant_enable, 0x60, 4); +static IMS_PCU_OFN_BIT_ATTR(xyscale_enable, 0x60, 1); + +static IMS_PCU_OFN_BIT_ATTR(scale_x2, 0x63, 6); +static IMS_PCU_OFN_BIT_ATTR(scale_y2, 0x63, 7); + +static struct attribute *ims_pcu_ofn_attrs[] = { + &dev_attr_reg_data.attr, + &dev_attr_reg_addr.attr, + &ims_pcu_ofn_attr_engine_enable.dattr.attr, + &ims_pcu_ofn_attr_speed_enable.dattr.attr, + &ims_pcu_ofn_attr_assert_enable.dattr.attr, + &ims_pcu_ofn_attr_xyquant_enable.dattr.attr, + &ims_pcu_ofn_attr_xyscale_enable.dattr.attr, + &ims_pcu_ofn_attr_scale_x2.dattr.attr, + &ims_pcu_ofn_attr_scale_y2.dattr.attr, + NULL +}; + +static struct attribute_group ims_pcu_ofn_attr_group = { + .name = "ofn", + .attrs = ims_pcu_ofn_attrs, +}; + static void ims_pcu_irq(struct urb *urb) { struct ims_pcu *pcu = urb->context; @@ -1624,7 +1853,6 @@ static int ims_pcu_init_application_mode(struct ims_pcu *pcu) static atomic_t device_no = ATOMIC_INIT(0); const struct ims_pcu_device_info *info; - u8 device_id; int error; error = ims_pcu_get_device_info(pcu); @@ -1633,7 +1861,7 @@ static int ims_pcu_init_application_mode(struct ims_pcu *pcu) return error; } - error = ims_pcu_identify_type(pcu, &device_id); + error = ims_pcu_identify_type(pcu, &pcu->device_id); if (error) { dev_err(pcu->dev, "Failed to identify device, error: %d\n", error); @@ -1645,9 +1873,9 @@ static int ims_pcu_init_application_mode(struct ims_pcu *pcu) return 0; } - if (device_id >= ARRAY_SIZE(ims_pcu_device_info) || - !ims_pcu_device_info[device_id].keymap) { - dev_err(pcu->dev, "Device ID %d is not valid\n", device_id); + if (pcu->device_id >= ARRAY_SIZE(ims_pcu_device_info) || + !ims_pcu_device_info[pcu->device_id].keymap) { + dev_err(pcu->dev, "Device ID %d is not valid\n", pcu->device_id); /* Same as above, punt to userspace */ return 0; } @@ -1655,11 +1883,21 @@ static int ims_pcu_init_application_mode(struct ims_pcu *pcu) /* Device appears to be operable, complete initialization */ pcu->device_no = atomic_inc_return(&device_no) - 1; + /* + * PCU-B devices, both GEN_1 and GEN_2 do not have OFN sensor + */ + if (pcu->device_id != IMS_PCU_PCU_B_DEVICE_ID) { + error = sysfs_create_group(&pcu->dev->kobj, + &ims_pcu_ofn_attr_group); + if (error) + return error; + } + error = ims_pcu_setup_backlight(pcu); if (error) return error; - info = &ims_pcu_device_info[device_id]; + info = &ims_pcu_device_info[pcu->device_id]; error = ims_pcu_setup_buttons(pcu, info->keymap, info->keymap_len); if (error) goto err_destroy_backlight; @@ -1674,10 +1912,10 @@ static int ims_pcu_init_application_mode(struct ims_pcu *pcu) return 0; -err_destroy_backlight: - ims_pcu_destroy_backlight(pcu); err_destroy_buttons: ims_pcu_destroy_buttons(pcu); +err_destroy_backlight: + ims_pcu_destroy_backlight(pcu); return error; } @@ -1691,6 +1929,10 @@ static void ims_pcu_destroy_application_mode(struct ims_pcu *pcu) ims_pcu_destroy_gamepad(pcu); ims_pcu_destroy_buttons(pcu); ims_pcu_destroy_backlight(pcu); + + if (pcu->device_id != IMS_PCU_PCU_B_DEVICE_ID) + sysfs_remove_group(&pcu->dev->kobj, + &ims_pcu_ofn_attr_group); } } diff --git a/drivers/input/misc/ixp4xx-beeper.c b/drivers/input/misc/ixp4xx-beeper.c index 17ccba88d636..ed8e5e8449d3 100644 --- a/drivers/input/misc/ixp4xx-beeper.c +++ b/drivers/input/misc/ixp4xx-beeper.c @@ -67,7 +67,7 @@ static int ixp4xx_spkr_event(struct input_dev *dev, unsigned int type, unsigned } if (value > 20 && value < 32767) - count = (IXP4XX_TIMER_FREQ / (value * 4)) - 1; + count = (ixp4xx_timer_freq / (value * 4)) - 1; ixp4xx_spkr_control(pin, count); diff --git a/drivers/input/misc/pm8xxx-vibrator.c b/drivers/input/misc/pm8xxx-vibrator.c index b88b7cbf93e2..6a915ba31bba 100644 --- a/drivers/input/misc/pm8xxx-vibrator.c +++ b/drivers/input/misc/pm8xxx-vibrator.c @@ -142,7 +142,6 @@ static int pm8xxx_vib_play_effect(struct input_dev *dev, void *data, } static int pm8xxx_vib_probe(struct platform_device *pdev) - { struct pm8xxx_vib *vib; struct input_dev *input_dev; @@ -214,12 +213,20 @@ static int pm8xxx_vib_suspend(struct device *dev) static SIMPLE_DEV_PM_OPS(pm8xxx_vib_pm_ops, pm8xxx_vib_suspend, NULL); +static const struct of_device_id pm8xxx_vib_id_table[] = { + { .compatible = "qcom,pm8058-vib" }, + { .compatible = "qcom,pm8921-vib" }, + { } +}; +MODULE_DEVICE_TABLE(of, pm8xxx_vib_id_table); + static struct platform_driver pm8xxx_vib_driver = { .probe = pm8xxx_vib_probe, .driver = { .name = "pm8xxx-vib", .owner = THIS_MODULE, .pm = &pm8xxx_vib_pm_ops, + .of_match_table = pm8xxx_vib_id_table, }, }; module_platform_driver(pm8xxx_vib_driver); diff --git a/drivers/input/misc/pmic8xxx-pwrkey.c b/drivers/input/misc/pmic8xxx-pwrkey.c index 0e1a05f95858..1cb8fda7a166 100644 --- a/drivers/input/misc/pmic8xxx-pwrkey.c +++ b/drivers/input/misc/pmic8xxx-pwrkey.c @@ -19,8 +19,7 @@ #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/log2.h> - -#include <linux/input/pmic8xxx-pwrkey.h> +#include <linux/of.h> #define PON_CNTL_1 0x1C #define PON_CNTL_PULL_UP BIT(7) @@ -89,15 +88,15 @@ static int pmic8xxx_pwrkey_probe(struct platform_device *pdev) unsigned int pon_cntl; struct regmap *regmap; struct pmic8xxx_pwrkey *pwrkey; - const struct pm8xxx_pwrkey_platform_data *pdata = - dev_get_platdata(&pdev->dev); + u32 kpd_delay; + bool pull_up; - if (!pdata) { - dev_err(&pdev->dev, "power key platform data not supplied\n"); - return -EINVAL; - } + if (of_property_read_u32(pdev->dev.of_node, "debounce", &kpd_delay)) + kpd_delay = 0; - if (pdata->kpd_trigger_delay_us > 62500) { + pull_up = of_property_read_bool(pdev->dev.of_node, "pull-up"); + + if (kpd_delay > 62500) { dev_err(&pdev->dev, "invalid power key trigger delay\n"); return -EINVAL; } @@ -125,7 +124,7 @@ static int pmic8xxx_pwrkey_probe(struct platform_device *pdev) pwr->name = "pmic8xxx_pwrkey"; pwr->phys = "pmic8xxx_pwrkey/input0"; - delay = (pdata->kpd_trigger_delay_us << 10) / USEC_PER_SEC; + delay = (kpd_delay << 10) / USEC_PER_SEC; delay = 1 + ilog2(delay); err = regmap_read(regmap, PON_CNTL_1, &pon_cntl); @@ -136,7 +135,7 @@ static int pmic8xxx_pwrkey_probe(struct platform_device *pdev) pon_cntl &= ~PON_CNTL_TRIG_DELAY_MASK; pon_cntl |= (delay & PON_CNTL_TRIG_DELAY_MASK); - if (pdata->pull_up) + if (pull_up) pon_cntl |= PON_CNTL_PULL_UP; else pon_cntl &= ~PON_CNTL_PULL_UP; @@ -172,7 +171,7 @@ static int pmic8xxx_pwrkey_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, pwrkey); - device_init_wakeup(&pdev->dev, pdata->wakeup); + device_init_wakeup(&pdev->dev, 1); return 0; } @@ -184,13 +183,21 @@ static int pmic8xxx_pwrkey_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id pm8xxx_pwr_key_id_table[] = { + { .compatible = "qcom,pm8058-pwrkey" }, + { .compatible = "qcom,pm8921-pwrkey" }, + { } +}; +MODULE_DEVICE_TABLE(of, pm8xxx_pwr_key_id_table); + static struct platform_driver pmic8xxx_pwrkey_driver = { .probe = pmic8xxx_pwrkey_probe, .remove = pmic8xxx_pwrkey_remove, .driver = { - .name = PM8XXX_PWRKEY_DEV_NAME, + .name = "pm8xxx-pwrkey", .owner = THIS_MODULE, .pm = &pm8xxx_pwr_key_pm_ops, + .of_match_table = pm8xxx_pwr_key_id_table, }, }; module_platform_driver(pmic8xxx_pwrkey_driver); diff --git a/drivers/input/misc/sirfsoc-onkey.c b/drivers/input/misc/sirfsoc-onkey.c index e8897c36d21b..e4104f9b2e6d 100644 --- a/drivers/input/misc/sirfsoc-onkey.c +++ b/drivers/input/misc/sirfsoc-onkey.c @@ -1,7 +1,8 @@ /* * Power key driver for SiRF PrimaII * - * Copyright (c) 2013 Cambridge Silicon Radio Limited, a CSR plc group company. + * Copyright (c) 2013 - 2014 Cambridge Silicon Radio Limited, a CSR plc group + * company. * * Licensed under GPLv2 or later. */ @@ -13,16 +14,41 @@ #include <linux/input.h> #include <linux/rtc/sirfsoc_rtciobrg.h> #include <linux/of.h> +#include <linux/workqueue.h> struct sirfsoc_pwrc_drvdata { u32 pwrc_base; struct input_dev *input; + struct delayed_work work; }; #define PWRC_ON_KEY_BIT (1 << 0) #define PWRC_INT_STATUS 0xc #define PWRC_INT_MASK 0x10 +#define PWRC_PIN_STATUS 0x14 +#define PWRC_KEY_DETECT_UP_TIME 20 /* ms*/ + +static int sirfsoc_pwrc_is_on_key_down(struct sirfsoc_pwrc_drvdata *pwrcdrv) +{ + u32 state = sirfsoc_rtc_iobrg_readl(pwrcdrv->pwrc_base + + PWRC_PIN_STATUS); + return !(state & PWRC_ON_KEY_BIT); /* ON_KEY is active low */ +} + +static void sirfsoc_pwrc_report_event(struct work_struct *work) +{ + struct sirfsoc_pwrc_drvdata *pwrcdrv = + container_of(work, struct sirfsoc_pwrc_drvdata, work.work); + + if (sirfsoc_pwrc_is_on_key_down(pwrcdrv)) { + schedule_delayed_work(&pwrcdrv->work, + msecs_to_jiffies(PWRC_KEY_DETECT_UP_TIME)); + } else { + input_event(pwrcdrv->input, EV_KEY, KEY_POWER, 0); + input_sync(pwrcdrv->input); + } +} static irqreturn_t sirfsoc_pwrc_isr(int irq, void *dev_id) { @@ -34,21 +60,44 @@ static irqreturn_t sirfsoc_pwrc_isr(int irq, void *dev_id) sirfsoc_rtc_iobrg_writel(int_status & ~PWRC_ON_KEY_BIT, pwrcdrv->pwrc_base + PWRC_INT_STATUS); - /* - * For a typical Linux system, we report KEY_SUSPEND to trigger apm-power.c - * to queue a SUSPEND APM event - */ - input_event(pwrcdrv->input, EV_PWR, KEY_SUSPEND, 1); + input_event(pwrcdrv->input, EV_KEY, KEY_POWER, 1); input_sync(pwrcdrv->input); - - /* - * Todo: report KEY_POWER event for Android platforms, Android PowerManager - * will handle the suspend and powerdown/hibernation - */ + schedule_delayed_work(&pwrcdrv->work, + msecs_to_jiffies(PWRC_KEY_DETECT_UP_TIME)); return IRQ_HANDLED; } +static void sirfsoc_pwrc_toggle_interrupts(struct sirfsoc_pwrc_drvdata *pwrcdrv, + bool enable) +{ + u32 int_mask; + + int_mask = sirfsoc_rtc_iobrg_readl(pwrcdrv->pwrc_base + PWRC_INT_MASK); + if (enable) + int_mask |= PWRC_ON_KEY_BIT; + else + int_mask &= ~PWRC_ON_KEY_BIT; + sirfsoc_rtc_iobrg_writel(int_mask, pwrcdrv->pwrc_base + PWRC_INT_MASK); +} + +static int sirfsoc_pwrc_open(struct input_dev *input) +{ + struct sirfsoc_pwrc_drvdata *pwrcdrv = input_get_drvdata(input); + + sirfsoc_pwrc_toggle_interrupts(pwrcdrv, true); + + return 0; +} + +static void sirfsoc_pwrc_close(struct input_dev *input) +{ + struct sirfsoc_pwrc_drvdata *pwrcdrv = input_get_drvdata(input); + + sirfsoc_pwrc_toggle_interrupts(pwrcdrv, false); + cancel_delayed_work_sync(&pwrcdrv->work); +} + static const struct of_device_id sirfsoc_pwrc_of_match[] = { { .compatible = "sirf,prima2-pwrc" }, {}, @@ -70,7 +119,7 @@ static int sirfsoc_pwrc_probe(struct platform_device *pdev) } /* - * we can't use of_iomap because pwrc is not mapped in memory, + * We can't use of_iomap because pwrc is not mapped in memory, * the so-called base address is only offset in rtciobrg */ error = of_property_read_u32(np, "reg", &pwrcdrv->pwrc_base); @@ -86,11 +135,22 @@ static int sirfsoc_pwrc_probe(struct platform_device *pdev) pwrcdrv->input->name = "sirfsoc pwrckey"; pwrcdrv->input->phys = "pwrc/input0"; - pwrcdrv->input->evbit[0] = BIT_MASK(EV_PWR); + pwrcdrv->input->evbit[0] = BIT_MASK(EV_KEY); + input_set_capability(pwrcdrv->input, EV_KEY, KEY_POWER); + + INIT_DELAYED_WORK(&pwrcdrv->work, sirfsoc_pwrc_report_event); + + pwrcdrv->input->open = sirfsoc_pwrc_open; + pwrcdrv->input->close = sirfsoc_pwrc_close; + + input_set_drvdata(pwrcdrv->input, pwrcdrv); + + /* Make sure the device is quiesced */ + sirfsoc_pwrc_toggle_interrupts(pwrcdrv, false); irq = platform_get_irq(pdev, 0); error = devm_request_irq(&pdev->dev, irq, - sirfsoc_pwrc_isr, IRQF_SHARED, + sirfsoc_pwrc_isr, 0, "sirfsoc_pwrc_int", pwrcdrv); if (error) { dev_err(&pdev->dev, "unable to claim irq %d, error: %d\n", @@ -98,11 +158,6 @@ static int sirfsoc_pwrc_probe(struct platform_device *pdev) return error; } - sirfsoc_rtc_iobrg_writel( - sirfsoc_rtc_iobrg_readl(pwrcdrv->pwrc_base + PWRC_INT_MASK) | - PWRC_ON_KEY_BIT, - pwrcdrv->pwrc_base + PWRC_INT_MASK); - error = input_register_device(pwrcdrv->input); if (error) { dev_err(&pdev->dev, @@ -111,7 +166,7 @@ static int sirfsoc_pwrc_probe(struct platform_device *pdev) return error; } - platform_set_drvdata(pdev, pwrcdrv); + dev_set_drvdata(&pdev->dev, pwrcdrv); device_init_wakeup(&pdev->dev, 1); return 0; @@ -125,25 +180,25 @@ static int sirfsoc_pwrc_remove(struct platform_device *pdev) } #ifdef CONFIG_PM_SLEEP -static int pwrc_resume(struct device *dev) +static int sirfsoc_pwrc_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct sirfsoc_pwrc_drvdata *pwrcdrv = platform_get_drvdata(pdev); + struct sirfsoc_pwrc_drvdata *pwrcdrv = dev_get_drvdata(dev); + struct input_dev *input = pwrcdrv->input; /* * Do not mask pwrc interrupt as we want pwrc work as a wakeup source * if users touch X_ONKEY_B, see arch/arm/mach-prima2/pm.c */ - sirfsoc_rtc_iobrg_writel( - sirfsoc_rtc_iobrg_readl( - pwrcdrv->pwrc_base + PWRC_INT_MASK) | PWRC_ON_KEY_BIT, - pwrcdrv->pwrc_base + PWRC_INT_MASK); + mutex_lock(&input->mutex); + if (input->users) + sirfsoc_pwrc_toggle_interrupts(pwrcdrv, true); + mutex_unlock(&input->mutex); return 0; } #endif -static SIMPLE_DEV_PM_OPS(sirfsoc_pwrc_pm_ops, NULL, pwrc_resume); +static SIMPLE_DEV_PM_OPS(sirfsoc_pwrc_pm_ops, NULL, sirfsoc_pwrc_resume); static struct platform_driver sirfsoc_pwrc_driver = { .probe = sirfsoc_pwrc_probe, diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c new file mode 100644 index 000000000000..08ead2aaede5 --- /dev/null +++ b/drivers/input/misc/soc_button_array.c @@ -0,0 +1,218 @@ +/* + * Supports for the button array on SoC tablets originally running + * Windows 8. + * + * (C) Copyright 2014 Intel Corporation + * + * 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; version 2 + * of the License. + */ + +#include <linux/module.h> +#include <linux/input.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/acpi.h> +#include <linux/gpio/consumer.h> +#include <linux/gpio_keys.h> +#include <linux/input.h> +#include <linux/platform_device.h> +#include <linux/pnp.h> + +/* + * Definition of buttons on the tablet. The ACPI index of each button + * is defined in section 2.8.7.2 of "Windows ACPI Design Guide for SoC + * Platforms" + */ +#define MAX_NBUTTONS 5 + +struct soc_button_info { + const char *name; + int acpi_index; + unsigned int event_type; + unsigned int event_code; + bool autorepeat; + bool wakeup; +}; + +/* + * Some of the buttons like volume up/down are auto repeat, while others + * are not. To support both, we register two platform devices, and put + * buttons into them based on whether the key should be auto repeat. + */ +#define BUTTON_TYPES 2 + +struct soc_button_data { + struct platform_device *children[BUTTON_TYPES]; +}; + +/* + * Get the Nth GPIO number from the ACPI object. + */ +static int soc_button_lookup_gpio(struct device *dev, int acpi_index) +{ + struct gpio_desc *desc; + int gpio; + + desc = gpiod_get_index(dev, KBUILD_MODNAME, acpi_index); + if (IS_ERR(desc)) + return PTR_ERR(desc); + + gpio = desc_to_gpio(desc); + + gpiod_put(desc); + + return gpio; +} + +static struct platform_device * +soc_button_device_create(struct pnp_dev *pdev, + const struct soc_button_info *button_info, + bool autorepeat) +{ + const struct soc_button_info *info; + struct platform_device *pd; + struct gpio_keys_button *gpio_keys; + struct gpio_keys_platform_data *gpio_keys_pdata; + int n_buttons = 0; + int gpio; + int error; + + gpio_keys_pdata = devm_kzalloc(&pdev->dev, + sizeof(*gpio_keys_pdata) + + sizeof(*gpio_keys) * MAX_NBUTTONS, + GFP_KERNEL); + gpio_keys = (void *)(gpio_keys_pdata + 1); + + for (info = button_info; info->name; info++) { + if (info->autorepeat != autorepeat) + continue; + + gpio = soc_button_lookup_gpio(&pdev->dev, info->acpi_index); + if (gpio < 0) + continue; + + gpio_keys[n_buttons].type = info->event_type; + gpio_keys[n_buttons].code = info->event_code; + gpio_keys[n_buttons].gpio = gpio; + gpio_keys[n_buttons].active_low = 1; + gpio_keys[n_buttons].desc = info->name; + gpio_keys[n_buttons].wakeup = info->wakeup; + n_buttons++; + } + + if (n_buttons == 0) { + error = -ENODEV; + goto err_free_mem; + } + + gpio_keys_pdata->buttons = gpio_keys; + gpio_keys_pdata->nbuttons = n_buttons; + gpio_keys_pdata->rep = autorepeat; + + pd = platform_device_alloc("gpio-keys", PLATFORM_DEVID_AUTO); + if (!pd) { + error = -ENOMEM; + goto err_free_mem; + } + + error = platform_device_add_data(pd, gpio_keys_pdata, + sizeof(*gpio_keys_pdata)); + if (error) + goto err_free_pdev; + + error = platform_device_add(pd); + if (error) + goto err_free_pdev; + + return pd; + +err_free_pdev: + platform_device_put(pd); +err_free_mem: + devm_kfree(&pdev->dev, gpio_keys_pdata); + return ERR_PTR(error); +} + +static void soc_button_remove(struct pnp_dev *pdev) +{ + struct soc_button_data *priv = pnp_get_drvdata(pdev); + int i; + + for (i = 0; i < BUTTON_TYPES; i++) + if (priv->children[i]) + platform_device_unregister(priv->children[i]); +} + +static int soc_button_pnp_probe(struct pnp_dev *pdev, + const struct pnp_device_id *id) +{ + const struct soc_button_info *button_info = (void *)id->driver_data; + struct soc_button_data *priv; + struct platform_device *pd; + int i; + int error; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + pnp_set_drvdata(pdev, priv); + + for (i = 0; i < BUTTON_TYPES; i++) { + pd = soc_button_device_create(pdev, button_info, i == 0); + if (IS_ERR(pd)) { + error = PTR_ERR(pd); + if (error != -ENODEV) { + soc_button_remove(pdev); + return error; + } + } + + priv->children[i] = pd; + } + + if (!priv->children[0] && !priv->children[1]) + return -ENODEV; + + return 0; +} + +static struct soc_button_info soc_button_PNP0C40[] = { + { "power", 0, EV_KEY, KEY_POWER, false, true }, + { "home", 1, EV_KEY, KEY_HOME, false, true }, + { "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false }, + { "volume_down", 3, EV_KEY, KEY_VOLUMEDOWN, true, false }, + { "rotation_lock", 4, EV_SW, SW_ROTATE_LOCK, false, false }, + { } +}; + +static const struct pnp_device_id soc_button_pnp_match[] = { + { .id = "PNP0C40", .driver_data = (long)soc_button_PNP0C40 }, + { .id = "" } +}; +MODULE_DEVICE_TABLE(pnp, soc_button_pnp_match); + +static struct pnp_driver soc_button_pnp_driver = { + .name = KBUILD_MODNAME, + .id_table = soc_button_pnp_match, + .probe = soc_button_pnp_probe, + .remove = soc_button_remove, +}; + +static int __init soc_button_init(void) +{ + return pnp_register_driver(&soc_button_pnp_driver); +} + +static void __exit soc_button_exit(void) +{ + pnp_unregister_driver(&soc_button_pnp_driver); +} + +module_init(soc_button_init); +module_exit(soc_button_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 772835938a52..856936247500 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -20,6 +20,8 @@ * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org> * * Changes/Revisions: + * 0.4 01/09/2014 (Benjamin Tissoires <benjamin.tissoires@redhat.com>) + * - add UI_GET_SYSNAME ioctl * 0.3 09/04/2006 (Anssi Hannula <anssi.hannula@gmail.com>) * - updated ff support for the changes in kernel interface * - added MODULE_VERSION @@ -670,6 +672,31 @@ static int uinput_ff_upload_from_user(const char __user *buffer, __ret; \ }) +static int uinput_str_to_user(void __user *dest, const char *str, + unsigned int maxlen) +{ + char __user *p = dest; + int len, ret; + + if (!str) + return -ENOENT; + + if (maxlen == 0) + return -EINVAL; + + len = strlen(str) + 1; + if (len > maxlen) + len = maxlen; + + ret = copy_to_user(p, str, len); + if (ret) + return -EFAULT; + + /* force terminating '\0' */ + ret = put_user(0, p + len - 1); + return ret ? -EFAULT : len; +} + static long uinput_ioctl_handler(struct file *file, unsigned int cmd, unsigned long arg, void __user *p) { @@ -679,6 +706,8 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd, struct uinput_ff_erase ff_erase; struct uinput_request *req; char *phys; + const char *name; + unsigned int size; retval = mutex_lock_interruptible(&udev->mutex); if (retval) @@ -693,51 +722,51 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd, switch (cmd) { case UI_DEV_CREATE: retval = uinput_create_device(udev); - break; + goto out; case UI_DEV_DESTROY: uinput_destroy_device(udev); - break; + goto out; case UI_SET_EVBIT: retval = uinput_set_bit(arg, evbit, EV_MAX); - break; + goto out; case UI_SET_KEYBIT: retval = uinput_set_bit(arg, keybit, KEY_MAX); - break; + goto out; case UI_SET_RELBIT: retval = uinput_set_bit(arg, relbit, REL_MAX); - break; + goto out; case UI_SET_ABSBIT: retval = uinput_set_bit(arg, absbit, ABS_MAX); - break; + goto out; case UI_SET_MSCBIT: retval = uinput_set_bit(arg, mscbit, MSC_MAX); - break; + goto out; case UI_SET_LEDBIT: retval = uinput_set_bit(arg, ledbit, LED_MAX); - break; + goto out; case UI_SET_SNDBIT: retval = uinput_set_bit(arg, sndbit, SND_MAX); - break; + goto out; case UI_SET_FFBIT: retval = uinput_set_bit(arg, ffbit, FF_MAX); - break; + goto out; case UI_SET_SWBIT: retval = uinput_set_bit(arg, swbit, SW_MAX); - break; + goto out; case UI_SET_PROPBIT: retval = uinput_set_bit(arg, propbit, INPUT_PROP_MAX); - break; + goto out; case UI_SET_PHYS: if (udev->state == UIST_CREATED) { @@ -753,18 +782,18 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd, kfree(udev->dev->phys); udev->dev->phys = phys; - break; + goto out; case UI_BEGIN_FF_UPLOAD: retval = uinput_ff_upload_from_user(p, &ff_up); if (retval) - break; + goto out; req = uinput_request_find(udev, ff_up.request_id); if (!req || req->code != UI_FF_UPLOAD || !req->u.upload.effect) { retval = -EINVAL; - break; + goto out; } ff_up.retval = 0; @@ -775,65 +804,77 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd, memset(&ff_up.old, 0, sizeof(struct ff_effect)); retval = uinput_ff_upload_to_user(p, &ff_up); - break; + goto out; case UI_BEGIN_FF_ERASE: if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) { retval = -EFAULT; - break; + goto out; } req = uinput_request_find(udev, ff_erase.request_id); if (!req || req->code != UI_FF_ERASE) { retval = -EINVAL; - break; + goto out; } ff_erase.retval = 0; ff_erase.effect_id = req->u.effect_id; if (copy_to_user(p, &ff_erase, sizeof(ff_erase))) { retval = -EFAULT; - break; + goto out; } - break; + goto out; case UI_END_FF_UPLOAD: retval = uinput_ff_upload_from_user(p, &ff_up); if (retval) - break; + goto out; req = uinput_request_find(udev, ff_up.request_id); if (!req || req->code != UI_FF_UPLOAD || !req->u.upload.effect) { retval = -EINVAL; - break; + goto out; } req->retval = ff_up.retval; uinput_request_done(udev, req); - break; + goto out; case UI_END_FF_ERASE: if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) { retval = -EFAULT; - break; + goto out; } req = uinput_request_find(udev, ff_erase.request_id); if (!req || req->code != UI_FF_ERASE) { retval = -EINVAL; - break; + goto out; } req->retval = ff_erase.retval; uinput_request_done(udev, req); - break; + goto out; + } - default: - retval = -EINVAL; + size = _IOC_SIZE(cmd); + + /* Now check variable-length commands */ + switch (cmd & ~IOCSIZE_MASK) { + case UI_GET_SYSNAME(0): + if (udev->state != UIST_CREATED) { + retval = -ENOENT; + goto out; + } + name = dev_name(&udev->dev->dev); + retval = uinput_str_to_user(p, name, size); + goto out; } + retval = -EINVAL; out: mutex_unlock(&udev->mutex); return retval; diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c index b6505454bcc4..7b7add5061a5 100644 --- a/drivers/input/misc/wistron_btns.c +++ b/drivers/input/misc/wistron_btns.c @@ -277,6 +277,16 @@ static struct key_entry keymap_fs_amilo_pro_v3505[] __initdata = { { KE_END, 0 } }; +static struct key_entry keymap_fs_amilo_pro_v8210[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, /* Fn+F1 */ + { KE_KEY, 0x06, {KEY_DISPLAYTOGGLE} }, /* Fn+F4 */ + { KE_BLUETOOTH, 0x30 }, /* Fn+F10 */ + { KE_KEY, 0x31, {KEY_MAIL} }, /* mail button */ + { KE_KEY, 0x36, {KEY_WWW} }, /* www button */ + { KE_WIFI, 0x78 }, /* satelite dish button */ + { KE_END, FE_WIFI_LED } +}; + static struct key_entry keymap_fujitsu_n3510[] __initdata = { { KE_KEY, 0x11, {KEY_PROG1} }, { KE_KEY, 0x12, {KEY_PROG2} }, @@ -654,6 +664,15 @@ static const struct dmi_system_id dmi_ids[] __initconst = { .driver_data = keymap_fs_amilo_pro_v3505 }, { + /* Fujitsu-Siemens Amilo Pro Edition V8210 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro Series V8210"), + }, + .driver_data = keymap_fs_amilo_pro_v8210 + }, + { /* Fujitsu-Siemens Amilo M7400 */ .callback = dmi_matched, .matches = { |