From 6175556fdc0a66ce5f1831e22892fac6f28fc2ec Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Fri, 28 Aug 2009 10:50:34 -0700 Subject: OMAP: Rename OMAP_MPUIO_BASE to OMAP1_MPUIO_BASE Rename OMAP_MPUIO_BASE to OMAP1_MPUIO_BASE Signed-off-by: Tony Lindgren --- drivers/input/keyboard/omap-keypad.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c index 87ec7b18ac69..bba85add35a3 100644 --- a/drivers/input/keyboard/omap-keypad.c +++ b/drivers/input/keyboard/omap-keypad.c @@ -116,7 +116,7 @@ static irqreturn_t omap_kp_interrupt(int irq, void *dev_id) } } else /* disable keyboard interrupt and schedule for handling */ - omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); + omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); tasklet_schedule(&kp_tasklet); @@ -143,20 +143,20 @@ static void omap_kp_scan_keypad(struct omap_kp *omap_kp, unsigned char *state) } else { /* disable keyboard interrupt and schedule for handling */ - omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); + omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); /* read the keypad status */ - omap_writew(0xff, OMAP_MPUIO_BASE + OMAP_MPUIO_KBC); + omap_writew(0xff, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBC); for (col = 0; col < omap_kp->cols; col++) { omap_writew(~(1 << col) & 0xff, - OMAP_MPUIO_BASE + OMAP_MPUIO_KBC); + OMAP1_MPUIO_BASE + OMAP_MPUIO_KBC); udelay(omap_kp->delay); - state[col] = ~omap_readw(OMAP_MPUIO_BASE + + state[col] = ~omap_readw(OMAP1_MPUIO_BASE + OMAP_MPUIO_KBR_LATCH) & 0xff; } - omap_writew(0x00, OMAP_MPUIO_BASE + OMAP_MPUIO_KBC); + omap_writew(0x00, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBC); udelay(2); } } @@ -234,7 +234,7 @@ static void omap_kp_tasklet(unsigned long data) for (i = 0; i < omap_kp_data->rows; i++) enable_irq(gpio_to_irq(row_gpios[i])); } else { - omap_writew(0, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); + omap_writew(0, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); kp_cur_group = -1; } } @@ -317,7 +317,7 @@ static int __devinit omap_kp_probe(struct platform_device *pdev) /* Disable the interrupt for the MPUIO keyboard */ if (!cpu_is_omap24xx()) - omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); + omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); keymap = pdata->keymap; @@ -391,7 +391,7 @@ static int __devinit omap_kp_probe(struct platform_device *pdev) } if (pdata->dbounce) - omap_writew(0xff, OMAP_MPUIO_BASE + OMAP_MPUIO_GPIO_DEBOUNCING); + omap_writew(0xff, OMAP1_MPUIO_BASE + OMAP_MPUIO_GPIO_DEBOUNCING); /* scan current status and enable interrupt */ omap_kp_scan_keypad(omap_kp, keypad_state); @@ -402,7 +402,7 @@ static int __devinit omap_kp_probe(struct platform_device *pdev) "omap-keypad", omap_kp) < 0) goto err4; } - omap_writew(0, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); + omap_writew(0, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); } else { for (irq_idx = 0; irq_idx < omap_kp->rows; irq_idx++) { if (request_irq(gpio_to_irq(row_gpios[irq_idx]), @@ -449,7 +449,7 @@ static int __devexit omap_kp_remove(struct platform_device *pdev) free_irq(gpio_to_irq(row_gpios[i]), 0); } } else { - omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); + omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); free_irq(omap_kp->irq, 0); } -- cgit v1.2.1 From 32bb0e0c778a4a6cd4534a5b98f08cd45e9ab5b9 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 23 Jul 2009 15:50:43 +0200 Subject: wm97xx-core: Pass platform_data to battery Signed-off-by: Marek Vasut Acked-by: Dmitry Torokhov Signed-off-by: Anton Vorontsov --- drivers/input/touchscreen/wm97xx-core.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c index 2957d48e0045..cf8cbc60d5ce 100644 --- a/drivers/input/touchscreen/wm97xx-core.c +++ b/drivers/input/touchscreen/wm97xx-core.c @@ -561,6 +561,7 @@ static void wm97xx_ts_input_close(struct input_dev *idev) static int wm97xx_probe(struct device *dev) { struct wm97xx *wm; + struct wm97xx_pdata *pdata = dev->platform_data; int ret = 0, id = 0; wm = kzalloc(sizeof(struct wm97xx), GFP_KERNEL); @@ -656,6 +657,7 @@ static int wm97xx_probe(struct device *dev) } platform_set_drvdata(wm->battery_dev, wm); wm->battery_dev->dev.parent = dev; + wm->battery_dev->dev.platform_data = pdata; ret = platform_device_add(wm->battery_dev); if (ret < 0) goto batt_reg_err; @@ -669,6 +671,7 @@ static int wm97xx_probe(struct device *dev) } platform_set_drvdata(wm->touch_dev, wm); wm->touch_dev->dev.parent = dev; + wm->touch_dev->dev.platform_data = pdata; ret = platform_device_add(wm->touch_dev); if (ret < 0) goto touch_reg_err; -- cgit v1.2.1 From 1f85d381062a046fd8f3ddb654a5276266daf72c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 15 Sep 2009 00:21:34 +0000 Subject: sh: add kycr2_delay for sh_keysc After KYCR2 is set, udelay might become necessary if there are only a small number of keys attached. This patch introduces an optional delay through the platform data to address this problem. Signed-off-by: Kuninori Morimoto Signed-off-by: Paul Mundt --- drivers/input/keyboard/sh_keysc.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/sh_keysc.c b/drivers/input/keyboard/sh_keysc.c index cea70e6a1031..68fd502fcfef 100644 --- a/drivers/input/keyboard/sh_keysc.c +++ b/drivers/input/keyboard/sh_keysc.c @@ -80,6 +80,9 @@ static irqreturn_t sh_keysc_isr(int irq, void *dev_id) iowrite16(KYCR2_IRQ_LEVEL | (keyin_set << 8), priv->iomem_base + KYCR2_OFFS); + if (pdata->kycr2_delay) + udelay(pdata->kycr2_delay); + keys ^= ~0; keys &= (1 << (sh_keysc_mode[pdata->mode].keyin * sh_keysc_mode[pdata->mode].keyout)) - 1; -- cgit v1.2.1 From a4dbd6740df0872cdf0a86841f75beec8381964d Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 24 Jun 2009 10:06:31 -0700 Subject: driver model: constify attribute groups Let attribute group vectors be declared "const". We'd like to let most attribute metadata live in read-only sections... this is a start. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/input/input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/input.c b/drivers/input/input.c index 7c237e6ac711..851791d955f3 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -1144,7 +1144,7 @@ static struct attribute_group input_dev_caps_attr_group = { .attrs = input_dev_caps_attrs, }; -static struct attribute_group *input_dev_attr_groups[] = { +static const struct attribute_group *input_dev_attr_groups[] = { &input_dev_attr_group, &input_dev_id_attr_group, &input_dev_caps_attr_group, -- cgit v1.2.1 From 0c73b992dd4c645f050344cb13142c0fd3496824 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 15 Sep 2009 12:07:12 +0200 Subject: input: Add support for the WM831x ON pin The WM831x series of PMICs support control of initial power on through the ON pin on the device with soft control of the pin at other times. Represent this to userspace as KEY_POWER. Signed-off-by: Mark Brown Acked-by: Dmitry Torokhov Signed-off-by: Samuel Ortiz --- drivers/input/misc/Kconfig | 10 +++ drivers/input/misc/Makefile | 1 + drivers/input/misc/wm831x-on.c | 163 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 174 insertions(+) create mode 100644 drivers/input/misc/wm831x-on.c (limited to 'drivers/input') diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index cbe21bc96b52..852941d108ff 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -279,4 +279,14 @@ config INPUT_BFIN_ROTARY To compile this driver as a module, choose M here: the module will be called bfin-rotary. +config INPUT_WM831X_ON + tristate "WM831X ON pin" + depends on MFD_WM831X + help + Support the ON pin of WM831X PMICs as an input device + reporting power button status. + + To compile this driver as a module, choose M here: the module + will be called wm831x_on. + endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 79c1e9a5ea31..c97533fb83c8 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -26,4 +26,5 @@ obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o obj-$(CONFIG_INPUT_UINPUT) += uinput.o obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o +obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o diff --git a/drivers/input/misc/wm831x-on.c b/drivers/input/misc/wm831x-on.c new file mode 100644 index 000000000000..ba4f5dd7c60e --- /dev/null +++ b/drivers/input/misc/wm831x-on.c @@ -0,0 +1,163 @@ +/** + * wm831x-on.c - WM831X ON pin driver + * + * Copyright (C) 2009 Wolfson Microelectronics plc + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct wm831x_on { + struct input_dev *dev; + struct delayed_work work; + struct wm831x *wm831x; +}; + +/* + * The chip gives us an interrupt when the ON pin is asserted but we + * then need to poll to see when the pin is deasserted. + */ +static void wm831x_poll_on(struct work_struct *work) +{ + struct wm831x_on *wm831x_on = container_of(work, struct wm831x_on, + work.work); + struct wm831x *wm831x = wm831x_on->wm831x; + int poll, ret; + + ret = wm831x_reg_read(wm831x, WM831X_ON_PIN_CONTROL); + if (ret >= 0) { + poll = !(ret & WM831X_ON_PIN_STS); + + input_report_key(wm831x_on->dev, KEY_POWER, poll); + input_sync(wm831x_on->dev); + } else { + dev_err(wm831x->dev, "Failed to read ON status: %d\n", ret); + poll = 1; + } + + if (poll) + schedule_delayed_work(&wm831x_on->work, 100); +} + +static irqreturn_t wm831x_on_irq(int irq, void *data) +{ + struct wm831x_on *wm831x_on = data; + + schedule_delayed_work(&wm831x_on->work, 0); + + return IRQ_HANDLED; +} + +static int __devinit wm831x_on_probe(struct platform_device *pdev) +{ + struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); + struct wm831x_on *wm831x_on; + int irq = platform_get_irq(pdev, 0); + int ret; + + wm831x_on = kzalloc(sizeof(struct wm831x_on), GFP_KERNEL); + if (!wm831x_on) { + dev_err(&pdev->dev, "Can't allocate data\n"); + return -ENOMEM; + } + + wm831x_on->wm831x = wm831x; + INIT_DELAYED_WORK(&wm831x_on->work, wm831x_poll_on); + + wm831x_on->dev = input_allocate_device(); + if (!wm831x_on->dev) { + dev_err(&pdev->dev, "Can't allocate input dev\n"); + ret = -ENOMEM; + goto err; + } + + wm831x_on->dev->evbit[0] = BIT_MASK(EV_KEY); + wm831x_on->dev->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER); + wm831x_on->dev->name = "wm831x_on"; + wm831x_on->dev->phys = "wm831x_on/input0"; + wm831x_on->dev->dev.parent = &pdev->dev; + + ret = wm831x_request_irq(wm831x, irq, wm831x_on_irq, + IRQF_TRIGGER_RISING, "wm831x_on", wm831x_on); + if (ret < 0) { + dev_err(&pdev->dev, "Unable to request IRQ: %d\n", ret); + goto err_input_dev; + } + ret = input_register_device(wm831x_on->dev); + if (ret) { + dev_dbg(&pdev->dev, "Can't register input device: %d\n", ret); + goto err_irq; + } + + platform_set_drvdata(pdev, wm831x_on); + + return 0; + +err_irq: + wm831x_free_irq(wm831x, irq, NULL); +err_input_dev: + input_free_device(wm831x_on->dev); +err: + kfree(wm831x_on); + return ret; +} + +static int __devexit wm831x_on_remove(struct platform_device *pdev) +{ + struct wm831x_on *wm831x_on = platform_get_drvdata(pdev); + int irq = platform_get_irq(pdev, 0); + + wm831x_free_irq(wm831x_on->wm831x, irq, wm831x_on); + cancel_delayed_work_sync(&wm831x_on->work); + input_unregister_device(wm831x_on->dev); + kfree(wm831x_on); + + return 0; +} + +static struct platform_driver wm831x_on_driver = { + .probe = wm831x_on_probe, + .remove = __devexit_p(wm831x_on_remove), + .driver = { + .name = "wm831x-on", + .owner = THIS_MODULE, + }, +}; + +static int __init wm831x_on_init(void) +{ + return platform_driver_register(&wm831x_on_driver); +} +module_init(wm831x_on_init); + +static void __exit wm831x_on_exit(void) +{ + platform_driver_unregister(&wm831x_on_driver); +} +module_exit(wm831x_on_exit); + +MODULE_ALIAS("platform:wm831x-on"); +MODULE_DESCRIPTION("WM831x ON pin"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mark Brown "); + -- cgit v1.2.1 From 0387e107d6043c810915bf552c3fee367f536f3a Mon Sep 17 00:00:00 2001 From: Daniel Ribeiro Date: Fri, 7 Aug 2009 22:54:56 +0200 Subject: input: PCAP2 based touchscreen driver Touchscreen driver for the PCAP2 multi function device used in Motorola EZX smartphones. Signed-off-by: Daniel Ribeiro Signed-off-by: Stefan Schmidt Signed-off-by: Antonio Ospite Acked-by: Dmitry Torokhov Signed-off-by: Samuel Ortiz --- drivers/input/touchscreen/Kconfig | 9 ++ drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/pcap_ts.c | 271 ++++++++++++++++++++++++++++++++++++ 3 files changed, 281 insertions(+) create mode 100644 drivers/input/touchscreen/pcap_ts.c (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 87a1ae63bcc4..ab02d72afbf3 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -510,4 +510,13 @@ config TOUCHSCREEN_W90X900 To compile this driver as a module, choose M here: the module will be called w90p910_ts. +config TOUCHSCREEN_PCAP + tristate "Motorola PCAP touchscreen" + depends on EZX_PCAP + help + Say Y here if you have a Motorola EZX telephone and + want to enable support for the built-in touchscreen. + + To compile this driver as a module, choose M here: the + module will be called pcap_ts. endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 3e1c5e0b952f..4599bf7ad819 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -40,3 +40,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ATMEL) += atmel-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o +obj-$(CONFIG_TOUCHSCREEN_PCAP) += pcap_ts.o diff --git a/drivers/input/touchscreen/pcap_ts.c b/drivers/input/touchscreen/pcap_ts.c new file mode 100644 index 000000000000..67fcd33595de --- /dev/null +++ b/drivers/input/touchscreen/pcap_ts.c @@ -0,0 +1,271 @@ +/* + * Driver for Motorola PCAP2 touchscreen as found in the EZX phone platform. + * + * Copyright (C) 2006 Harald Welte + * Copyright (C) 2009 Daniel Ribeiro + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct pcap_ts { + struct pcap_chip *pcap; + struct input_dev *input; + struct delayed_work work; + u16 x, y; + u16 pressure; + u8 read_state; +}; + +#define SAMPLE_DELAY 20 /* msecs */ + +#define X_AXIS_MIN 0 +#define X_AXIS_MAX 1023 +#define Y_AXIS_MAX X_AXIS_MAX +#define Y_AXIS_MIN X_AXIS_MIN +#define PRESSURE_MAX X_AXIS_MAX +#define PRESSURE_MIN X_AXIS_MIN + +static void pcap_ts_read_xy(void *data, u16 res[2]) +{ + struct pcap_ts *pcap_ts = data; + + switch (pcap_ts->read_state) { + case PCAP_ADC_TS_M_PRESSURE: + /* pressure reading is unreliable */ + if (res[0] > PRESSURE_MIN && res[0] < PRESSURE_MAX) + pcap_ts->pressure = res[0]; + pcap_ts->read_state = PCAP_ADC_TS_M_XY; + schedule_delayed_work(&pcap_ts->work, 0); + break; + case PCAP_ADC_TS_M_XY: + pcap_ts->y = res[0]; + pcap_ts->x = res[1]; + if (pcap_ts->x <= X_AXIS_MIN || pcap_ts->x >= X_AXIS_MAX || + pcap_ts->y <= Y_AXIS_MIN || pcap_ts->y >= Y_AXIS_MAX) { + /* pen has been released */ + input_report_abs(pcap_ts->input, ABS_PRESSURE, 0); + input_report_key(pcap_ts->input, BTN_TOUCH, 0); + + pcap_ts->read_state = PCAP_ADC_TS_M_STANDBY; + schedule_delayed_work(&pcap_ts->work, 0); + } else { + /* pen is touching the screen */ + input_report_abs(pcap_ts->input, ABS_X, pcap_ts->x); + input_report_abs(pcap_ts->input, ABS_Y, pcap_ts->y); + input_report_key(pcap_ts->input, BTN_TOUCH, 1); + input_report_abs(pcap_ts->input, ABS_PRESSURE, + pcap_ts->pressure); + + /* switch back to pressure read mode */ + pcap_ts->read_state = PCAP_ADC_TS_M_PRESSURE; + schedule_delayed_work(&pcap_ts->work, + msecs_to_jiffies(SAMPLE_DELAY)); + } + input_sync(pcap_ts->input); + break; + default: + dev_warn(&pcap_ts->input->dev, + "pcap_ts: Warning, unhandled read_state %d\n", + pcap_ts->read_state); + break; + } +} + +static void pcap_ts_work(struct work_struct *work) +{ + struct delayed_work *dw = container_of(work, struct delayed_work, work); + struct pcap_ts *pcap_ts = container_of(dw, struct pcap_ts, work); + u8 ch[2]; + + pcap_set_ts_bits(pcap_ts->pcap, + pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT); + + if (pcap_ts->read_state == PCAP_ADC_TS_M_STANDBY) + return; + + /* start adc conversion */ + ch[0] = PCAP_ADC_CH_TS_X1; + ch[1] = PCAP_ADC_CH_TS_Y1; + pcap_adc_async(pcap_ts->pcap, PCAP_ADC_BANK_1, 0, ch, + pcap_ts_read_xy, pcap_ts); +} + +static irqreturn_t pcap_ts_event_touch(int pirq, void *data) +{ + struct pcap_ts *pcap_ts = data; + + if (pcap_ts->read_state == PCAP_ADC_TS_M_STANDBY) { + pcap_ts->read_state = PCAP_ADC_TS_M_PRESSURE; + schedule_delayed_work(&pcap_ts->work, 0); + } + return IRQ_HANDLED; +} + +static int pcap_ts_open(struct input_dev *dev) +{ + struct pcap_ts *pcap_ts = input_get_drvdata(dev); + + pcap_ts->read_state = PCAP_ADC_TS_M_STANDBY; + schedule_delayed_work(&pcap_ts->work, 0); + + return 0; +} + +static void pcap_ts_close(struct input_dev *dev) +{ + struct pcap_ts *pcap_ts = input_get_drvdata(dev); + + cancel_delayed_work_sync(&pcap_ts->work); + + pcap_ts->read_state = PCAP_ADC_TS_M_NONTS; + pcap_set_ts_bits(pcap_ts->pcap, + pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT); +} + +static int __devinit pcap_ts_probe(struct platform_device *pdev) +{ + struct input_dev *input_dev; + struct pcap_ts *pcap_ts; + int err = -ENOMEM; + + pcap_ts = kzalloc(sizeof(*pcap_ts), GFP_KERNEL); + if (!pcap_ts) + return err; + + pcap_ts->pcap = dev_get_drvdata(pdev->dev.parent); + platform_set_drvdata(pdev, pcap_ts); + + input_dev = input_allocate_device(); + if (!input_dev) + goto fail; + + INIT_DELAYED_WORK(&pcap_ts->work, pcap_ts_work); + + pcap_ts->read_state = PCAP_ADC_TS_M_NONTS; + pcap_set_ts_bits(pcap_ts->pcap, + pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT); + + pcap_ts->input = input_dev; + input_set_drvdata(input_dev, pcap_ts); + + input_dev->name = "pcap-touchscreen"; + input_dev->phys = "pcap_ts/input0"; + input_dev->id.bustype = BUS_HOST; + input_dev->id.vendor = 0x0001; + input_dev->id.product = 0x0002; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &pdev->dev; + input_dev->open = pcap_ts_open; + input_dev->close = pcap_ts_close; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(input_dev, ABS_X, X_AXIS_MIN, X_AXIS_MAX, 0, 0); + input_set_abs_params(input_dev, ABS_Y, Y_AXIS_MIN, Y_AXIS_MAX, 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, PRESSURE_MIN, + PRESSURE_MAX, 0, 0); + + err = input_register_device(pcap_ts->input); + if (err) + goto fail_allocate; + + err = request_irq(pcap_to_irq(pcap_ts->pcap, PCAP_IRQ_TS), + pcap_ts_event_touch, 0, "Touch Screen", pcap_ts); + if (err) + goto fail_register; + + return 0; + +fail_register: + input_unregister_device(input_dev); + goto fail; +fail_allocate: + input_free_device(input_dev); +fail: + kfree(pcap_ts); + + return err; +} + +static int __devexit pcap_ts_remove(struct platform_device *pdev) +{ + struct pcap_ts *pcap_ts = platform_get_drvdata(pdev); + + free_irq(pcap_to_irq(pcap_ts->pcap, PCAP_IRQ_TS), pcap_ts); + cancel_delayed_work_sync(&pcap_ts->work); + + input_unregister_device(pcap_ts->input); + + kfree(pcap_ts); + + return 0; +} + +#ifdef CONFIG_PM +static int pcap_ts_suspend(struct device *dev) +{ + struct pcap_ts *pcap_ts = dev_get_drvdata(dev); + + pcap_set_ts_bits(pcap_ts->pcap, PCAP_ADC_TS_REF_LOWPWR); + return 0; +} + +static int pcap_ts_resume(struct device *dev) +{ + struct pcap_ts *pcap_ts = dev_get_drvdata(dev); + + pcap_set_ts_bits(pcap_ts->pcap, + pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT); + return 0; +} + +static struct dev_pm_ops pcap_ts_pm_ops = { + .suspend = pcap_ts_suspend, + .resume = pcap_ts_resume, +}; +#define PCAP_TS_PM_OPS (&pcap_ts_pm_ops) +#else +#define PCAP_TS_PM_OPS NULL +#endif + +static struct platform_driver pcap_ts_driver = { + .probe = pcap_ts_probe, + .remove = __devexit_p(pcap_ts_remove), + .driver = { + .name = "pcap-ts", + .owner = THIS_MODULE, + .pm = PCAP_TS_PM_OPS, + }, +}; + +static int __init pcap_ts_init(void) +{ + return platform_driver_register(&pcap_ts_driver); +} + +static void __exit pcap_ts_exit(void) +{ + platform_driver_unregister(&pcap_ts_driver); +} + +module_init(pcap_ts_init); +module_exit(pcap_ts_exit); + +MODULE_DESCRIPTION("Motorola PCAP2 touchscreen driver"); +MODULE_AUTHOR("Daniel Ribeiro / Harald Welte"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:pcap_ts"); -- cgit v1.2.1 From d0a821324819a2908b886ae8b2f33fc7824ff83f Mon Sep 17 00:00:00 2001 From: Daniel Ribeiro Date: Mon, 10 Aug 2009 20:27:48 +0200 Subject: input: PCAP2 misc input driver This is a driver for misc input events for the PCAP2 PMIC, it handles the Power key and the Headphone button. Signed-off-by: Daniel Ribeiro Signed-off-by: Ilya Petrov Signed-off-by: Antonio Ospite Acked-by: Dmitry Torokhov Signed-off-by: Samuel Ortiz --- drivers/input/misc/Kconfig | 10 +++ drivers/input/misc/Makefile | 2 + drivers/input/misc/pcap_keys.c | 144 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 156 insertions(+) create mode 100644 drivers/input/misc/pcap_keys.c (limited to 'drivers/input') diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 852941d108ff..1a50be379cbc 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -289,4 +289,14 @@ config INPUT_WM831X_ON To compile this driver as a module, choose M here: the module will be called wm831x_on. +config INPUT_PCAP + tristate "Motorola EZX PCAP misc input events" + depends on EZX_PCAP + help + Say Y here if you want to use Power key and Headphone button + on Motorola EZX phones. + + To compile this driver as a module, choose M here: the + module will be called pcap_keys. + endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index c97533fb83c8..bf4db626c313 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o +obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o obj-$(CONFIG_INPUT_POWERMATE) += powermate.o @@ -28,3 +29,4 @@ obj-$(CONFIG_INPUT_UINPUT) += uinput.o obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o + diff --git a/drivers/input/misc/pcap_keys.c b/drivers/input/misc/pcap_keys.c new file mode 100644 index 000000000000..7ea969347ca9 --- /dev/null +++ b/drivers/input/misc/pcap_keys.c @@ -0,0 +1,144 @@ +/* + * Input driver for PCAP events: + * * Power key + * * Headphone button + * + * Copyright (c) 2008,2009 Ilya Petrov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include + +struct pcap_keys { + struct pcap_chip *pcap; + struct input_dev *input; +}; + +/* PCAP2 interrupts us on keypress */ +static irqreturn_t pcap_keys_handler(int irq, void *_pcap_keys) +{ + struct pcap_keys *pcap_keys = _pcap_keys; + int pirq = irq_to_pcap(pcap_keys->pcap, irq); + u32 pstat; + + ezx_pcap_read(pcap_keys->pcap, PCAP_REG_PSTAT, &pstat); + pstat &= 1 << pirq; + + switch (pirq) { + case PCAP_IRQ_ONOFF: + input_report_key(pcap_keys->input, KEY_POWER, !pstat); + break; + case PCAP_IRQ_MIC: + input_report_key(pcap_keys->input, KEY_HP, !pstat); + break; + } + + input_sync(pcap_keys->input); + + return IRQ_HANDLED; +} + +static int __devinit pcap_keys_probe(struct platform_device *pdev) +{ + int err = -ENOMEM; + struct pcap_keys *pcap_keys; + struct input_dev *input_dev; + + pcap_keys = kmalloc(sizeof(struct pcap_keys), GFP_KERNEL); + if (!pcap_keys) + return err; + + pcap_keys->pcap = dev_get_drvdata(pdev->dev.parent); + + input_dev = input_allocate_device(); + if (!input_dev) + goto fail; + + pcap_keys->input = input_dev; + + platform_set_drvdata(pdev, pcap_keys); + input_dev->name = pdev->name; + input_dev->phys = "pcap-keys/input0"; + input_dev->id.bustype = BUS_HOST; + input_dev->dev.parent = &pdev->dev; + + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(KEY_POWER, input_dev->keybit); + __set_bit(KEY_HP, input_dev->keybit); + + err = input_register_device(input_dev); + if (err) + goto fail_allocate; + + err = request_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_ONOFF), + pcap_keys_handler, 0, "Power key", pcap_keys); + if (err) + goto fail_register; + + err = request_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_MIC), + pcap_keys_handler, 0, "Headphone button", pcap_keys); + if (err) + goto fail_pwrkey; + + return 0; + +fail_pwrkey: + free_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_ONOFF), pcap_keys); +fail_register: + input_unregister_device(input_dev); + goto fail; +fail_allocate: + input_free_device(input_dev); +fail: + kfree(pcap_keys); + return err; +} + +static int __devexit pcap_keys_remove(struct platform_device *pdev) +{ + struct pcap_keys *pcap_keys = platform_get_drvdata(pdev); + + free_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_ONOFF), pcap_keys); + free_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_MIC), pcap_keys); + + input_unregister_device(pcap_keys->input); + kfree(pcap_keys); + + return 0; +} + +static struct platform_driver pcap_keys_device_driver = { + .probe = pcap_keys_probe, + .remove = __devexit_p(pcap_keys_remove), + .driver = { + .name = "pcap-keys", + .owner = THIS_MODULE, + } +}; + +static int __init pcap_keys_init(void) +{ + return platform_driver_register(&pcap_keys_device_driver); +}; + +static void __exit pcap_keys_exit(void) +{ + platform_driver_unregister(&pcap_keys_device_driver); +}; + +module_init(pcap_keys_init); +module_exit(pcap_keys_exit); + +MODULE_DESCRIPTION("Motorola PCAP2 input events driver"); +MODULE_AUTHOR("Ilya Petrov "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:pcap_keys"); -- cgit v1.2.1 From e454cea20bdcff10ee698d11b8882662a0153a47 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Fri, 18 Sep 2009 23:01:12 +0200 Subject: Driver-Core: extend devnode callbacks to provide permissions This allows subsytems to provide devtmpfs with non-default permissions for the device node. Instead of the default mode of 0600, null, zero, random, urandom, full, tty, ptmx now have a mode of 0666, which allows non-privileged processes to access standard device nodes in case no other userspace process applies the expected permissions. This also fixes a wrong assignment in pktcdvd and a checkpatch.pl complain. Signed-off-by: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/input/input.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/input') diff --git a/drivers/input/input.c b/drivers/input/input.c index 851791d955f3..556539d617a4 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -1265,14 +1265,14 @@ static struct device_type input_dev_type = { .uevent = input_dev_uevent, }; -static char *input_nodename(struct device *dev) +static char *input_devnode(struct device *dev, mode_t *mode) { return kasprintf(GFP_KERNEL, "input/%s", dev_name(dev)); } struct class input_class = { .name = "input", - .nodename = input_nodename, + .devnode = input_devnode, }; EXPORT_SYMBOL_GPL(input_class); -- cgit v1.2.1 From 36726dd9229af008f31edd46b22a658354783232 Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Tue, 21 Jul 2009 15:57:47 -0300 Subject: trivial: fix typo s/ketymap/keymap/ in comment Signed-off-by: Thadeu Lima de Souza Cascardo Signed-off-by: Jiri Kosina --- drivers/input/keyboard/atkbd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index c9523e48c6ad..adb09e2ba394 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -229,7 +229,7 @@ struct atkbd { }; /* - * System-specific ketymap fixup routine + * System-specific keymap fixup routine */ static void (*atkbd_platform_fixup)(struct atkbd *, const void *data); static void *atkbd_platform_fixup_data; -- cgit v1.2.1 From e258b80e691f1f3ae83a60aa80eaf7322bd55ec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20H=C3=A4rdeman?= Date: Mon, 21 Sep 2009 17:04:53 -0700 Subject: input: add a driver for the Winbond WPCD376I Consumer IR hardware MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a driver for the the Consumer IR (CIR) functionality of the Winbond WPCD376I chipset (found on e.g. Intel DG45FC motherboards). Signed-off-by: David Härdeman Reviewed-by: Jesse Barnes Cc: Dmitry Torokhov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/input/misc/Kconfig | 16 + drivers/input/misc/Makefile | 1 + drivers/input/misc/winbond-cir.c | 1614 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 1631 insertions(+) create mode 100644 drivers/input/misc/winbond-cir.c (limited to 'drivers/input') diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 1a50be379cbc..76d6751f89a7 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -222,6 +222,22 @@ config INPUT_SGI_BTNS To compile this driver as a module, choose M here: the module will be called sgi_btns. +config INPUT_WINBOND_CIR + tristate "Winbond IR remote control" + depends on X86 && PNP + select LEDS_CLASS + select BITREVERSE + help + Say Y here if you want to use the IR remote functionality found + in some Winbond SuperI/O chips. Currently only the WPCD376I + chip is supported (included in some Intel Media series motherboards). + + IR Receive and wake-on-IR from suspend and power-off is currently + supported. + + To compile this driver as a module, choose M here: the module will be + called winbond_cir. + config HP_SDC_RTC tristate "HP SDC Real Time Clock" depends on (GSC || HP300) && SERIO diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index bf4db626c313..a8b84854fb7b 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o obj-$(CONFIG_INPUT_UINPUT) += uinput.o +obj-$(CONFIG_INPUT_WINBOND_CIR) += winbond-cir.o obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o diff --git a/drivers/input/misc/winbond-cir.c b/drivers/input/misc/winbond-cir.c new file mode 100644 index 000000000000..33309fe44e20 --- /dev/null +++ b/drivers/input/misc/winbond-cir.c @@ -0,0 +1,1614 @@ +/* + * winbond-cir.c - Driver for the Consumer IR functionality of Winbond + * SuperI/O chips. + * + * Currently supports the Winbond WPCD376i chip (PNP id WEC1022), but + * could probably support others (Winbond WEC102X, NatSemi, etc) + * with minor modifications. + * + * Original Author: David Härdeman + * Copyright (C) 2009 David Härdeman + * + * Dedicated to Matilda, my newborn daughter, without whose loving attention + * this driver would have been finished in half the time and with a fraction + * of the bugs. + * + * Written using: + * o Winbond WPCD376I datasheet helpfully provided by Jesse Barnes at Intel + * o NatSemi PC87338/PC97338 datasheet (for the serial port stuff) + * o DSDT dumps + * + * Supported features: + * o RC6 + * o Wake-On-CIR functionality + * + * To do: + * o Test NEC and RC5 + * + * Left as an exercise for the reader: + * o Learning (I have neither the hardware, nor the need) + * o IR Transmit (ibid) + * + * 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 of the License, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRVNAME "winbond-cir" + +/* CEIR Wake-Up Registers, relative to data->wbase */ +#define WBCIR_REG_WCEIR_CTL 0x03 /* CEIR Receiver Control */ +#define WBCIR_REG_WCEIR_STS 0x04 /* CEIR Receiver Status */ +#define WBCIR_REG_WCEIR_EV_EN 0x05 /* CEIR Receiver Event Enable */ +#define WBCIR_REG_WCEIR_CNTL 0x06 /* CEIR Receiver Counter Low */ +#define WBCIR_REG_WCEIR_CNTH 0x07 /* CEIR Receiver Counter High */ +#define WBCIR_REG_WCEIR_INDEX 0x08 /* CEIR Receiver Index */ +#define WBCIR_REG_WCEIR_DATA 0x09 /* CEIR Receiver Data */ +#define WBCIR_REG_WCEIR_CSL 0x0A /* CEIR Re. Compare Strlen */ +#define WBCIR_REG_WCEIR_CFG1 0x0B /* CEIR Re. Configuration 1 */ +#define WBCIR_REG_WCEIR_CFG2 0x0C /* CEIR Re. Configuration 2 */ + +/* CEIR Enhanced Functionality Registers, relative to data->ebase */ +#define WBCIR_REG_ECEIR_CTS 0x00 /* Enhanced IR Control Status */ +#define WBCIR_REG_ECEIR_CCTL 0x01 /* Infrared Counter Control */ +#define WBCIR_REG_ECEIR_CNT_LO 0x02 /* Infrared Counter LSB */ +#define WBCIR_REG_ECEIR_CNT_HI 0x03 /* Infrared Counter MSB */ +#define WBCIR_REG_ECEIR_IREM 0x04 /* Infrared Emitter Status */ + +/* SP3 Banked Registers, relative to data->sbase */ +#define WBCIR_REG_SP3_BSR 0x03 /* Bank Select, all banks */ + /* Bank 0 */ +#define WBCIR_REG_SP3_RXDATA 0x00 /* FIFO RX data (r) */ +#define WBCIR_REG_SP3_TXDATA 0x00 /* FIFO TX data (w) */ +#define WBCIR_REG_SP3_IER 0x01 /* Interrupt Enable */ +#define WBCIR_REG_SP3_EIR 0x02 /* Event Identification (r) */ +#define WBCIR_REG_SP3_FCR 0x02 /* FIFO Control (w) */ +#define WBCIR_REG_SP3_MCR 0x04 /* Mode Control */ +#define WBCIR_REG_SP3_LSR 0x05 /* Link Status */ +#define WBCIR_REG_SP3_MSR 0x06 /* Modem Status */ +#define WBCIR_REG_SP3_ASCR 0x07 /* Aux Status and Control */ + /* Bank 2 */ +#define WBCIR_REG_SP3_BGDL 0x00 /* Baud Divisor LSB */ +#define WBCIR_REG_SP3_BGDH 0x01 /* Baud Divisor MSB */ +#define WBCIR_REG_SP3_EXCR1 0x02 /* Extended Control 1 */ +#define WBCIR_REG_SP3_EXCR2 0x04 /* Extended Control 2 */ +#define WBCIR_REG_SP3_TXFLV 0x06 /* TX FIFO Level */ +#define WBCIR_REG_SP3_RXFLV 0x07 /* RX FIFO Level */ + /* Bank 3 */ +#define WBCIR_REG_SP3_MRID 0x00 /* Module Identification */ +#define WBCIR_REG_SP3_SH_LCR 0x01 /* LCR Shadow */ +#define WBCIR_REG_SP3_SH_FCR 0x02 /* FCR Shadow */ + /* Bank 4 */ +#define WBCIR_REG_SP3_IRCR1 0x02 /* Infrared Control 1 */ + /* Bank 5 */ +#define WBCIR_REG_SP3_IRCR2 0x04 /* Infrared Control 2 */ + /* Bank 6 */ +#define WBCIR_REG_SP3_IRCR3 0x00 /* Infrared Control 3 */ +#define WBCIR_REG_SP3_SIR_PW 0x02 /* SIR Pulse Width */ + /* Bank 7 */ +#define WBCIR_REG_SP3_IRRXDC 0x00 /* IR RX Demod Control */ +#define WBCIR_REG_SP3_IRTXMC 0x01 /* IR TX Mod Control */ +#define WBCIR_REG_SP3_RCCFG 0x02 /* CEIR Config */ +#define WBCIR_REG_SP3_IRCFG1 0x04 /* Infrared Config 1 */ +#define WBCIR_REG_SP3_IRCFG4 0x07 /* Infrared Config 4 */ + +/* + * Magic values follow + */ + +/* No interrupts for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */ +#define WBCIR_IRQ_NONE 0x00 +/* RX data bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */ +#define WBCIR_IRQ_RX 0x01 +/* Over/Under-flow bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */ +#define WBCIR_IRQ_ERR 0x04 +/* Led enable/disable bit for WBCIR_REG_ECEIR_CTS */ +#define WBCIR_LED_ENABLE 0x80 +/* RX data available bit for WBCIR_REG_SP3_LSR */ +#define WBCIR_RX_AVAIL 0x01 +/* RX disable bit for WBCIR_REG_SP3_ASCR */ +#define WBCIR_RX_DISABLE 0x20 +/* Extended mode enable bit for WBCIR_REG_SP3_EXCR1 */ +#define WBCIR_EXT_ENABLE 0x01 +/* Select compare register in WBCIR_REG_WCEIR_INDEX (bits 5 & 6) */ +#define WBCIR_REGSEL_COMPARE 0x10 +/* Select mask register in WBCIR_REG_WCEIR_INDEX (bits 5 & 6) */ +#define WBCIR_REGSEL_MASK 0x20 +/* Starting address of selected register in WBCIR_REG_WCEIR_INDEX */ +#define WBCIR_REG_ADDR0 0x00 + +/* Valid banks for the SP3 UART */ +enum wbcir_bank { + WBCIR_BANK_0 = 0x00, + WBCIR_BANK_1 = 0x80, + WBCIR_BANK_2 = 0xE0, + WBCIR_BANK_3 = 0xE4, + WBCIR_BANK_4 = 0xE8, + WBCIR_BANK_5 = 0xEC, + WBCIR_BANK_6 = 0xF0, + WBCIR_BANK_7 = 0xF4, +}; + +/* Supported IR Protocols */ +enum wbcir_protocol { + IR_PROTOCOL_RC5 = 0x0, + IR_PROTOCOL_NEC = 0x1, + IR_PROTOCOL_RC6 = 0x2, +}; + +/* Misc */ +#define WBCIR_NAME "Winbond CIR" +#define WBCIR_ID_FAMILY 0xF1 /* Family ID for the WPCD376I */ +#define WBCIR_ID_CHIP 0x04 /* Chip ID for the WPCD376I */ +#define IR_KEYPRESS_TIMEOUT 250 /* FIXME: should be per-protocol? */ +#define INVALID_SCANCODE 0x7FFFFFFF /* Invalid with all protos */ +#define WAKEUP_IOMEM_LEN 0x10 /* Wake-Up I/O Reg Len */ +#define EHFUNC_IOMEM_LEN 0x10 /* Enhanced Func I/O Reg Len */ +#define SP_IOMEM_LEN 0x08 /* Serial Port 3 (IR) Reg Len */ +#define WBCIR_MAX_IDLE_BYTES 10 + +static DEFINE_SPINLOCK(wbcir_lock); +static DEFINE_RWLOCK(keytable_lock); + +struct wbcir_key { + u32 scancode; + unsigned int keycode; +}; + +struct wbcir_keyentry { + struct wbcir_key key; + struct list_head list; +}; + +static struct wbcir_key rc6_def_keymap[] = { + { 0x800F0400, KEY_NUMERIC_0 }, + { 0x800F0401, KEY_NUMERIC_1 }, + { 0x800F0402, KEY_NUMERIC_2 }, + { 0x800F0403, KEY_NUMERIC_3 }, + { 0x800F0404, KEY_NUMERIC_4 }, + { 0x800F0405, KEY_NUMERIC_5 }, + { 0x800F0406, KEY_NUMERIC_6 }, + { 0x800F0407, KEY_NUMERIC_7 }, + { 0x800F0408, KEY_NUMERIC_8 }, + { 0x800F0409, KEY_NUMERIC_9 }, + { 0x800F041D, KEY_NUMERIC_STAR }, + { 0x800F041C, KEY_NUMERIC_POUND }, + { 0x800F0410, KEY_VOLUMEUP }, + { 0x800F0411, KEY_VOLUMEDOWN }, + { 0x800F0412, KEY_CHANNELUP }, + { 0x800F0413, KEY_CHANNELDOWN }, + { 0x800F040E, KEY_MUTE }, + { 0x800F040D, KEY_VENDOR }, /* Vista Logo Key */ + { 0x800F041E, KEY_UP }, + { 0x800F041F, KEY_DOWN }, + { 0x800F0420, KEY_LEFT }, + { 0x800F0421, KEY_RIGHT }, + { 0x800F0422, KEY_OK }, + { 0x800F0423, KEY_ESC }, + { 0x800F040F, KEY_INFO }, + { 0x800F040A, KEY_CLEAR }, + { 0x800F040B, KEY_ENTER }, + { 0x800F045B, KEY_RED }, + { 0x800F045C, KEY_GREEN }, + { 0x800F045D, KEY_YELLOW }, + { 0x800F045E, KEY_BLUE }, + { 0x800F045A, KEY_TEXT }, + { 0x800F0427, KEY_SWITCHVIDEOMODE }, + { 0x800F040C, KEY_POWER }, + { 0x800F0450, KEY_RADIO }, + { 0x800F0448, KEY_PVR }, + { 0x800F0447, KEY_AUDIO }, + { 0x800F0426, KEY_EPG }, + { 0x800F0449, KEY_CAMERA }, + { 0x800F0425, KEY_TV }, + { 0x800F044A, KEY_VIDEO }, + { 0x800F0424, KEY_DVD }, + { 0x800F0416, KEY_PLAY }, + { 0x800F0418, KEY_PAUSE }, + { 0x800F0419, KEY_STOP }, + { 0x800F0414, KEY_FASTFORWARD }, + { 0x800F041A, KEY_NEXT }, + { 0x800F041B, KEY_PREVIOUS }, + { 0x800F0415, KEY_REWIND }, + { 0x800F0417, KEY_RECORD }, +}; + +/* Registers and other state is protected by wbcir_lock */ +struct wbcir_data { + unsigned long wbase; /* Wake-Up Baseaddr */ + unsigned long ebase; /* Enhanced Func. Baseaddr */ + unsigned long sbase; /* Serial Port Baseaddr */ + unsigned int irq; /* Serial Port IRQ */ + + struct input_dev *input_dev; + struct timer_list timer_keyup; + struct led_trigger *rxtrigger; + struct led_trigger *txtrigger; + struct led_classdev led; + + u32 last_scancode; + unsigned int last_keycode; + u8 last_toggle; + u8 keypressed; + unsigned long keyup_jiffies; + unsigned int idle_count; + + /* RX irdata and parsing state */ + unsigned long irdata[30]; + unsigned int irdata_count; + unsigned int irdata_idle; + unsigned int irdata_off; + unsigned int irdata_error; + + /* Protected by keytable_lock */ + struct list_head keytable; +}; + +static enum wbcir_protocol protocol = IR_PROTOCOL_RC6; +module_param(protocol, uint, 0444); +MODULE_PARM_DESC(protocol, "IR protocol to use " + "(0 = RC5, 1 = NEC, 2 = RC6A, default)"); + +static int invert; /* default = 0 */ +module_param(invert, bool, 0444); +MODULE_PARM_DESC(invert, "Invert the signal from the IR receiver"); + +static unsigned int wake_sc = 0x800F040C; +module_param(wake_sc, uint, 0644); +MODULE_PARM_DESC(wake_sc, "Scancode of the power-on IR command"); + +static unsigned int wake_rc6mode = 6; +module_param(wake_rc6mode, uint, 0644); +MODULE_PARM_DESC(wake_rc6mode, "RC6 mode for the power-on command " + "(0 = 0, 6 = 6A, default)"); + + + +/***************************************************************************** + * + * UTILITY FUNCTIONS + * + *****************************************************************************/ + +/* Caller needs to hold wbcir_lock */ +static void +wbcir_set_bits(unsigned long addr, u8 bits, u8 mask) +{ + u8 val; + + val = inb(addr); + val = ((val & ~mask) | (bits & mask)); + outb(val, addr); +} + +/* Selects the register bank for the serial port */ +static inline void +wbcir_select_bank(struct wbcir_data *data, enum wbcir_bank bank) +{ + outb(bank, data->sbase + WBCIR_REG_SP3_BSR); +} + +static enum led_brightness +wbcir_led_brightness_get(struct led_classdev *led_cdev) +{ + struct wbcir_data *data = container_of(led_cdev, + struct wbcir_data, + led); + + if (inb(data->ebase + WBCIR_REG_ECEIR_CTS) & WBCIR_LED_ENABLE) + return LED_FULL; + else + return LED_OFF; +} + +static void +wbcir_led_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct wbcir_data *data = container_of(led_cdev, + struct wbcir_data, + led); + + wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CTS, + brightness == LED_OFF ? 0x00 : WBCIR_LED_ENABLE, + WBCIR_LED_ENABLE); +} + +/* Manchester encodes bits to RC6 message cells (see wbcir_parse_rc6) */ +static u8 +wbcir_to_rc6cells(u8 val) +{ + u8 coded = 0x00; + int i; + + val &= 0x0F; + for (i = 0; i < 4; i++) { + if (val & 0x01) + coded |= 0x02 << (i * 2); + else + coded |= 0x01 << (i * 2); + val >>= 1; + } + + return coded; +} + + + +/***************************************************************************** + * + * INPUT FUNCTIONS + * + *****************************************************************************/ + +static unsigned int +wbcir_do_getkeycode(struct wbcir_data *data, u32 scancode) +{ + struct wbcir_keyentry *keyentry; + unsigned int keycode = KEY_RESERVED; + unsigned long flags; + + read_lock_irqsave(&keytable_lock, flags); + + list_for_each_entry(keyentry, &data->keytable, list) { + if (keyentry->key.scancode == scancode) { + keycode = keyentry->key.keycode; + break; + } + } + + read_unlock_irqrestore(&keytable_lock, flags); + return keycode; +} + +static int +wbcir_getkeycode(struct input_dev *dev, int scancode, int *keycode) +{ + struct wbcir_data *data = input_get_drvdata(dev); + + *keycode = (int)wbcir_do_getkeycode(data, (u32)scancode); + return 0; +} + +static int +wbcir_setkeycode(struct input_dev *dev, int sscancode, int keycode) +{ + struct wbcir_data *data = input_get_drvdata(dev); + struct wbcir_keyentry *keyentry; + struct wbcir_keyentry *new_keyentry; + unsigned long flags; + unsigned int old_keycode = KEY_RESERVED; + u32 scancode = (u32)sscancode; + + if (keycode < 0 || keycode > KEY_MAX) + return -EINVAL; + + new_keyentry = kmalloc(sizeof(*new_keyentry), GFP_KERNEL); + if (!new_keyentry) + return -ENOMEM; + + write_lock_irqsave(&keytable_lock, flags); + + list_for_each_entry(keyentry, &data->keytable, list) { + if (keyentry->key.scancode != scancode) + continue; + + old_keycode = keyentry->key.keycode; + keyentry->key.keycode = keycode; + + if (keyentry->key.keycode == KEY_RESERVED) { + list_del(&keyentry->list); + kfree(keyentry); + } + + break; + } + + set_bit(keycode, dev->keybit); + + if (old_keycode == KEY_RESERVED) { + new_keyentry->key.scancode = scancode; + new_keyentry->key.keycode = keycode; + list_add(&new_keyentry->list, &data->keytable); + } else { + kfree(new_keyentry); + clear_bit(old_keycode, dev->keybit); + list_for_each_entry(keyentry, &data->keytable, list) { + if (keyentry->key.keycode == old_keycode) { + set_bit(old_keycode, dev->keybit); + break; + } + } + } + + write_unlock_irqrestore(&keytable_lock, flags); + return 0; +} + +/* + * Timer function to report keyup event some time after keydown is + * reported by the ISR. + */ +static void +wbcir_keyup(unsigned long cookie) +{ + struct wbcir_data *data = (struct wbcir_data *)cookie; + unsigned long flags; + + /* + * data->keyup_jiffies is used to prevent a race condition if a + * hardware interrupt occurs at this point and the keyup timer + * event is moved further into the future as a result. + * + * The timer will then be reactivated and this function called + * again in the future. We need to exit gracefully in that case + * to allow the input subsystem to do its auto-repeat magic or + * a keyup event might follow immediately after the keydown. + */ + + spin_lock_irqsave(&wbcir_lock, flags); + + if (time_is_after_eq_jiffies(data->keyup_jiffies) && data->keypressed) { + data->keypressed = 0; + led_trigger_event(data->rxtrigger, LED_OFF); + input_report_key(data->input_dev, data->last_keycode, 0); + input_sync(data->input_dev); + } + + spin_unlock_irqrestore(&wbcir_lock, flags); +} + +static void +wbcir_keydown(struct wbcir_data *data, u32 scancode, u8 toggle) +{ + unsigned int keycode; + + /* Repeat? */ + if (data->last_scancode == scancode && + data->last_toggle == toggle && + data->keypressed) + goto set_timer; + data->last_scancode = scancode; + + /* Do we need to release an old keypress? */ + if (data->keypressed) { + input_report_key(data->input_dev, data->last_keycode, 0); + input_sync(data->input_dev); + data->keypressed = 0; + } + + /* Report scancode */ + input_event(data->input_dev, EV_MSC, MSC_SCAN, (int)scancode); + + /* Do we know this scancode? */ + keycode = wbcir_do_getkeycode(data, scancode); + if (keycode == KEY_RESERVED) + goto set_timer; + + /* Register a keypress */ + input_report_key(data->input_dev, keycode, 1); + data->keypressed = 1; + data->last_keycode = keycode; + data->last_toggle = toggle; + +set_timer: + input_sync(data->input_dev); + led_trigger_event(data->rxtrigger, + data->keypressed ? LED_FULL : LED_OFF); + data->keyup_jiffies = jiffies + msecs_to_jiffies(IR_KEYPRESS_TIMEOUT); + mod_timer(&data->timer_keyup, data->keyup_jiffies); +} + + + +/***************************************************************************** + * + * IR PARSING FUNCTIONS + * + *****************************************************************************/ + +/* Resets all irdata */ +static void +wbcir_reset_irdata(struct wbcir_data *data) +{ + memset(data->irdata, 0, sizeof(data->irdata)); + data->irdata_count = 0; + data->irdata_off = 0; + data->irdata_error = 0; +} + +/* Adds one bit of irdata */ +static void +add_irdata_bit(struct wbcir_data *data, int set) +{ + if (data->irdata_count >= sizeof(data->irdata) * 8) { + data->irdata_error = 1; + return; + } + + if (set) + __set_bit(data->irdata_count, data->irdata); + data->irdata_count++; +} + +/* Gets count bits of irdata */ +static u16 +get_bits(struct wbcir_data *data, int count) +{ + u16 val = 0x0; + + if (data->irdata_count - data->irdata_off < count) { + data->irdata_error = 1; + return 0x0; + } + + while (count > 0) { + val <<= 1; + if (test_bit(data->irdata_off, data->irdata)) + val |= 0x1; + count--; + data->irdata_off++; + } + + return val; +} + +/* Reads 16 cells and converts them to a byte */ +static u8 +wbcir_rc6cells_to_byte(struct wbcir_data *data) +{ + u16 raw = get_bits(data, 16); + u8 val = 0x00; + int bit; + + for (bit = 0; bit < 8; bit++) { + switch (raw & 0x03) { + case 0x01: + break; + case 0x02: + val |= (0x01 << bit); + break; + default: + data->irdata_error = 1; + break; + } + raw >>= 2; + } + + return val; +} + +/* Decodes a number of bits from raw RC5 data */ +static u8 +wbcir_get_rc5bits(struct wbcir_data *data, unsigned int count) +{ + u16 raw = get_bits(data, count * 2); + u8 val = 0x00; + int bit; + + for (bit = 0; bit < count; bit++) { + switch (raw & 0x03) { + case 0x01: + val |= (0x01 << bit); + break; + case 0x02: + break; + default: + data->irdata_error = 1; + break; + } + raw >>= 2; + } + + return val; +} + +static void +wbcir_parse_rc6(struct device *dev, struct wbcir_data *data) +{ + /* + * Normal bits are manchester coded as follows: + * cell0 + cell1 = logic "0" + * cell1 + cell0 = logic "1" + * + * The IR pulse has the following components: + * + * Leader - 6 * cell1 - discarded + * Gap - 2 * cell0 - discarded + * Start bit - Normal Coding - always "1" + * Mode Bit 2 - 0 - Normal Coding + * Toggle bit - Normal Coding with double bit time, + * e.g. cell0 + cell0 + cell1 + cell1 + * means logic "0". + * + * The rest depends on the mode, the following modes are known: + * + * MODE 0: + * Address Bit 7 - 0 - Normal Coding + * Command Bit 7 - 0 - Normal Coding + * + * MODE 6: + * The above Toggle Bit is used as a submode bit, 0 = A, 1 = B. + * Submode B is for pointing devices, only remotes using submode A + * are supported. + * + * Customer range bit - 0 => Customer = 7 bits, 0...127 + * 1 => Customer = 15 bits, 32768...65535 + * Customer Bits - Normal Coding + * + * Customer codes are allocated by Philips. The rest of the bits + * are customer dependent. The following is commonly used (and the + * only supported config): + * + * Toggle Bit - Normal Coding + * Address Bit 6 - 0 - Normal Coding + * Command Bit 7 - 0 - Normal Coding + * + * All modes are followed by at least 6 * cell0. + * + * MODE 0 msglen: + * 1 * 2 (start bit) + 3 * 2 (mode) + 2 * 2 (toggle) + + * 8 * 2 (address) + 8 * 2 (command) = + * 44 cells + * + * MODE 6A msglen: + * 1 * 2 (start bit) + 3 * 2 (mode) + 2 * 2 (submode) + + * 1 * 2 (customer range bit) + 7/15 * 2 (customer bits) + + * 1 * 2 (toggle bit) + 7 * 2 (address) + 8 * 2 (command) = + * 60 - 76 cells + */ + u8 mode; + u8 toggle; + u16 customer = 0x0; + u8 address; + u8 command; + u32 scancode; + + /* Leader mark */ + while (get_bits(data, 1) && !data->irdata_error) + /* Do nothing */; + + /* Leader space */ + if (get_bits(data, 1)) { + dev_dbg(dev, "RC6 - Invalid leader space\n"); + return; + } + + /* Start bit */ + if (get_bits(data, 2) != 0x02) { + dev_dbg(dev, "RC6 - Invalid start bit\n"); + return; + } + + /* Mode */ + mode = get_bits(data, 6); + switch (mode) { + case 0x15: /* 010101 = b000 */ + mode = 0; + break; + case 0x29: /* 101001 = b110 */ + mode = 6; + break; + default: + dev_dbg(dev, "RC6 - Invalid mode\n"); + return; + } + + /* Toggle bit / Submode bit */ + toggle = get_bits(data, 4); + switch (toggle) { + case 0x03: + toggle = 0; + break; + case 0x0C: + toggle = 1; + break; + default: + dev_dbg(dev, "RC6 - Toggle bit error\n"); + break; + } + + /* Customer */ + if (mode == 6) { + if (toggle != 0) { + dev_dbg(dev, "RC6B - Not Supported\n"); + return; + } + + customer = wbcir_rc6cells_to_byte(data); + + if (customer & 0x80) { + /* 15 bit customer value */ + customer <<= 8; + customer |= wbcir_rc6cells_to_byte(data); + } + } + + /* Address */ + address = wbcir_rc6cells_to_byte(data); + if (mode == 6) { + toggle = address >> 7; + address &= 0x7F; + } + + /* Command */ + command = wbcir_rc6cells_to_byte(data); + + /* Create scancode */ + scancode = command; + scancode |= address << 8; + scancode |= customer << 16; + + /* Last sanity check */ + if (data->irdata_error) { + dev_dbg(dev, "RC6 - Cell error(s)\n"); + return; + } + + dev_info(dev, "IR-RC6 ad 0x%02X cm 0x%02X cu 0x%04X " + "toggle %u mode %u scan 0x%08X\n", + address, + command, + customer, + (unsigned int)toggle, + (unsigned int)mode, + scancode); + + wbcir_keydown(data, scancode, toggle); +} + +static void +wbcir_parse_rc5(struct device *dev, struct wbcir_data *data) +{ + /* + * Bits are manchester coded as follows: + * cell1 + cell0 = logic "0" + * cell0 + cell1 = logic "1" + * (i.e. the reverse of RC6) + * + * Start bit 1 - "1" - discarded + * Start bit 2 - Must be inverted to get command bit 6 + * Toggle bit + * Address Bit 4 - 0 + * Command Bit 5 - 0 + */ + u8 toggle; + u8 address; + u8 command; + u32 scancode; + + /* Start bit 1 */ + if (!get_bits(data, 1)) { + dev_dbg(dev, "RC5 - Invalid start bit\n"); + return; + } + + /* Start bit 2 */ + if (!wbcir_get_rc5bits(data, 1)) + command = 0x40; + else + command = 0x00; + + toggle = wbcir_get_rc5bits(data, 1); + address = wbcir_get_rc5bits(data, 5); + command |= wbcir_get_rc5bits(data, 6); + scancode = address << 7 | command; + + /* Last sanity check */ + if (data->irdata_error) { + dev_dbg(dev, "RC5 - Invalid message\n"); + return; + } + + dev_dbg(dev, "IR-RC5 ad %u cm %u t %u s %u\n", + (unsigned int)address, + (unsigned int)command, + (unsigned int)toggle, + (unsigned int)scancode); + + wbcir_keydown(data, scancode, toggle); +} + +static void +wbcir_parse_nec(struct device *dev, struct wbcir_data *data) +{ + /* + * Each bit represents 560 us. + * + * Leader - 9 ms burst + * Gap - 4.5 ms silence + * Address1 bit 0 - 7 - Address 1 + * Address2 bit 0 - 7 - Address 2 + * Command1 bit 0 - 7 - Command 1 + * Command2 bit 0 - 7 - Command 2 + * + * Note the bit order! + * + * With the old NEC protocol, Address2 was the inverse of Address1 + * and Command2 was the inverse of Command1 and were used as + * an error check. + * + * With NEC extended, Address1 is the LSB of the Address and + * Address2 is the MSB, Command parsing remains unchanged. + * + * A repeat message is coded as: + * Leader - 9 ms burst + * Gap - 2.25 ms silence + * Repeat - 560 us active + */ + u8 address1; + u8 address2; + u8 command1; + u8 command2; + u16 address; + u32 scancode; + + /* Leader mark */ + while (get_bits(data, 1) && !data->irdata_error) + /* Do nothing */; + + /* Leader space */ + if (get_bits(data, 4)) { + dev_dbg(dev, "NEC - Invalid leader space\n"); + return; + } + + /* Repeat? */ + if (get_bits(data, 1)) { + if (!data->keypressed) { + dev_dbg(dev, "NEC - Stray repeat message\n"); + return; + } + + dev_dbg(dev, "IR-NEC repeat s %u\n", + (unsigned int)data->last_scancode); + + wbcir_keydown(data, data->last_scancode, data->last_toggle); + return; + } + + /* Remaining leader space */ + if (get_bits(data, 3)) { + dev_dbg(dev, "NEC - Invalid leader space\n"); + return; + } + + address1 = bitrev8(get_bits(data, 8)); + address2 = bitrev8(get_bits(data, 8)); + command1 = bitrev8(get_bits(data, 8)); + command2 = bitrev8(get_bits(data, 8)); + + /* Sanity check */ + if (data->irdata_error) { + dev_dbg(dev, "NEC - Invalid message\n"); + return; + } + + /* Check command validity */ + if (command1 != ~command2) { + dev_dbg(dev, "NEC - Command bytes mismatch\n"); + return; + } + + /* Check for extended NEC protocol */ + address = address1; + if (address1 != ~address2) + address |= address2 << 8; + + scancode = address << 8 | command1; + + dev_dbg(dev, "IR-NEC ad %u cm %u s %u\n", + (unsigned int)address, + (unsigned int)command1, + (unsigned int)scancode); + + wbcir_keydown(data, scancode, !data->last_toggle); +} + + + +/***************************************************************************** + * + * INTERRUPT FUNCTIONS + * + *****************************************************************************/ + +static irqreturn_t +wbcir_irq_handler(int irqno, void *cookie) +{ + struct pnp_dev *device = cookie; + struct wbcir_data *data = pnp_get_drvdata(device); + struct device *dev = &device->dev; + u8 status; + unsigned long flags; + u8 irdata[8]; + int i; + unsigned int hw; + + spin_lock_irqsave(&wbcir_lock, flags); + + wbcir_select_bank(data, WBCIR_BANK_0); + + status = inb(data->sbase + WBCIR_REG_SP3_EIR); + + if (!(status & (WBCIR_IRQ_RX | WBCIR_IRQ_ERR))) { + spin_unlock_irqrestore(&wbcir_lock, flags); + return IRQ_NONE; + } + + if (status & WBCIR_IRQ_ERR) + data->irdata_error = 1; + + if (!(status & WBCIR_IRQ_RX)) + goto out; + + /* Since RXHDLEV is set, at least 8 bytes are in the FIFO */ + insb(data->sbase + WBCIR_REG_SP3_RXDATA, &irdata[0], 8); + + for (i = 0; i < sizeof(irdata); i++) { + hw = hweight8(irdata[i]); + if (hw > 4) + add_irdata_bit(data, 0); + else + add_irdata_bit(data, 1); + + if (hw == 8) + data->idle_count++; + else + data->idle_count = 0; + } + + if (data->idle_count > WBCIR_MAX_IDLE_BYTES) { + /* Set RXINACTIVE... */ + outb(WBCIR_RX_DISABLE, data->sbase + WBCIR_REG_SP3_ASCR); + + /* ...and drain the FIFO */ + while (inb(data->sbase + WBCIR_REG_SP3_LSR) & WBCIR_RX_AVAIL) + inb(data->sbase + WBCIR_REG_SP3_RXDATA); + + dev_dbg(dev, "IRDATA:\n"); + for (i = 0; i < data->irdata_count; i += BITS_PER_LONG) + dev_dbg(dev, "0x%08lX\n", data->irdata[i/BITS_PER_LONG]); + + switch (protocol) { + case IR_PROTOCOL_RC5: + wbcir_parse_rc5(dev, data); + break; + case IR_PROTOCOL_RC6: + wbcir_parse_rc6(dev, data); + break; + case IR_PROTOCOL_NEC: + wbcir_parse_nec(dev, data); + break; + } + + wbcir_reset_irdata(data); + data->idle_count = 0; + } + +out: + spin_unlock_irqrestore(&wbcir_lock, flags); + return IRQ_HANDLED; +} + + + +/***************************************************************************** + * + * SUSPEND/RESUME FUNCTIONS + * + *****************************************************************************/ + +static void +wbcir_shutdown(struct pnp_dev *device) +{ + struct device *dev = &device->dev; + struct wbcir_data *data = pnp_get_drvdata(device); + int do_wake = 1; + u8 match[11]; + u8 mask[11]; + u8 rc6_csl = 0; + int i; + + memset(match, 0, sizeof(match)); + memset(mask, 0, sizeof(mask)); + + if (wake_sc == INVALID_SCANCODE || !device_may_wakeup(dev)) { + do_wake = 0; + goto finish; + } + + switch (protocol) { + case IR_PROTOCOL_RC5: + if (wake_sc > 0xFFF) { + do_wake = 0; + dev_err(dev, "RC5 - Invalid wake scancode\n"); + break; + } + + /* Mask = 13 bits, ex toggle */ + mask[0] = 0xFF; + mask[1] = 0x17; + + match[0] = (wake_sc & 0x003F); /* 6 command bits */ + match[0] |= (wake_sc & 0x0180) >> 1; /* 2 address bits */ + match[1] = (wake_sc & 0x0E00) >> 9; /* 3 address bits */ + if (!(wake_sc & 0x0040)) /* 2nd start bit */ + match[1] |= 0x10; + + break; + + case IR_PROTOCOL_NEC: + if (wake_sc > 0xFFFFFF) { + do_wake = 0; + dev_err(dev, "NEC - Invalid wake scancode\n"); + break; + } + + mask[0] = mask[1] = mask[2] = mask[3] = 0xFF; + + match[1] = bitrev8((wake_sc & 0xFF)); + match[0] = ~match[1]; + + match[3] = bitrev8((wake_sc & 0xFF00) >> 8); + if (wake_sc > 0xFFFF) + match[2] = bitrev8((wake_sc & 0xFF0000) >> 16); + else + match[2] = ~match[3]; + + break; + + case IR_PROTOCOL_RC6: + + if (wake_rc6mode == 0) { + if (wake_sc > 0xFFFF) { + do_wake = 0; + dev_err(dev, "RC6 - Invalid wake scancode\n"); + break; + } + + /* Command */ + match[0] = wbcir_to_rc6cells(wake_sc >> 0); + mask[0] = 0xFF; + match[1] = wbcir_to_rc6cells(wake_sc >> 4); + mask[1] = 0xFF; + + /* Address */ + match[2] = wbcir_to_rc6cells(wake_sc >> 8); + mask[2] = 0xFF; + match[3] = wbcir_to_rc6cells(wake_sc >> 12); + mask[3] = 0xFF; + + /* Header */ + match[4] = 0x50; /* mode1 = mode0 = 0, ignore toggle */ + mask[4] = 0xF0; + match[5] = 0x09; /* start bit = 1, mode2 = 0 */ + mask[5] = 0x0F; + + rc6_csl = 44; + + } else if (wake_rc6mode == 6) { + i = 0; + + /* Command */ + match[i] = wbcir_to_rc6cells(wake_sc >> 0); + mask[i++] = 0xFF; + match[i] = wbcir_to_rc6cells(wake_sc >> 4); + mask[i++] = 0xFF; + + /* Address + Toggle */ + match[i] = wbcir_to_rc6cells(wake_sc >> 8); + mask[i++] = 0xFF; + match[i] = wbcir_to_rc6cells(wake_sc >> 12); + mask[i++] = 0x3F; + + /* Customer bits 7 - 0 */ + match[i] = wbcir_to_rc6cells(wake_sc >> 16); + mask[i++] = 0xFF; + match[i] = wbcir_to_rc6cells(wake_sc >> 20); + mask[i++] = 0xFF; + + if (wake_sc & 0x80000000) { + /* Customer range bit and bits 15 - 8 */ + match[i] = wbcir_to_rc6cells(wake_sc >> 24); + mask[i++] = 0xFF; + match[i] = wbcir_to_rc6cells(wake_sc >> 28); + mask[i++] = 0xFF; + rc6_csl = 76; + } else if (wake_sc <= 0x007FFFFF) { + rc6_csl = 60; + } else { + do_wake = 0; + dev_err(dev, "RC6 - Invalid wake scancode\n"); + break; + } + + /* Header */ + match[i] = 0x93; /* mode1 = mode0 = 1, submode = 0 */ + mask[i++] = 0xFF; + match[i] = 0x0A; /* start bit = 1, mode2 = 1 */ + mask[i++] = 0x0F; + + } else { + do_wake = 0; + dev_err(dev, "RC6 - Invalid wake mode\n"); + } + + break; + + default: + do_wake = 0; + break; + } + +finish: + if (do_wake) { + /* Set compare and compare mask */ + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_INDEX, + WBCIR_REGSEL_COMPARE | WBCIR_REG_ADDR0, + 0x3F); + outsb(data->wbase + WBCIR_REG_WCEIR_DATA, match, 11); + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_INDEX, + WBCIR_REGSEL_MASK | WBCIR_REG_ADDR0, + 0x3F); + outsb(data->wbase + WBCIR_REG_WCEIR_DATA, mask, 11); + + /* RC6 Compare String Len */ + outb(rc6_csl, data->wbase + WBCIR_REG_WCEIR_CSL); + + /* Clear status bits NEC_REP, BUFF, MSG_END, MATCH */ + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_STS, 0x17, 0x17); + + /* Clear BUFF_EN, Clear END_EN, Set MATCH_EN */ + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x01, 0x07); + + /* Set CEIR_EN */ + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x01, 0x01); + + } else { + /* Clear BUFF_EN, Clear END_EN, Clear MATCH_EN */ + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07); + + /* Clear CEIR_EN */ + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x00, 0x01); + } + + /* Disable interrupts */ + outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER); +} + +static int +wbcir_suspend(struct pnp_dev *device, pm_message_t state) +{ + wbcir_shutdown(device); + return 0; +} + +static int +wbcir_resume(struct pnp_dev *device) +{ + struct wbcir_data *data = pnp_get_drvdata(device); + + /* Clear BUFF_EN, Clear END_EN, Clear MATCH_EN */ + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07); + + /* Clear CEIR_EN */ + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x00, 0x01); + + /* Enable interrupts */ + wbcir_reset_irdata(data); + outb(WBCIR_IRQ_RX | WBCIR_IRQ_ERR, data->sbase + WBCIR_REG_SP3_IER); + + return 0; +} + + + +/***************************************************************************** + * + * SETUP/INIT FUNCTIONS + * + *****************************************************************************/ + +static void +wbcir_cfg_ceir(struct wbcir_data *data) +{ + u8 tmp; + + /* Set PROT_SEL, RX_INV, Clear CEIR_EN (needed for the led) */ + tmp = protocol << 4; + if (invert) + tmp |= 0x08; + outb(tmp, data->wbase + WBCIR_REG_WCEIR_CTL); + + /* Clear status bits NEC_REP, BUFF, MSG_END, MATCH */ + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_STS, 0x17, 0x17); + + /* Clear BUFF_EN, Clear END_EN, Clear MATCH_EN */ + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07); + + /* Set RC5 cell time to correspond to 36 kHz */ + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CFG1, 0x4A, 0x7F); + + /* Set IRTX_INV */ + if (invert) + outb(0x04, data->ebase + WBCIR_REG_ECEIR_CCTL); + else + outb(0x00, data->ebase + WBCIR_REG_ECEIR_CCTL); + + /* + * Clear IR LED, set SP3 clock to 24Mhz + * set SP3_IRRX_SW to binary 01, helpfully not documented + */ + outb(0x10, data->ebase + WBCIR_REG_ECEIR_CTS); +} + +static int __devinit +wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) +{ + struct device *dev = &device->dev; + struct wbcir_data *data; + int err; + + if (!(pnp_port_len(device, 0) == EHFUNC_IOMEM_LEN && + pnp_port_len(device, 1) == WAKEUP_IOMEM_LEN && + pnp_port_len(device, 2) == SP_IOMEM_LEN)) { + dev_err(dev, "Invalid resources\n"); + return -ENODEV; + } + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + err = -ENOMEM; + goto exit; + } + + pnp_set_drvdata(device, data); + + data->ebase = pnp_port_start(device, 0); + data->wbase = pnp_port_start(device, 1); + data->sbase = pnp_port_start(device, 2); + data->irq = pnp_irq(device, 0); + + if (data->wbase == 0 || data->ebase == 0 || + data->sbase == 0 || data->irq == 0) { + err = -ENODEV; + dev_err(dev, "Invalid resources\n"); + goto exit_free_data; + } + + dev_dbg(&device->dev, "Found device " + "(w: 0x%lX, e: 0x%lX, s: 0x%lX, i: %u)\n", + data->wbase, data->ebase, data->sbase, data->irq); + + if (!request_region(data->wbase, WAKEUP_IOMEM_LEN, DRVNAME)) { + dev_err(dev, "Region 0x%lx-0x%lx already in use!\n", + data->wbase, data->wbase + WAKEUP_IOMEM_LEN - 1); + err = -EBUSY; + goto exit_free_data; + } + + if (!request_region(data->ebase, EHFUNC_IOMEM_LEN, DRVNAME)) { + dev_err(dev, "Region 0x%lx-0x%lx already in use!\n", + data->ebase, data->ebase + EHFUNC_IOMEM_LEN - 1); + err = -EBUSY; + goto exit_release_wbase; + } + + if (!request_region(data->sbase, SP_IOMEM_LEN, DRVNAME)) { + dev_err(dev, "Region 0x%lx-0x%lx already in use!\n", + data->sbase, data->sbase + SP_IOMEM_LEN - 1); + err = -EBUSY; + goto exit_release_ebase; + } + + err = request_irq(data->irq, wbcir_irq_handler, + IRQF_DISABLED, DRVNAME, device); + if (err) { + dev_err(dev, "Failed to claim IRQ %u\n", data->irq); + err = -EBUSY; + goto exit_release_sbase; + } + + led_trigger_register_simple("cir-tx", &data->txtrigger); + if (!data->txtrigger) { + err = -ENOMEM; + goto exit_free_irq; + } + + led_trigger_register_simple("cir-rx", &data->rxtrigger); + if (!data->rxtrigger) { + err = -ENOMEM; + goto exit_unregister_txtrigger; + } + + data->led.name = "cir::activity"; + data->led.default_trigger = "cir-rx"; + data->led.brightness_set = wbcir_led_brightness_set; + data->led.brightness_get = wbcir_led_brightness_get; + err = led_classdev_register(&device->dev, &data->led); + if (err) + goto exit_unregister_rxtrigger; + + data->input_dev = input_allocate_device(); + if (!data->input_dev) { + err = -ENOMEM; + goto exit_unregister_led; + } + + data->input_dev->evbit[0] = BIT(EV_KEY); + data->input_dev->name = WBCIR_NAME; + data->input_dev->phys = "wbcir/cir0"; + data->input_dev->id.bustype = BUS_HOST; + data->input_dev->id.vendor = PCI_VENDOR_ID_WINBOND; + data->input_dev->id.product = WBCIR_ID_FAMILY; + data->input_dev->id.version = WBCIR_ID_CHIP; + data->input_dev->getkeycode = wbcir_getkeycode; + data->input_dev->setkeycode = wbcir_setkeycode; + input_set_capability(data->input_dev, EV_MSC, MSC_SCAN); + input_set_drvdata(data->input_dev, data); + + err = input_register_device(data->input_dev); + if (err) + goto exit_free_input; + + data->last_scancode = INVALID_SCANCODE; + INIT_LIST_HEAD(&data->keytable); + setup_timer(&data->timer_keyup, wbcir_keyup, (unsigned long)data); + + /* Load default keymaps */ + if (protocol == IR_PROTOCOL_RC6) { + int i; + for (i = 0; i < ARRAY_SIZE(rc6_def_keymap); i++) { + err = wbcir_setkeycode(data->input_dev, + (int)rc6_def_keymap[i].scancode, + (int)rc6_def_keymap[i].keycode); + if (err) + goto exit_unregister_keys; + } + } + + device_init_wakeup(&device->dev, 1); + + wbcir_cfg_ceir(data); + + /* Disable interrupts */ + wbcir_select_bank(data, WBCIR_BANK_0); + outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER); + + /* Enable extended mode */ + wbcir_select_bank(data, WBCIR_BANK_2); + outb(WBCIR_EXT_ENABLE, data->sbase + WBCIR_REG_SP3_EXCR1); + + /* + * Configure baud generator, IR data will be sampled at + * a bitrate of: (24Mhz * prescaler) / (divisor * 16). + * + * The ECIR registers include a flag to change the + * 24Mhz clock freq to 48Mhz. + * + * It's not documented in the specs, but fifo levels + * other than 16 seems to be unsupported. + */ + + /* prescaler 1.0, tx/rx fifo lvl 16 */ + outb(0x30, data->sbase + WBCIR_REG_SP3_EXCR2); + + /* Set baud divisor to generate one byte per bit/cell */ + switch (protocol) { + case IR_PROTOCOL_RC5: + outb(0xA7, data->sbase + WBCIR_REG_SP3_BGDL); + break; + case IR_PROTOCOL_RC6: + outb(0x53, data->sbase + WBCIR_REG_SP3_BGDL); + break; + case IR_PROTOCOL_NEC: + outb(0x69, data->sbase + WBCIR_REG_SP3_BGDL); + break; + } + outb(0x00, data->sbase + WBCIR_REG_SP3_BGDH); + + /* Set CEIR mode */ + wbcir_select_bank(data, WBCIR_BANK_0); + outb(0xC0, data->sbase + WBCIR_REG_SP3_MCR); + inb(data->sbase + WBCIR_REG_SP3_LSR); /* Clear LSR */ + inb(data->sbase + WBCIR_REG_SP3_MSR); /* Clear MSR */ + + /* Disable RX demod, run-length encoding/decoding, set freq span */ + wbcir_select_bank(data, WBCIR_BANK_7); + outb(0x10, data->sbase + WBCIR_REG_SP3_RCCFG); + + /* Disable timer */ + wbcir_select_bank(data, WBCIR_BANK_4); + outb(0x00, data->sbase + WBCIR_REG_SP3_IRCR1); + + /* Enable MSR interrupt, Clear AUX_IRX */ + wbcir_select_bank(data, WBCIR_BANK_5); + outb(0x00, data->sbase + WBCIR_REG_SP3_IRCR2); + + /* Disable CRC */ + wbcir_select_bank(data, WBCIR_BANK_6); + outb(0x20, data->sbase + WBCIR_REG_SP3_IRCR3); + + /* Set RX/TX (de)modulation freq, not really used */ + wbcir_select_bank(data, WBCIR_BANK_7); + outb(0xF2, data->sbase + WBCIR_REG_SP3_IRRXDC); + outb(0x69, data->sbase + WBCIR_REG_SP3_IRTXMC); + + /* Set invert and pin direction */ + if (invert) + outb(0x10, data->sbase + WBCIR_REG_SP3_IRCFG4); + else + outb(0x00, data->sbase + WBCIR_REG_SP3_IRCFG4); + + /* Set FIFO thresholds (RX = 8, TX = 3), reset RX/TX */ + wbcir_select_bank(data, WBCIR_BANK_0); + outb(0x97, data->sbase + WBCIR_REG_SP3_FCR); + + /* Clear AUX status bits */ + outb(0xE0, data->sbase + WBCIR_REG_SP3_ASCR); + + /* Enable interrupts */ + outb(WBCIR_IRQ_RX | WBCIR_IRQ_ERR, data->sbase + WBCIR_REG_SP3_IER); + + return 0; + +exit_unregister_keys: + if (!list_empty(&data->keytable)) { + struct wbcir_keyentry *key; + struct wbcir_keyentry *keytmp; + + list_for_each_entry_safe(key, keytmp, &data->keytable, list) { + list_del(&key->list); + kfree(key); + } + } + input_unregister_device(data->input_dev); + /* Can't call input_free_device on an unregistered device */ + data->input_dev = NULL; +exit_free_input: + input_free_device(data->input_dev); +exit_unregister_led: + led_classdev_unregister(&data->led); +exit_unregister_rxtrigger: + led_trigger_unregister_simple(data->rxtrigger); +exit_unregister_txtrigger: + led_trigger_unregister_simple(data->txtrigger); +exit_free_irq: + free_irq(data->irq, device); +exit_release_sbase: + release_region(data->sbase, SP_IOMEM_LEN); +exit_release_ebase: + release_region(data->ebase, EHFUNC_IOMEM_LEN); +exit_release_wbase: + release_region(data->wbase, WAKEUP_IOMEM_LEN); +exit_free_data: + kfree(data); + pnp_set_drvdata(device, NULL); +exit: + return err; +} + +static void __devexit +wbcir_remove(struct pnp_dev *device) +{ + struct wbcir_data *data = pnp_get_drvdata(device); + struct wbcir_keyentry *key; + struct wbcir_keyentry *keytmp; + + /* Disable interrupts */ + wbcir_select_bank(data, WBCIR_BANK_0); + outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER); + + del_timer_sync(&data->timer_keyup); + + free_irq(data->irq, device); + + /* Clear status bits NEC_REP, BUFF, MSG_END, MATCH */ + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_STS, 0x17, 0x17); + + /* Clear CEIR_EN */ + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x00, 0x01); + + /* Clear BUFF_EN, END_EN, MATCH_EN */ + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07); + + /* This will generate a keyup event if necessary */ + input_unregister_device(data->input_dev); + + led_trigger_unregister_simple(data->rxtrigger); + led_trigger_unregister_simple(data->txtrigger); + led_classdev_unregister(&data->led); + + /* This is ok since &data->led isn't actually used */ + wbcir_led_brightness_set(&data->led, LED_OFF); + + release_region(data->wbase, WAKEUP_IOMEM_LEN); + release_region(data->ebase, EHFUNC_IOMEM_LEN); + release_region(data->sbase, SP_IOMEM_LEN); + + list_for_each_entry_safe(key, keytmp, &data->keytable, list) { + list_del(&key->list); + kfree(key); + } + + kfree(data); + + pnp_set_drvdata(device, NULL); +} + +static const struct pnp_device_id wbcir_ids[] = { + { "WEC1022", 0 }, + { "", 0 } +}; +MODULE_DEVICE_TABLE(pnp, wbcir_ids); + +static struct pnp_driver wbcir_driver = { + .name = WBCIR_NAME, + .id_table = wbcir_ids, + .probe = wbcir_probe, + .remove = __devexit_p(wbcir_remove), + .suspend = wbcir_suspend, + .resume = wbcir_resume, + .shutdown = wbcir_shutdown +}; + +static int __init +wbcir_init(void) +{ + int ret; + + switch (protocol) { + case IR_PROTOCOL_RC5: + case IR_PROTOCOL_NEC: + case IR_PROTOCOL_RC6: + break; + default: + printk(KERN_ERR DRVNAME ": Invalid protocol argument\n"); + return -EINVAL; + } + + ret = pnp_register_driver(&wbcir_driver); + if (ret) + printk(KERN_ERR DRVNAME ": Unable to register driver\n"); + + return ret; +} + +static void __exit +wbcir_exit(void) +{ + pnp_unregister_driver(&wbcir_driver); +} + +MODULE_AUTHOR("David Härdeman "); +MODULE_DESCRIPTION("Winbond SuperI/O Consumer IR Driver"); +MODULE_LICENSE("GPL"); + +module_init(wbcir_init); +module_exit(wbcir_exit); + + -- cgit v1.2.1 From e0626e3844e8f430fc1a4417f523a00797df7ca6 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Tue, 22 Sep 2009 16:46:08 -0700 Subject: spi: prefix modalias with "spi:" This makes it consistent with other buses (platform, i2c, vio, ...). I'm not sure why we use the prefixes, but there must be a reason. This was easy enough to do it, and I did it. Signed-off-by: Anton Vorontsov Cc: David Brownell Cc: David Woodhouse Cc: Grant Likely Cc: Jean Delvare Cc: Ben Dooks Cc: Benjamin Herrenschmidt Cc: Dmitry Torokhov Cc: Samuel Ortiz Cc: "John W. Linville" Acked-by: Mike Frysinger Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/input/touchscreen/ad7877.c | 1 + drivers/input/touchscreen/ad7879.c | 1 + drivers/input/touchscreen/ads7846.c | 1 + 3 files changed, 3 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/touchscreen/ad7877.c b/drivers/input/touchscreen/ad7877.c index ecaeb7e8e75e..eb83939c705e 100644 --- a/drivers/input/touchscreen/ad7877.c +++ b/drivers/input/touchscreen/ad7877.c @@ -842,3 +842,4 @@ module_exit(ad7877_exit); MODULE_AUTHOR("Michael Hennerich "); MODULE_DESCRIPTION("AD7877 touchscreen Driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:ad7877"); diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c index 5d8a70398807..19b4db7e974d 100644 --- a/drivers/input/touchscreen/ad7879.c +++ b/drivers/input/touchscreen/ad7879.c @@ -779,3 +779,4 @@ module_exit(ad7879_exit); MODULE_AUTHOR("Michael Hennerich "); MODULE_DESCRIPTION("AD7879(-1) touchscreen Driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:ad7879"); diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index ba9d38c3f412..09c810999b92 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -1256,3 +1256,4 @@ module_exit(ads7846_exit); MODULE_DESCRIPTION("ADS7846 TouchScreen Driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:ads7846"); -- cgit v1.2.1 From d2b5ec3aa0784335f031239e71fb50924cac9e0d Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Wed, 23 Sep 2009 15:56:02 -0700 Subject: input: fix build failures caused by Kconfig Winbond WPCD376I Consumer IR hardware driver Kconfig entry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix these warnings: drivers/built-in.o: In function `apanel_remove': apanel.c:(.text+0x56e852): undefined reference to `led_classdev_unregister' drivers/built-in.o: In function `apanel_probe': apanel.c:(.text+0x56eae3): undefined reference to `led_classdev_register' drivers/built-in.o: In function `acpi_fujitsu_hotkey_add': fujitsu-laptop.c:(.text+0x5d7647): undefined reference to `led_classdev_register' fujitsu-laptop.c:(.text+0x5d76b5): undefined reference to `led_classdev_register' drivers/built-in.o: In function `wbcir_probe': winbond-cir.c:(.devinit.text+0x5f375): undefined reference to `led_classdev_register' winbond-cir.c:(.devinit.text+0x5f663): undefined reference to `led_classdev_unregister' drivers/built-in.o: In function `wbcir_remove': winbond-cir.c:(.devexit.text+0x7f23): undefined reference to `led_classdev_unregister' drivers/built-in.o: In function `fujitsu_cleanup': fujitsu-laptop.c:(.exit.text+0xbe37): undefined reference to `led_classdev_unregister' fujitsu-laptop.c:(.exit.text+0xbe53): undefined reference to `led_classdev_unregister' It happens because the new INPUT_WINBOND_CIR driver relies on new-leds infrastructure - but does not select it in drivers/input/misc/Kconfig. But it selects LEDS_CLASS, which confuses a number of other drivers into thinking that all the leds infrastructure is in place. Fix this by selecting NEW_LEDS as well, like similar drivers do. Eventually, this whole leds infrastructure complexity should be cleaned up, it's been going on for years. Signed-off-by: Ingo Molnar Cc: Jesse Barnes Cc: Dmitry Torokhov Cc: David Härdeman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/input/misc/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/input') diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 76d6751f89a7..02f4f8f1db6f 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -225,6 +225,7 @@ config INPUT_SGI_BTNS config INPUT_WINBOND_CIR tristate "Winbond IR remote control" depends on X86 && PNP + select NEW_LEDS select LEDS_CLASS select BITREVERSE help -- cgit v1.2.1 From 1c4115e595dec42aa0e81ba47ef46e35b34ed428 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Thu, 1 Oct 2009 15:43:55 -0700 Subject: drivers/input/input.c: fix CONFIG_PM=n warning drivers/input/input.c:1277: warning: 'input_dev_reset' defined but not used Acked-by: Dmitry Torokhov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/input/input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/input') diff --git a/drivers/input/input.c b/drivers/input/input.c index e828aab7dace..16ec33f27c5d 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -1273,6 +1273,7 @@ static int input_dev_uevent(struct device *device, struct kobj_uevent_env *env) } \ } while (0) +#ifdef CONFIG_PM static void input_dev_reset(struct input_dev *dev, bool activate) { if (!dev->event) @@ -1287,7 +1288,6 @@ static void input_dev_reset(struct input_dev *dev, bool activate) } } -#ifdef CONFIG_PM static int input_dev_suspend(struct device *dev) { struct input_dev *input_dev = to_input_dev(dev); -- cgit v1.2.1 From a99bbaf5ee6bad1aca0c88ea65ec6e5373e86184 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Sun, 4 Oct 2009 16:11:37 +0400 Subject: headers: remove sched.h from poll.h Signed-off-by: Alexey Dobriyan Signed-off-by: Linus Torvalds --- drivers/input/evdev.c | 1 + drivers/input/input.c | 1 + drivers/input/joydev.c | 1 + drivers/input/misc/uinput.c | 1 + drivers/input/mousedev.c | 1 + 5 files changed, 5 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 1148140d08a1..dee6706038aa 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -13,6 +13,7 @@ #define EVDEV_BUFFER_SIZE 64 #include +#include #include #include #include diff --git a/drivers/input/input.c b/drivers/input/input.c index 16ec33f27c5d..c6f88ebb40c7 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c index 901b2525993e..b1bd6dd32286 100644 --- a/drivers/input/joydev.c +++ b/drivers/input/joydev.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index c5a49aba418f..d3f57245420a 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -30,6 +30,7 @@ * - first public version */ #include +#include #include #include #include diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c index 966b8868f792..a13d80f7da17 100644 --- a/drivers/input/mousedev.c +++ b/drivers/input/mousedev.c @@ -13,6 +13,7 @@ #define MOUSEDEV_MINORS 32 #define MOUSEDEV_MIX 31 +#include #include #include #include -- cgit v1.2.1 From d43c36dc6b357fa1806800f18aa30123c747a6d1 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Wed, 7 Oct 2009 17:09:06 +0400 Subject: headers: remove sched.h from interrupt.h After m68k's task_thread_info() doesn't refer to current, it's possible to remove sched.h from interrupt.h and not break m68k! Many thanks to Heiko Carstens for allowing this. Signed-off-by: Alexey Dobriyan --- drivers/input/keyboard/hilkbd.c | 1 + drivers/input/keyboard/sunkbd.c | 1 + drivers/input/serio/libps2.c | 1 + drivers/input/serio/serio_raw.c | 1 + drivers/input/serio/serport.c | 1 + 5 files changed, 5 insertions(+) (limited to 'drivers/input') diff --git a/drivers/input/keyboard/hilkbd.c b/drivers/input/keyboard/hilkbd.c index e9d639ec283d..5f72440b50c8 100644 --- a/drivers/input/keyboard/hilkbd.c +++ b/drivers/input/keyboard/hilkbd.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #ifdef CONFIG_HP300 diff --git a/drivers/input/keyboard/sunkbd.c b/drivers/input/keyboard/sunkbd.c index 472b56639cdb..a99a04b03ee4 100644 --- a/drivers/input/keyboard/sunkbd.c +++ b/drivers/input/keyboard/sunkbd.c @@ -27,6 +27,7 @@ */ #include +#include #include #include #include diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c index 769ba65a585a..f3876acc3e83 100644 --- a/drivers/input/serio/libps2.c +++ b/drivers/input/serio/libps2.c @@ -13,6 +13,7 @@ #include #include +#include #include #include #include diff --git a/drivers/input/serio/serio_raw.c b/drivers/input/serio/serio_raw.c index b03009bb7468..27fdaaffbb40 100644 --- a/drivers/input/serio/serio_raw.c +++ b/drivers/input/serio/serio_raw.c @@ -9,6 +9,7 @@ * the Free Software Foundation. */ +#include #include #include #include diff --git a/drivers/input/serio/serport.c b/drivers/input/serio/serport.c index b9694b6445d0..6d345112bcb7 100644 --- a/drivers/input/serio/serport.c +++ b/drivers/input/serio/serport.c @@ -15,6 +15,7 @@ #include #include +#include #include #include #include -- cgit v1.2.1