diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2013-05-01 19:47:44 +0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2013-05-01 19:47:44 +0400 |
commit | bf61c8840efe60fd8f91446860b63338fb424158 (patch) | |
tree | 7a71832407a4f0d6346db773343f4c3ae2257b19 /drivers/input | |
parent | 5846115b30f3a881e542c8bfde59a699c1c13740 (diff) | |
parent | 0c6a61657da78098472fd0eb71cc01f2387fa1bb (diff) | |
download | linux-bf61c8840efe60fd8f91446860b63338fb424158.tar.xz |
Merge branch 'next' into for-linus
Prepare first set of updates for 3.10 merge window.
Diffstat (limited to 'drivers/input')
53 files changed, 3472 insertions, 1003 deletions
diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c index 53eaf89f2db5..d398f1321f14 100644 --- a/drivers/input/input-mt.c +++ b/drivers/input/input-mt.c @@ -79,6 +79,8 @@ int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots, } if (flags & INPUT_MT_DIRECT) __set_bit(INPUT_PROP_DIRECT, dev->propbit); + if (flags & INPUT_MT_SEMI_MT) + __set_bit(INPUT_PROP_SEMI_MT, dev->propbit); if (flags & INPUT_MT_TRACK) { unsigned int n2 = num_slots * num_slots; mt->red = kcalloc(n2, sizeof(*mt->red), GFP_KERNEL); @@ -246,20 +248,24 @@ void input_mt_sync_frame(struct input_dev *dev) { struct input_mt *mt = dev->mt; struct input_mt_slot *s; + bool use_count = false; if (!mt) return; if (mt->flags & INPUT_MT_DROP_UNUSED) { for (s = mt->slots; s != mt->slots + mt->num_slots; s++) { - if (s->frame == mt->frame) + if (input_mt_is_used(mt, s)) continue; input_mt_slot(dev, s - mt->slots); input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1); } } - input_mt_report_pointer_emulation(dev, (mt->flags & INPUT_MT_POINTER)); + if ((mt->flags & INPUT_MT_POINTER) && !(mt->flags & INPUT_MT_SEMI_MT)) + use_count = true; + + input_mt_report_pointer_emulation(dev, use_count); mt->frame++; } diff --git a/drivers/input/joystick/analog.c b/drivers/input/joystick/analog.c index 358cd7ee905b..7cd74e29cbc8 100644 --- a/drivers/input/joystick/analog.c +++ b/drivers/input/joystick/analog.c @@ -162,7 +162,7 @@ static unsigned int get_time_pit(void) #define GET_TIME(x) do { x = get_cycles(); } while (0) #define DELTA(x,y) ((y)-(x)) #define TIME_NAME "PCC" -#elif defined(CONFIG_MN10300) +#elif defined(CONFIG_MN10300) || defined(CONFIG_TILE) #define GET_TIME(x) do { x = get_cycles(); } while (0) #define DELTA(x, y) ((x) - (y)) #define TIME_NAME "TSC" diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 992137cf3a64..ac0500667000 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -409,7 +409,7 @@ config KEYBOARD_NEWTON config KEYBOARD_NOMADIK tristate "ST-Ericsson Nomadik SKE keyboard" - depends on PLAT_NOMADIK + depends on (ARCH_NOMADIK || ARCH_U8500) select INPUT_MATRIXKMAP help Say Y here if you want to use a keypad provided on the SKE controller @@ -554,6 +554,7 @@ config KEYBOARD_OMAP config KEYBOARD_OMAP4 tristate "TI OMAP4+ keypad support" + depends on ARCH_OMAP2PLUS select INPUT_MATRIXKMAP help Say Y here if you want to use the OMAP4+ keypad. diff --git a/drivers/input/keyboard/amikbd.c b/drivers/input/keyboard/amikbd.c index 79172af164f2..ba0b36f7daea 100644 --- a/drivers/input/keyboard/amikbd.c +++ b/drivers/input/keyboard/amikbd.c @@ -260,18 +260,6 @@ static struct platform_driver amikbd_driver = { }, }; -static int __init amikbd_init(void) -{ - return platform_driver_probe(&amikbd_driver, amikbd_probe); -} - -module_init(amikbd_init); - -static void __exit amikbd_exit(void) -{ - platform_driver_unregister(&amikbd_driver); -} - -module_exit(amikbd_exit); +module_platform_driver_probe(amikbd_driver, amikbd_probe); MODULE_ALIAS("platform:amiga-keyboard"); diff --git a/drivers/input/keyboard/davinci_keyscan.c b/drivers/input/keyboard/davinci_keyscan.c index 4e4e453ea15e..829753702b62 100644 --- a/drivers/input/keyboard/davinci_keyscan.c +++ b/drivers/input/keyboard/davinci_keyscan.c @@ -329,17 +329,7 @@ static struct platform_driver davinci_ks_driver = { .remove = davinci_ks_remove, }; -static int __init davinci_ks_init(void) -{ - return platform_driver_probe(&davinci_ks_driver, davinci_ks_probe); -} -module_init(davinci_ks_init); - -static void __exit davinci_ks_exit(void) -{ - platform_driver_unregister(&davinci_ks_driver); -} -module_exit(davinci_ks_exit); +module_platform_driver_probe(davinci_ks_driver, davinci_ks_probe); MODULE_AUTHOR("Miguel Aguilar"); MODULE_DESCRIPTION("Texas Instruments DaVinci Key Scan Driver"); diff --git a/drivers/input/keyboard/imx_keypad.c b/drivers/input/keyboard/imx_keypad.c index 98f9113251d2..03c8cc5cb6c1 100644 --- a/drivers/input/keyboard/imx_keypad.c +++ b/drivers/input/keyboard/imx_keypad.c @@ -448,24 +448,17 @@ static int imx_keypad_probe(struct platform_device *pdev) return -EINVAL; } - res = request_mem_region(res->start, resource_size(res), pdev->name); - if (res == NULL) { - dev_err(&pdev->dev, "failed to request I/O memory\n"); - return -EBUSY; - } - - input_dev = input_allocate_device(); + input_dev = devm_input_allocate_device(&pdev->dev); if (!input_dev) { dev_err(&pdev->dev, "failed to allocate the input device\n"); - error = -ENOMEM; - goto failed_rel_mem; + return -ENOMEM; } - keypad = kzalloc(sizeof(struct imx_keypad), GFP_KERNEL); + keypad = devm_kzalloc(&pdev->dev, sizeof(struct imx_keypad), + GFP_KERNEL); if (!keypad) { dev_err(&pdev->dev, "not enough memory for driver data\n"); - error = -ENOMEM; - goto failed_free_input; + return -ENOMEM; } keypad->input_dev = input_dev; @@ -475,18 +468,14 @@ static int imx_keypad_probe(struct platform_device *pdev) setup_timer(&keypad->check_matrix_timer, imx_keypad_check_for_events, (unsigned long) keypad); - keypad->mmio_base = ioremap(res->start, resource_size(res)); - if (keypad->mmio_base == NULL) { - dev_err(&pdev->dev, "failed to remap I/O memory\n"); - error = -ENOMEM; - goto failed_free_priv; - } + keypad->mmio_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(keypad->mmio_base)) + return PTR_ERR(keypad->mmio_base); - keypad->clk = clk_get(&pdev->dev, NULL); + keypad->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(keypad->clk)) { dev_err(&pdev->dev, "failed to get keypad clock\n"); - error = PTR_ERR(keypad->clk); - goto failed_unmap; + return PTR_ERR(keypad->clk); } /* Init the Input device */ @@ -502,7 +491,7 @@ static int imx_keypad_probe(struct platform_device *pdev) keypad->keycodes, input_dev); if (error) { dev_err(&pdev->dev, "failed to build keymap\n"); - goto failed_clock_put; + return error; } /* Search for rows and cols enabled */ @@ -527,61 +516,24 @@ static int imx_keypad_probe(struct platform_device *pdev) imx_keypad_inhibit(keypad); clk_disable_unprepare(keypad->clk); - error = request_irq(irq, imx_keypad_irq_handler, 0, + error = devm_request_irq(&pdev->dev, irq, imx_keypad_irq_handler, 0, pdev->name, keypad); if (error) { dev_err(&pdev->dev, "failed to request IRQ\n"); - goto failed_clock_put; + return error; } /* Register the input device */ error = input_register_device(input_dev); if (error) { dev_err(&pdev->dev, "failed to register input device\n"); - goto failed_free_irq; + return error; } platform_set_drvdata(pdev, keypad); device_init_wakeup(&pdev->dev, 1); return 0; - -failed_free_irq: - free_irq(irq, pdev); -failed_clock_put: - clk_put(keypad->clk); -failed_unmap: - iounmap(keypad->mmio_base); -failed_free_priv: - kfree(keypad); -failed_free_input: - input_free_device(input_dev); -failed_rel_mem: - release_mem_region(res->start, resource_size(res)); - return error; -} - -static int imx_keypad_remove(struct platform_device *pdev) -{ - struct imx_keypad *keypad = platform_get_drvdata(pdev); - struct resource *res; - - dev_dbg(&pdev->dev, ">%s\n", __func__); - - platform_set_drvdata(pdev, NULL); - - input_unregister_device(keypad->input_dev); - - free_irq(keypad->irq, keypad); - clk_put(keypad->clk); - - iounmap(keypad->mmio_base); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, resource_size(res)); - - kfree(keypad); - - return 0; } #ifdef CONFIG_PM_SLEEP @@ -640,7 +592,6 @@ static struct platform_driver imx_keypad_driver = { .of_match_table = of_match_ptr(imx_keypad_of_match), }, .probe = imx_keypad_probe, - .remove = imx_keypad_remove, }; module_platform_driver(imx_keypad_driver); diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c index f4ff0dda7597..71d77192ac1e 100644 --- a/drivers/input/keyboard/matrix_keypad.c +++ b/drivers/input/keyboard/matrix_keypad.c @@ -403,7 +403,7 @@ matrix_keypad_parse_dt(struct device *dev) struct matrix_keypad_platform_data *pdata; struct device_node *np = dev->of_node; unsigned int *gpios; - int i; + int i, nrow, ncol; if (!np) { dev_err(dev, "device lacks DT data\n"); @@ -416,9 +416,9 @@ matrix_keypad_parse_dt(struct device *dev) return ERR_PTR(-ENOMEM); } - pdata->num_row_gpios = of_gpio_named_count(np, "row-gpios"); - pdata->num_col_gpios = of_gpio_named_count(np, "col-gpios"); - if (!pdata->num_row_gpios || !pdata->num_col_gpios) { + pdata->num_row_gpios = nrow = of_gpio_named_count(np, "row-gpios"); + pdata->num_col_gpios = ncol = of_gpio_named_count(np, "col-gpios"); + if (nrow <= 0 || ncol <= 0) { dev_err(dev, "number of keypad rows/columns not specified\n"); return ERR_PTR(-EINVAL); } diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index 0e6a8151fee3..c7d505cce72f 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -430,17 +430,7 @@ static struct platform_driver ske_keypad_driver = { .remove = ske_keypad_remove, }; -static int __init ske_keypad_init(void) -{ - return platform_driver_probe(&ske_keypad_driver, ske_keypad_probe); -} -module_init(ske_keypad_init); - -static void __exit ske_keypad_exit(void) -{ - platform_driver_unregister(&ske_keypad_driver); -} -module_exit(ske_keypad_exit); +module_platform_driver_probe(ske_keypad_driver, ske_keypad_probe); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Naveen Kumar <naveen.gaddipati@stericsson.com> / Sundar Iyer <sundar.iyer@stericsson.com>"); diff --git a/drivers/input/keyboard/spear-keyboard.c b/drivers/input/keyboard/spear-keyboard.c index 695d237417d6..cb1e8f614631 100644 --- a/drivers/input/keyboard/spear-keyboard.c +++ b/drivers/input/keyboard/spear-keyboard.c @@ -228,11 +228,9 @@ static int spear_kbd_probe(struct platform_device *pdev) kbd->suspended_rate = pdata->suspended_rate; } - kbd->io_base = devm_request_and_ioremap(&pdev->dev, res); - if (!kbd->io_base) { - dev_err(&pdev->dev, "request-ioremap failed for kbd_region\n"); - return -ENOMEM; - } + kbd->io_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(kbd->io_base)) + return PTR_ERR(kbd->io_base); kbd->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(kbd->clk)) diff --git a/drivers/input/keyboard/tca8418_keypad.c b/drivers/input/keyboard/tca8418_keypad.c index 50e9c5e195e1..a34cc6714e5b 100644 --- a/drivers/input/keyboard/tca8418_keypad.c +++ b/drivers/input/keyboard/tca8418_keypad.c @@ -384,7 +384,7 @@ static const struct i2c_device_id tca8418_id[] = { MODULE_DEVICE_TABLE(i2c, tca8418_id); #ifdef CONFIG_OF -static const struct of_device_id tca8418_dt_ids[] __devinitconst = { +static const struct of_device_id tca8418_dt_ids[] = { { .compatible = "ti,tca8418", }, { } }; diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c index d89e7d392d1e..b46142f78ef2 100644 --- a/drivers/input/keyboard/tegra-kbc.c +++ b/drivers/input/keyboard/tegra-kbc.c @@ -27,17 +27,19 @@ #include <linux/io.h> #include <linux/interrupt.h> #include <linux/of.h> +#include <linux/of_device.h> #include <linux/clk.h> #include <linux/slab.h> #include <linux/input/matrix_keypad.h> -#include <mach/clk.h> +#include <linux/clk/tegra.h> +#include <linux/err.h> -#define KBC_MAX_GPIO 24 #define KBC_MAX_KPENT 8 -#define KBC_MAX_ROW 16 -#define KBC_MAX_COL 8 -#define KBC_MAX_KEY (KBC_MAX_ROW * KBC_MAX_COL) +/* Maximum row/column supported by Tegra KBC yet is 16x8 */ +#define KBC_MAX_GPIO 24 +/* Maximum keys supported by Tegra KBC yet is 16 x 8*/ +#define KBC_MAX_KEY (16 * 8) #define KBC_MAX_DEBOUNCE_CNT 0x3ffu @@ -80,6 +82,12 @@ enum tegra_pin_type { PIN_CFG_ROW, }; +/* Tegra KBC hw support */ +struct tegra_kbc_hw_support { + int max_rows; + int max_columns; +}; + struct tegra_kbc_pin_cfg { enum tegra_pin_type type; unsigned char num; @@ -108,6 +116,9 @@ struct tegra_kbc { u32 wakeup_key; struct timer_list timer; struct clk *clk; + const struct tegra_kbc_hw_support *hw_support; + int max_keys; + int num_rows_and_columns; }; static void tegra_kbc_report_released_keys(struct input_dev *input, @@ -204,11 +215,11 @@ static void tegra_kbc_report_keys(struct tegra_kbc *kbc) /* * If the platform uses Fn keymaps, translate keys on a Fn keypress. - * Function keycodes are KBC_MAX_KEY apart from the plain keycodes. + * Function keycodes are max_keys apart from the plain keycodes. */ if (fn_keypress) { for (i = 0; i < num_down; i++) { - scancodes[i] += KBC_MAX_KEY; + scancodes[i] += kbc->max_keys; keycodes[i] = kbc->keycode[scancodes[i]]; } } @@ -315,7 +326,7 @@ static void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter) /* Either mask all keys or none. */ rst_val = (filter && !kbc->wakeup) ? ~0 : 0; - for (i = 0; i < KBC_MAX_ROW; i++) + for (i = 0; i < kbc->hw_support->max_rows; i++) writel(rst_val, kbc->mmio + KBC_ROW0_MASK_0 + i * 4); } @@ -452,7 +463,7 @@ static bool tegra_kbc_check_pin_cfg(const struct tegra_kbc *kbc, switch (pin_cfg->type) { case PIN_CFG_ROW: - if (pin_cfg->num >= KBC_MAX_ROW) { + if (pin_cfg->num >= kbc->hw_support->max_rows) { dev_err(kbc->dev, "pin_cfg[%d]: invalid row number %d\n", i, pin_cfg->num); @@ -462,7 +473,7 @@ static bool tegra_kbc_check_pin_cfg(const struct tegra_kbc *kbc, break; case PIN_CFG_COL: - if (pin_cfg->num >= KBC_MAX_COL) { + if (pin_cfg->num >= kbc->hw_support->max_columns) { dev_err(kbc->dev, "pin_cfg[%d]: invalid column number %d\n", i, pin_cfg->num); @@ -520,6 +531,18 @@ static int tegra_kbc_parse_dt(struct tegra_kbc *kbc) } num_cols = proplen / sizeof(u32); + if (num_rows > kbc->hw_support->max_rows) { + dev_err(kbc->dev, + "Number of rows is more than supported by hardware\n"); + return -EINVAL; + } + + if (num_cols > kbc->hw_support->max_columns) { + dev_err(kbc->dev, + "Number of cols is more than supported by hardware\n"); + return -EINVAL; + } + if (!of_get_property(np, "linux,keymap", &proplen)) { dev_err(kbc->dev, "property linux,keymap not found\n"); return -ENOENT; @@ -532,7 +555,7 @@ static int tegra_kbc_parse_dt(struct tegra_kbc *kbc) } /* Set all pins as non-configured */ - for (i = 0; i < KBC_MAX_GPIO; i++) + for (i = 0; i < kbc->num_rows_and_columns; i++) kbc->pin_cfg[i].type = PIN_CFG_IGNORE; ret = of_property_read_u32_array(np, "nvidia,kbc-row-pins", @@ -562,6 +585,24 @@ static int tegra_kbc_parse_dt(struct tegra_kbc *kbc) return 0; } +static const struct tegra_kbc_hw_support tegra20_kbc_hw_support = { + .max_rows = 16, + .max_columns = 8, +}; + +static const struct tegra_kbc_hw_support tegra11_kbc_hw_support = { + .max_rows = 11, + .max_columns = 8, +}; + +static const struct of_device_id tegra_kbc_of_match[] = { + { .compatible = "nvidia,tegra114-kbc", .data = &tegra11_kbc_hw_support}, + { .compatible = "nvidia,tegra30-kbc", .data = &tegra20_kbc_hw_support}, + { .compatible = "nvidia,tegra20-kbc", .data = &tegra20_kbc_hw_support}, + { }, +}; +MODULE_DEVICE_TABLE(of, tegra_kbc_of_match); + static int tegra_kbc_probe(struct platform_device *pdev) { struct tegra_kbc *kbc; @@ -570,7 +611,10 @@ static int tegra_kbc_probe(struct platform_device *pdev) int num_rows = 0; unsigned int debounce_cnt; unsigned int scan_time_rows; - unsigned int keymap_rows = KBC_MAX_KEY; + unsigned int keymap_rows; + const struct of_device_id *match; + + match = of_match_device(of_match_ptr(tegra_kbc_of_match), &pdev->dev); kbc = devm_kzalloc(&pdev->dev, sizeof(*kbc), GFP_KERNEL); if (!kbc) { @@ -579,6 +623,12 @@ static int tegra_kbc_probe(struct platform_device *pdev) } kbc->dev = &pdev->dev; + kbc->hw_support = match->data; + kbc->max_keys = kbc->hw_support->max_rows * + kbc->hw_support->max_columns; + kbc->num_rows_and_columns = kbc->hw_support->max_rows + + kbc->hw_support->max_columns; + keymap_rows = kbc->max_keys; spin_lock_init(&kbc->lock); err = tegra_kbc_parse_dt(kbc); @@ -608,11 +658,9 @@ static int tegra_kbc_probe(struct platform_device *pdev) setup_timer(&kbc->timer, tegra_kbc_keypress_timer, (unsigned long)kbc); - kbc->mmio = devm_request_and_ioremap(&pdev->dev, res); - if (!kbc->mmio) { - dev_err(&pdev->dev, "Cannot request memregion/iomap address\n"); - return -EBUSY; - } + kbc->mmio = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(kbc->mmio)) + return PTR_ERR(kbc->mmio); kbc->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(kbc->clk)) { @@ -641,7 +689,8 @@ static int tegra_kbc_probe(struct platform_device *pdev) keymap_rows *= 2; err = matrix_keypad_build_keymap(kbc->keymap_data, NULL, - keymap_rows, KBC_MAX_COL, + keymap_rows, + kbc->hw_support->max_columns, kbc->keycode, kbc->idev); if (err) { dev_err(&pdev->dev, "failed to setup keymap\n"); @@ -767,12 +816,6 @@ static int tegra_kbc_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(tegra_kbc_pm_ops, tegra_kbc_suspend, tegra_kbc_resume); -static const struct of_device_id tegra_kbc_of_match[] = { - { .compatible = "nvidia,tegra20-kbc", }, - { }, -}; -MODULE_DEVICE_TABLE(of, tegra_kbc_of_match); - static struct platform_driver tegra_kbc_driver = { .probe = tegra_kbc_probe, .driver = { diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 2a1647ef5610..af80928a46b4 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -72,6 +72,16 @@ config INPUT_AD714X_SPI To compile this driver as a module, choose M here: the module will be called ad714x-spi. +config INPUT_ARIZONA_HAPTICS + tristate "Arizona haptics support" + depends on MFD_ARIZONA && SND_SOC + select INPUT_FF_MEMLESS + help + Say Y to enable support for the haptics module in Arizona CODECs. + + To compile this driver as a module, choose M here: the + module will be called arizona-haptics. + config INPUT_BMA150 tristate "BMA150/SMB380 acceleration sensor support" depends on I2C @@ -580,6 +590,16 @@ config INPUT_ADXL34X_SPI To compile this driver as a module, choose M here: the module will be called adxl34x-spi. +config INPUT_IMS_PCU + tristate "IMS Passenger Control Unit driver" + depends on USB + depends on LEDS_CLASS + help + Say Y here if you have system with IMS Rave Passenger Control Unit. + + To compile this driver as a module, choose M here: the module will be + called ims_pcu. + config INPUT_CMA3000 tristate "VTI CMA3000 Tri-axis accelerometer" help diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 1f874afeea6a..d7fc17f11d77 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_INPUT_ADXL34X) += adxl34x.o obj-$(CONFIG_INPUT_ADXL34X_I2C) += adxl34x-i2c.o obj-$(CONFIG_INPUT_ADXL34X_SPI) += adxl34x-spi.o obj-$(CONFIG_INPUT_APANEL) += apanel.o +obj-$(CONFIG_INPUT_ARIZONA_HAPTICS) += arizona-haptics.o obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o obj-$(CONFIG_INPUT_BFIN_ROTARY) += bfin_rotary.o @@ -28,6 +29,7 @@ obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o obj-$(CONFIG_INPUT_GPIO_TILT_POLLED) += gpio_tilt_polled.o obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o +obj-$(CONFIG_INPUT_IMS_PCU) += ims-pcu.o obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o diff --git a/drivers/input/misc/ad714x-i2c.c b/drivers/input/misc/ad714x-i2c.c index 29d2064c26f2..e0f522516ef5 100644 --- a/drivers/input/misc/ad714x-i2c.c +++ b/drivers/input/misc/ad714x-i2c.c @@ -13,7 +13,7 @@ #include <linux/pm.h> #include "ad714x.h" -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int ad714x_i2c_suspend(struct device *dev) { return ad714x_disable(i2c_get_clientdata(to_i2c_client(dev))); diff --git a/drivers/input/misc/ad714x-spi.c b/drivers/input/misc/ad714x-spi.c index bdccca42d138..61891486067c 100644 --- a/drivers/input/misc/ad714x-spi.c +++ b/drivers/input/misc/ad714x-spi.c @@ -16,7 +16,7 @@ #define AD714x_SPI_CMD_PREFIX 0xE000 /* bits 15:11 */ #define AD714x_SPI_READ BIT(10) -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int ad714x_spi_suspend(struct device *dev) { return ad714x_disable(spi_get_drvdata(to_spi_device(dev))); diff --git a/drivers/input/misc/adxl34x-i2c.c b/drivers/input/misc/adxl34x-i2c.c index 535dda48cace..416f47ddcc90 100644 --- a/drivers/input/misc/adxl34x-i2c.c +++ b/drivers/input/misc/adxl34x-i2c.c @@ -105,7 +105,7 @@ static int adxl34x_i2c_remove(struct i2c_client *client) return adxl34x_remove(ac); } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int adxl34x_i2c_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); diff --git a/drivers/input/misc/adxl34x-spi.c b/drivers/input/misc/adxl34x-spi.c index ad5f40d37e48..76dc0679d3b1 100644 --- a/drivers/input/misc/adxl34x-spi.c +++ b/drivers/input/misc/adxl34x-spi.c @@ -89,16 +89,16 @@ static int adxl34x_spi_probe(struct spi_device *spi) static int adxl34x_spi_remove(struct spi_device *spi) { - struct adxl34x *ac = dev_get_drvdata(&spi->dev); + struct adxl34x *ac = spi_get_drvdata(spi); return adxl34x_remove(ac); } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int adxl34x_spi_suspend(struct device *dev) { struct spi_device *spi = to_spi_device(dev); - struct adxl34x *ac = dev_get_drvdata(&spi->dev); + struct adxl34x *ac = spi_get_drvdata(spi); adxl34x_suspend(ac); @@ -108,7 +108,7 @@ static int adxl34x_spi_suspend(struct device *dev) static int adxl34x_spi_resume(struct device *dev) { struct spi_device *spi = to_spi_device(dev); - struct adxl34x *ac = dev_get_drvdata(&spi->dev); + struct adxl34x *ac = spi_get_drvdata(spi); adxl34x_resume(ac); diff --git a/drivers/input/misc/arizona-haptics.c b/drivers/input/misc/arizona-haptics.c new file mode 100644 index 000000000000..7a04f54ef961 --- /dev/null +++ b/drivers/input/misc/arizona-haptics.c @@ -0,0 +1,255 @@ +/* + * Arizona haptics driver + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + * + * 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 <linux/module.h> +#include <linux/platform_device.h> +#include <linux/input.h> +#include <linux/slab.h> + +#include <sound/soc.h> +#include <sound/soc-dapm.h> + +#include <linux/mfd/arizona/core.h> +#include <linux/mfd/arizona/pdata.h> +#include <linux/mfd/arizona/registers.h> + +struct arizona_haptics { + struct arizona *arizona; + struct input_dev *input_dev; + struct work_struct work; + + struct mutex mutex; + u8 intensity; +}; + +static void arizona_haptics_work(struct work_struct *work) +{ + struct arizona_haptics *haptics = container_of(work, + struct arizona_haptics, + work); + struct arizona *arizona = haptics->arizona; + struct mutex *dapm_mutex = &arizona->dapm->card->dapm_mutex; + int ret; + + if (!haptics->arizona->dapm) { + dev_err(arizona->dev, "No DAPM context\n"); + return; + } + + if (haptics->intensity) { + ret = regmap_update_bits(arizona->regmap, + ARIZONA_HAPTICS_PHASE_2_INTENSITY, + ARIZONA_PHASE2_INTENSITY_MASK, + haptics->intensity); + if (ret != 0) { + dev_err(arizona->dev, "Failed to set intensity: %d\n", + ret); + return; + } + + /* This enable sequence will be a noop if already enabled */ + ret = regmap_update_bits(arizona->regmap, + ARIZONA_HAPTICS_CONTROL_1, + ARIZONA_HAP_CTRL_MASK, + 1 << ARIZONA_HAP_CTRL_SHIFT); + if (ret != 0) { + dev_err(arizona->dev, "Failed to start haptics: %d\n", + ret); + return; + } + + mutex_lock_nested(dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + + ret = snd_soc_dapm_enable_pin(arizona->dapm, "HAPTICS"); + if (ret != 0) { + dev_err(arizona->dev, "Failed to start HAPTICS: %d\n", + ret); + mutex_unlock(dapm_mutex); + return; + } + + ret = snd_soc_dapm_sync(arizona->dapm); + if (ret != 0) { + dev_err(arizona->dev, "Failed to sync DAPM: %d\n", + ret); + mutex_unlock(dapm_mutex); + return; + } + + mutex_unlock(dapm_mutex); + + } else { + /* This disable sequence will be a noop if already enabled */ + mutex_lock_nested(dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + + ret = snd_soc_dapm_disable_pin(arizona->dapm, "HAPTICS"); + if (ret != 0) { + dev_err(arizona->dev, "Failed to disable HAPTICS: %d\n", + ret); + mutex_unlock(dapm_mutex); + return; + } + + ret = snd_soc_dapm_sync(arizona->dapm); + if (ret != 0) { + dev_err(arizona->dev, "Failed to sync DAPM: %d\n", + ret); + mutex_unlock(dapm_mutex); + return; + } + + mutex_unlock(dapm_mutex); + + ret = regmap_update_bits(arizona->regmap, + ARIZONA_HAPTICS_CONTROL_1, + ARIZONA_HAP_CTRL_MASK, + 1 << ARIZONA_HAP_CTRL_SHIFT); + if (ret != 0) { + dev_err(arizona->dev, "Failed to stop haptics: %d\n", + ret); + return; + } + } +} + +static int arizona_haptics_play(struct input_dev *input, void *data, + struct ff_effect *effect) +{ + struct arizona_haptics *haptics = input_get_drvdata(input); + struct arizona *arizona = haptics->arizona; + + if (!arizona->dapm) { + dev_err(arizona->dev, "No DAPM context\n"); + return -EBUSY; + } + + if (effect->u.rumble.strong_magnitude) { + /* Scale the magnitude into the range the device supports */ + if (arizona->pdata.hap_act) { + haptics->intensity = + effect->u.rumble.strong_magnitude >> 9; + if (effect->direction < 0x8000) + haptics->intensity += 0x7f; + } else { + haptics->intensity = + effect->u.rumble.strong_magnitude >> 8; + } + } else { + haptics->intensity = 0; + } + + schedule_work(&haptics->work); + + return 0; +} + +static void arizona_haptics_close(struct input_dev *input) +{ + struct arizona_haptics *haptics = input_get_drvdata(input); + struct mutex *dapm_mutex = &haptics->arizona->dapm->card->dapm_mutex; + + cancel_work_sync(&haptics->work); + + mutex_lock_nested(dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + + if (haptics->arizona->dapm) + snd_soc_dapm_disable_pin(haptics->arizona->dapm, "HAPTICS"); + + mutex_unlock(dapm_mutex); +} + +static int arizona_haptics_probe(struct platform_device *pdev) +{ + struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); + struct arizona_haptics *haptics; + int ret; + + haptics = devm_kzalloc(&pdev->dev, sizeof(*haptics), GFP_KERNEL); + if (!haptics) + return -ENOMEM; + + haptics->arizona = arizona; + + ret = regmap_update_bits(arizona->regmap, ARIZONA_HAPTICS_CONTROL_1, + ARIZONA_HAP_ACT, arizona->pdata.hap_act); + if (ret != 0) { + dev_err(arizona->dev, "Failed to set haptics actuator: %d\n", + ret); + return ret; + } + + INIT_WORK(&haptics->work, arizona_haptics_work); + + haptics->input_dev = input_allocate_device(); + if (haptics->input_dev == NULL) { + dev_err(arizona->dev, "Failed to allocate input device\n"); + return -ENOMEM; + } + + input_set_drvdata(haptics->input_dev, haptics); + + haptics->input_dev->name = "arizona:haptics"; + haptics->input_dev->dev.parent = pdev->dev.parent; + haptics->input_dev->close = arizona_haptics_close; + __set_bit(FF_RUMBLE, haptics->input_dev->ffbit); + + ret = input_ff_create_memless(haptics->input_dev, NULL, + arizona_haptics_play); + if (ret < 0) { + dev_err(arizona->dev, "input_ff_create_memless() failed: %d\n", + ret); + goto err_ialloc; + } + + ret = input_register_device(haptics->input_dev); + if (ret < 0) { + dev_err(arizona->dev, "couldn't register input device: %d\n", + ret); + goto err_iff; + } + + platform_set_drvdata(pdev, haptics); + + return 0; + +err_iff: + if (haptics->input_dev) + input_ff_destroy(haptics->input_dev); +err_ialloc: + input_free_device(haptics->input_dev); + + return ret; +} + +static int arizona_haptics_remove(struct platform_device *pdev) +{ + struct arizona_haptics *haptics = platform_get_drvdata(pdev); + + input_unregister_device(haptics->input_dev); + + return 0; +} + +static struct platform_driver arizona_haptics_driver = { + .probe = arizona_haptics_probe, + .remove = arizona_haptics_remove, + .driver = { + .name = "arizona-haptics", + .owner = THIS_MODULE, + }, +}; +module_platform_driver(arizona_haptics_driver); + +MODULE_ALIAS("platform:arizona-haptics"); +MODULE_DESCRIPTION("Arizona haptics driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); diff --git a/drivers/input/misc/atlas_btns.c b/drivers/input/misc/atlas_btns.c index 26f13131639a..5d4402365a52 100644 --- a/drivers/input/misc/atlas_btns.c +++ b/drivers/input/misc/atlas_btns.c @@ -121,7 +121,7 @@ static int atlas_acpi_button_add(struct acpi_device *device) return err; } -static int atlas_acpi_button_remove(struct acpi_device *device, int type) +static int atlas_acpi_button_remove(struct acpi_device *device) { acpi_status status; diff --git a/drivers/input/misc/da9052_onkey.c b/drivers/input/misc/da9052_onkey.c index 630c1ce4980a..020569a499f2 100644 --- a/drivers/input/misc/da9052_onkey.c +++ b/drivers/input/misc/da9052_onkey.c @@ -24,7 +24,6 @@ struct da9052_onkey { struct da9052 *da9052; struct input_dev *input; struct delayed_work work; - unsigned int irq; }; static void da9052_onkey_query(struct da9052_onkey *onkey) @@ -76,7 +75,6 @@ static int da9052_onkey_probe(struct platform_device *pdev) struct da9052 *da9052 = dev_get_drvdata(pdev->dev.parent); struct da9052_onkey *onkey; struct input_dev *input_dev; - int irq; int error; if (!da9052) { @@ -84,13 +82,6 @@ static int da9052_onkey_probe(struct platform_device *pdev) return -EINVAL; } - irq = platform_get_irq_byname(pdev, "ONKEY"); - if (irq < 0) { - dev_err(&pdev->dev, - "Failed to get an IRQ for input device, %d\n", irq); - return -EINVAL; - } - onkey = kzalloc(sizeof(*onkey), GFP_KERNEL); input_dev = input_allocate_device(); if (!onkey || !input_dev) { @@ -101,7 +92,6 @@ static int da9052_onkey_probe(struct platform_device *pdev) onkey->input = input_dev; onkey->da9052 = da9052; - onkey->irq = irq; INIT_DELAYED_WORK(&onkey->work, da9052_onkey_work); input_dev->name = "da9052-onkey"; @@ -111,13 +101,11 @@ static int da9052_onkey_probe(struct platform_device *pdev) input_dev->evbit[0] = BIT_MASK(EV_KEY); __set_bit(KEY_POWER, input_dev->keybit); - error = request_threaded_irq(onkey->irq, NULL, da9052_onkey_irq, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, - "ONKEY", onkey); + error = da9052_request_irq(onkey->da9052, DA9052_IRQ_NONKEY, "ONKEY", + da9052_onkey_irq, onkey); if (error < 0) { dev_err(onkey->da9052->dev, - "Failed to register ONKEY IRQ %d, error = %d\n", - onkey->irq, error); + "Failed to register ONKEY IRQ: %d\n", error); goto err_free_mem; } @@ -132,7 +120,7 @@ static int da9052_onkey_probe(struct platform_device *pdev) return 0; err_free_irq: - free_irq(onkey->irq, onkey); + da9052_free_irq(onkey->da9052, DA9052_IRQ_NONKEY, onkey); cancel_delayed_work_sync(&onkey->work); err_free_mem: input_free_device(input_dev); @@ -145,7 +133,7 @@ static int da9052_onkey_remove(struct platform_device *pdev) { struct da9052_onkey *onkey = platform_get_drvdata(pdev); - free_irq(onkey->irq, onkey); + da9052_free_irq(onkey->da9052, DA9052_IRQ_NONKEY, onkey); cancel_delayed_work_sync(&onkey->work); input_unregister_device(onkey->input); diff --git a/drivers/input/misc/hp_sdc_rtc.c b/drivers/input/misc/hp_sdc_rtc.c index 0b4f54265f62..2e3334b8f82d 100644 --- a/drivers/input/misc/hp_sdc_rtc.c +++ b/drivers/input/misc/hp_sdc_rtc.c @@ -109,7 +109,9 @@ static int hp_sdc_rtc_do_read_bbrtc (struct rtc_time *rtctm) if (hp_sdc_enqueue_transaction(&t)) return -1; - down_interruptible(&tsem); /* Put ourselves to sleep for results. */ + /* Put ourselves to sleep for results. */ + if (WARN_ON(down_interruptible(&tsem))) + return -1; /* Check for nonpresence of BBRTC */ if (!((tseq[83] | tseq[90] | tseq[69] | tseq[76] | @@ -176,11 +178,16 @@ static int64_t hp_sdc_rtc_read_i8042timer (uint8_t loadcmd, int numreg) t.seq = tseq; t.act.semaphore = &i8042tregs; - down_interruptible(&i8042tregs); /* Sleep if output regs in use. */ + /* Sleep if output regs in use. */ + if (WARN_ON(down_interruptible(&i8042tregs))) + return -1; if (hp_sdc_enqueue_transaction(&t)) return -1; - down_interruptible(&i8042tregs); /* Sleep until results come back. */ + /* Sleep until results come back. */ + if (WARN_ON(down_interruptible(&i8042tregs))) + return -1; + up(&i8042tregs); return (tseq[5] | @@ -276,6 +283,7 @@ static inline int hp_sdc_rtc_read_ct(struct timeval *res) { } +#if 0 /* not used yet */ /* Set the i8042 real-time clock */ static int hp_sdc_rtc_set_rt (struct timeval *setto) { @@ -386,6 +394,7 @@ static int hp_sdc_rtc_set_i8042timer (struct timeval *setto, uint8_t setcmd) } return 0; } +#endif static ssize_t hp_sdc_rtc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c new file mode 100644 index 000000000000..e204f26b0011 --- /dev/null +++ b/drivers/input/misc/ims-pcu.c @@ -0,0 +1,1901 @@ +/* + * Driver for IMS Passenger Control Unit Devices + * + * Copyright (C) 2013 The IMS Company + * + * 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 <linux/completion.h> +#include <linux/device.h> +#include <linux/firmware.h> +#include <linux/ihex.h> +#include <linux/input.h> +#include <linux/kernel.h> +#include <linux/leds.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/usb/input.h> +#include <linux/usb/cdc.h> +#include <asm/unaligned.h> + +#define IMS_PCU_KEYMAP_LEN 32 + +struct ims_pcu_buttons { + struct input_dev *input; + char name[32]; + char phys[32]; + unsigned short keymap[IMS_PCU_KEYMAP_LEN]; +}; + +struct ims_pcu_gamepad { + struct input_dev *input; + char name[32]; + char phys[32]; +}; + +struct ims_pcu_backlight { + struct led_classdev cdev; + struct work_struct work; + enum led_brightness desired_brightness; + char name[32]; +}; + +#define IMS_PCU_PART_NUMBER_LEN 15 +#define IMS_PCU_SERIAL_NUMBER_LEN 8 +#define IMS_PCU_DOM_LEN 8 +#define IMS_PCU_FW_VERSION_LEN (9 + 1) +#define IMS_PCU_BL_VERSION_LEN (9 + 1) +#define IMS_PCU_BL_RESET_REASON_LEN (2 + 1) + +#define IMS_PCU_BUF_SIZE 128 + +struct ims_pcu { + struct usb_device *udev; + struct device *dev; /* control interface's device, used for logging */ + + unsigned int device_no; + + bool bootloader_mode; + + char part_number[IMS_PCU_PART_NUMBER_LEN]; + char serial_number[IMS_PCU_SERIAL_NUMBER_LEN]; + char date_of_manufacturing[IMS_PCU_DOM_LEN]; + char fw_version[IMS_PCU_FW_VERSION_LEN]; + char bl_version[IMS_PCU_BL_VERSION_LEN]; + char reset_reason[IMS_PCU_BL_RESET_REASON_LEN]; + int update_firmware_status; + + struct usb_interface *ctrl_intf; + + struct usb_endpoint_descriptor *ep_ctrl; + struct urb *urb_ctrl; + u8 *urb_ctrl_buf; + dma_addr_t ctrl_dma; + size_t max_ctrl_size; + + struct usb_interface *data_intf; + + struct usb_endpoint_descriptor *ep_in; + struct urb *urb_in; + u8 *urb_in_buf; + dma_addr_t read_dma; + size_t max_in_size; + + struct usb_endpoint_descriptor *ep_out; + u8 *urb_out_buf; + size_t max_out_size; + + u8 read_buf[IMS_PCU_BUF_SIZE]; + u8 read_pos; + u8 check_sum; + bool have_stx; + bool have_dle; + + u8 cmd_buf[IMS_PCU_BUF_SIZE]; + u8 ack_id; + u8 expected_response; + u8 cmd_buf_len; + struct completion cmd_done; + struct mutex cmd_mutex; + + u32 fw_start_addr; + u32 fw_end_addr; + struct completion async_firmware_done; + + struct ims_pcu_buttons buttons; + struct ims_pcu_gamepad *gamepad; + struct ims_pcu_backlight backlight; + + bool setup_complete; /* Input and LED devices have been created */ +}; + + +/********************************************************************* + * Buttons Input device support * + *********************************************************************/ + +static const unsigned short ims_pcu_keymap_1[] = { + [1] = KEY_ATTENDANT_OFF, + [2] = KEY_ATTENDANT_ON, + [3] = KEY_LIGHTS_TOGGLE, + [4] = KEY_VOLUMEUP, + [5] = KEY_VOLUMEDOWN, + [6] = KEY_INFO, +}; + +static const unsigned short ims_pcu_keymap_2[] = { + [4] = KEY_VOLUMEUP, + [5] = KEY_VOLUMEDOWN, + [6] = KEY_INFO, +}; + +static const unsigned short ims_pcu_keymap_3[] = { + [1] = KEY_HOMEPAGE, + [2] = KEY_ATTENDANT_TOGGLE, + [3] = KEY_LIGHTS_TOGGLE, + [4] = KEY_VOLUMEUP, + [5] = KEY_VOLUMEDOWN, + [6] = KEY_DISPLAYTOGGLE, + [18] = KEY_PLAYPAUSE, +}; + +static const unsigned short ims_pcu_keymap_4[] = { + [1] = KEY_ATTENDANT_OFF, + [2] = KEY_ATTENDANT_ON, + [3] = KEY_LIGHTS_TOGGLE, + [4] = KEY_VOLUMEUP, + [5] = KEY_VOLUMEDOWN, + [6] = KEY_INFO, + [18] = KEY_PLAYPAUSE, +}; + +static const unsigned short ims_pcu_keymap_5[] = { + [1] = KEY_ATTENDANT_OFF, + [2] = KEY_ATTENDANT_ON, + [3] = KEY_LIGHTS_TOGGLE, +}; + +struct ims_pcu_device_info { + const unsigned short *keymap; + size_t keymap_len; + bool has_gamepad; +}; + +#define IMS_PCU_DEVINFO(_n, _gamepad) \ + [_n] = { \ + .keymap = ims_pcu_keymap_##_n, \ + .keymap_len = ARRAY_SIZE(ims_pcu_keymap_##_n), \ + .has_gamepad = _gamepad, \ + } + +static const struct ims_pcu_device_info ims_pcu_device_info[] = { + IMS_PCU_DEVINFO(1, true), + IMS_PCU_DEVINFO(2, true), + IMS_PCU_DEVINFO(3, true), + IMS_PCU_DEVINFO(4, true), + IMS_PCU_DEVINFO(5, false), +}; + +static void ims_pcu_buttons_report(struct ims_pcu *pcu, u32 data) +{ + struct ims_pcu_buttons *buttons = &pcu->buttons; + struct input_dev *input = buttons->input; + int i; + + for (i = 0; i < 32; i++) { + unsigned short keycode = buttons->keymap[i]; + + if (keycode != KEY_RESERVED) + input_report_key(input, keycode, data & (1UL << i)); + } + + input_sync(input); +} + +static int ims_pcu_setup_buttons(struct ims_pcu *pcu, + const unsigned short *keymap, + size_t keymap_len) +{ + struct ims_pcu_buttons *buttons = &pcu->buttons; + struct input_dev *input; + int i; + int error; + + input = input_allocate_device(); + if (!input) { + dev_err(pcu->dev, + "Not enough memory for input input device\n"); + return -ENOMEM; + } + + snprintf(buttons->name, sizeof(buttons->name), + "IMS PCU#%d Button Interface", pcu->device_no); + + usb_make_path(pcu->udev, buttons->phys, sizeof(buttons->phys)); + strlcat(buttons->phys, "/input0", sizeof(buttons->phys)); + + memcpy(buttons->keymap, keymap, sizeof(*keymap) * keymap_len); + + input->name = buttons->name; + input->phys = buttons->phys; + usb_to_input_id(pcu->udev, &input->id); + input->dev.parent = &pcu->ctrl_intf->dev; + + input->keycode = buttons->keymap; + input->keycodemax = ARRAY_SIZE(buttons->keymap); + input->keycodesize = sizeof(buttons->keymap[0]); + + __set_bit(EV_KEY, input->evbit); + for (i = 0; i < IMS_PCU_KEYMAP_LEN; i++) + __set_bit(buttons->keymap[i], input->keybit); + __clear_bit(KEY_RESERVED, input->keybit); + + error = input_register_device(input); + if (error) { + dev_err(pcu->dev, + "Failed to register buttons input device: %d\n", + error); + input_free_device(input); + return error; + } + + buttons->input = input; + return 0; +} + +static void ims_pcu_destroy_buttons(struct ims_pcu *pcu) +{ + struct ims_pcu_buttons *buttons = &pcu->buttons; + + input_unregister_device(buttons->input); +} + + +/********************************************************************* + * Gamepad Input device support * + *********************************************************************/ + +static void ims_pcu_gamepad_report(struct ims_pcu *pcu, u32 data) +{ + struct ims_pcu_gamepad *gamepad = pcu->gamepad; + struct input_dev *input = gamepad->input; + int x, y; + + x = !!(data & (1 << 14)) - !!(data & (1 << 13)); + y = !!(data & (1 << 12)) - !!(data & (1 << 11)); + + input_report_abs(input, ABS_X, x); + input_report_abs(input, ABS_Y, y); + + input_report_key(input, BTN_A, data & (1 << 7)); + input_report_key(input, BTN_B, data & (1 << 8)); + input_report_key(input, BTN_X, data & (1 << 9)); + input_report_key(input, BTN_Y, data & (1 << 10)); + input_report_key(input, BTN_START, data & (1 << 15)); + input_report_key(input, BTN_SELECT, data & (1 << 16)); + + input_sync(input); +} + +static int ims_pcu_setup_gamepad(struct ims_pcu *pcu) +{ + struct ims_pcu_gamepad *gamepad; + struct input_dev *input; + int error; + + gamepad = kzalloc(sizeof(struct ims_pcu_gamepad), GFP_KERNEL); + input = input_allocate_device(); + if (!gamepad || !input) { + dev_err(pcu->dev, + "Not enough memory for gamepad device\n"); + error = -ENOMEM; + goto err_free_mem; + } + + gamepad->input = input; + + snprintf(gamepad->name, sizeof(gamepad->name), + "IMS PCU#%d Gamepad Interface", pcu->device_no); + + usb_make_path(pcu->udev, gamepad->phys, sizeof(gamepad->phys)); + strlcat(gamepad->phys, "/input1", sizeof(gamepad->phys)); + + input->name = gamepad->name; + input->phys = gamepad->phys; + usb_to_input_id(pcu->udev, &input->id); + input->dev.parent = &pcu->ctrl_intf->dev; + + __set_bit(EV_KEY, input->evbit); + __set_bit(BTN_A, input->keybit); + __set_bit(BTN_B, input->keybit); + __set_bit(BTN_X, input->keybit); + __set_bit(BTN_Y, input->keybit); + __set_bit(BTN_START, input->keybit); + __set_bit(BTN_SELECT, input->keybit); + + __set_bit(EV_ABS, input->evbit); + input_set_abs_params(input, ABS_X, -1, 1, 0, 0); + input_set_abs_params(input, ABS_Y, -1, 1, 0, 0); + + error = input_register_device(input); + if (error) { + dev_err(pcu->dev, + "Failed to register gamepad input device: %d\n", + error); + goto err_free_mem; + } + + pcu->gamepad = gamepad; + return 0; + +err_free_mem: + input_free_device(input); + kfree(gamepad); + return -ENOMEM; +} + +static void ims_pcu_destroy_gamepad(struct ims_pcu *pcu) +{ + struct ims_pcu_gamepad *gamepad = pcu->gamepad; + + input_unregister_device(gamepad->input); + kfree(gamepad); +} + + +/********************************************************************* + * PCU Communication protocol handling * + *********************************************************************/ + +#define IMS_PCU_PROTOCOL_STX 0x02 +#define IMS_PCU_PROTOCOL_ETX 0x03 +#define IMS_PCU_PROTOCOL_DLE 0x10 + +/* PCU commands */ +#define IMS_PCU_CMD_STATUS 0xa0 +#define IMS_PCU_CMD_PCU_RESET 0xa1 +#define IMS_PCU_CMD_RESET_REASON 0xa2 +#define IMS_PCU_CMD_SEND_BUTTONS 0xa3 +#define IMS_PCU_CMD_JUMP_TO_BTLDR 0xa4 +#define IMS_PCU_CMD_GET_INFO 0xa5 +#define IMS_PCU_CMD_SET_BRIGHTNESS 0xa6 +#define IMS_PCU_CMD_EEPROM 0xa7 +#define IMS_PCU_CMD_GET_FW_VERSION 0xa8 +#define IMS_PCU_CMD_GET_BL_VERSION 0xa9 +#define IMS_PCU_CMD_SET_INFO 0xab +#define IMS_PCU_CMD_GET_BRIGHTNESS 0xac +#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 */ + +/* PCU responses */ +#define IMS_PCU_RSP_STATUS 0xc0 +#define IMS_PCU_RSP_PCU_RESET 0 /* Originally 0xc1 */ +#define IMS_PCU_RSP_RESET_REASON 0xc2 +#define IMS_PCU_RSP_SEND_BUTTONS 0xc3 +#define IMS_PCU_RSP_JUMP_TO_BTLDR 0 /* Originally 0xc4 */ +#define IMS_PCU_RSP_GET_INFO 0xc5 +#define IMS_PCU_RSP_SET_BRIGHTNESS 0xc6 +#define IMS_PCU_RSP_EEPROM 0xc7 +#define IMS_PCU_RSP_GET_FW_VERSION 0xc8 +#define IMS_PCU_RSP_GET_BL_VERSION 0xc9 +#define IMS_PCU_RSP_SET_INFO 0xcb +#define IMS_PCU_RSP_GET_BRIGHTNESS 0xcc +#define IMS_PCU_RSP_CMD_INVALID 0xcd +#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_EVNT_BUTTONS 0xe0 /* Unsolicited, button state */ +#define IMS_PCU_GAMEPAD_MASK 0x0001ff80UL /* Bits 7 through 16 */ + + +#define IMS_PCU_MIN_PACKET_LEN 3 +#define IMS_PCU_DATA_OFFSET 2 + +#define IMS_PCU_CMD_WRITE_TIMEOUT 100 /* msec */ +#define IMS_PCU_CMD_RESPONSE_TIMEOUT 500 /* msec */ + +static void ims_pcu_report_events(struct ims_pcu *pcu) +{ + u32 data = get_unaligned_be32(&pcu->read_buf[3]); + + ims_pcu_buttons_report(pcu, data & ~IMS_PCU_GAMEPAD_MASK); + if (pcu->gamepad) + ims_pcu_gamepad_report(pcu, data); +} + +static void ims_pcu_handle_response(struct ims_pcu *pcu) +{ + switch (pcu->read_buf[0]) { + case IMS_PCU_RSP_EVNT_BUTTONS: + if (likely(pcu->setup_complete)) + ims_pcu_report_events(pcu); + break; + + default: + /* + * See if we got command completion. + * If both the sequence and response code match save + * the data and signal completion. + */ + if (pcu->read_buf[0] == pcu->expected_response && + pcu->read_buf[1] == pcu->ack_id - 1) { + + memcpy(pcu->cmd_buf, pcu->read_buf, pcu->read_pos); + pcu->cmd_buf_len = pcu->read_pos; + complete(&pcu->cmd_done); + } + break; + } +} + +static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb) +{ + int i; + + for (i = 0; i < urb->actual_length; i++) { + u8 data = pcu->urb_in_buf[i]; + + /* Skip everything until we get Start Xmit */ + if (!pcu->have_stx && data != IMS_PCU_PROTOCOL_STX) + continue; + + if (pcu->have_dle) { + pcu->have_dle = false; + pcu->read_buf[pcu->read_pos++] = data; + pcu->check_sum += data; + continue; + } + + switch (data) { + case IMS_PCU_PROTOCOL_STX: + if (pcu->have_stx) + dev_warn(pcu->dev, + "Unexpected STX at byte %d, discarding old data\n", + pcu->read_pos); + pcu->have_stx = true; + pcu->have_dle = false; + pcu->read_pos = 0; + pcu->check_sum = 0; + break; + + case IMS_PCU_PROTOCOL_DLE: + pcu->have_dle = true; + break; + + case IMS_PCU_PROTOCOL_ETX: + if (pcu->read_pos < IMS_PCU_MIN_PACKET_LEN) { + dev_warn(pcu->dev, + "Short packet received (%d bytes), ignoring\n", + pcu->read_pos); + } else if (pcu->check_sum != 0) { + dev_warn(pcu->dev, + "Invalid checksum in packet (%d bytes), ignoring\n", + pcu->read_pos); + } else { + ims_pcu_handle_response(pcu); + } + + pcu->have_stx = false; + pcu->have_dle = false; + pcu->read_pos = 0; + break; + + default: + pcu->read_buf[pcu->read_pos++] = data; + pcu->check_sum += data; + break; + } + } +} + +static bool ims_pcu_byte_needs_escape(u8 byte) +{ + return byte == IMS_PCU_PROTOCOL_STX || + byte == IMS_PCU_PROTOCOL_ETX || + byte == IMS_PCU_PROTOCOL_DLE; +} + +static int ims_pcu_send_cmd_chunk(struct ims_pcu *pcu, + u8 command, int chunk, int len) +{ + int error; + + error = usb_bulk_msg(pcu->udev, + usb_sndbulkpipe(pcu->udev, + pcu->ep_out->bEndpointAddress), + pcu->urb_out_buf, len, + NULL, IMS_PCU_CMD_WRITE_TIMEOUT); + if (error < 0) { + dev_dbg(pcu->dev, + "Sending 0x%02x command failed at chunk %d: %d\n", + command, chunk, error); + return error; + } + + return 0; +} + +static int ims_pcu_send_command(struct ims_pcu *pcu, + u8 command, const u8 *data, int len) +{ + int count = 0; + int chunk = 0; + int delta; + int i; + int error; + u8 csum = 0; + u8 ack_id; + + pcu->urb_out_buf[count++] = IMS_PCU_PROTOCOL_STX; + + /* We know the command need not be escaped */ + pcu->urb_out_buf[count++] = command; + csum += command; + + ack_id = pcu->ack_id++; + if (ack_id == 0xff) + ack_id = pcu->ack_id++; + + if (ims_pcu_byte_needs_escape(ack_id)) + pcu->urb_out_buf[count++] = IMS_PCU_PROTOCOL_DLE; + + pcu->urb_out_buf[count++] = ack_id; + csum += ack_id; + + for (i = 0; i < len; i++) { + + delta = ims_pcu_byte_needs_escape(data[i]) ? 2 : 1; + if (count + delta >= pcu->max_out_size) { + error = ims_pcu_send_cmd_chunk(pcu, command, + ++chunk, count); + if (error) + return error; + + count = 0; + } + + if (delta == 2) + pcu->urb_out_buf[count++] = IMS_PCU_PROTOCOL_DLE; + + pcu->urb_out_buf[count++] = data[i]; + csum += data[i]; + } + + csum = 1 + ~csum; + + delta = ims_pcu_byte_needs_escape(csum) ? 3 : 2; + if (count + delta >= pcu->max_out_size) { + error = ims_pcu_send_cmd_chunk(pcu, command, ++chunk, count); + if (error) + return error; + + count = 0; + } + + if (delta == 3) + pcu->urb_out_buf[count++] = IMS_PCU_PROTOCOL_DLE; + + pcu->urb_out_buf[count++] = csum; + pcu->urb_out_buf[count++] = IMS_PCU_PROTOCOL_ETX; + + return ims_pcu_send_cmd_chunk(pcu, command, ++chunk, count); +} + +static int __ims_pcu_execute_command(struct ims_pcu *pcu, + u8 command, const void *data, size_t len, + u8 expected_response, int response_time) +{ + int error; + + pcu->expected_response = expected_response; + init_completion(&pcu->cmd_done); + + error = ims_pcu_send_command(pcu, command, data, len); + if (error) + return error; + + if (expected_response && + !wait_for_completion_timeout(&pcu->cmd_done, + msecs_to_jiffies(response_time))) { + dev_dbg(pcu->dev, "Command 0x%02x timed out\n", command); + return -ETIMEDOUT; + } + + return 0; +} + +#define ims_pcu_execute_command(pcu, code, data, len) \ + __ims_pcu_execute_command(pcu, \ + IMS_PCU_CMD_##code, data, len, \ + IMS_PCU_RSP_##code, \ + IMS_PCU_CMD_RESPONSE_TIMEOUT) + +#define ims_pcu_execute_query(pcu, code) \ + ims_pcu_execute_command(pcu, code, NULL, 0) + +/* Bootloader commands */ +#define IMS_PCU_BL_CMD_QUERY_DEVICE 0xa1 +#define IMS_PCU_BL_CMD_UNLOCK_CONFIG 0xa2 +#define IMS_PCU_BL_CMD_ERASE_APP 0xa3 +#define IMS_PCU_BL_CMD_PROGRAM_DEVICE 0xa4 +#define IMS_PCU_BL_CMD_PROGRAM_COMPLETE 0xa5 +#define IMS_PCU_BL_CMD_READ_APP 0xa6 +#define IMS_PCU_BL_CMD_RESET_DEVICE 0xa7 +#define IMS_PCU_BL_CMD_LAUNCH_APP 0xa8 + +/* Bootloader commands */ +#define IMS_PCU_BL_RSP_QUERY_DEVICE 0xc1 +#define IMS_PCU_BL_RSP_UNLOCK_CONFIG 0xc2 +#define IMS_PCU_BL_RSP_ERASE_APP 0xc3 +#define IMS_PCU_BL_RSP_PROGRAM_DEVICE 0xc4 +#define IMS_PCU_BL_RSP_PROGRAM_COMPLETE 0xc5 +#define IMS_PCU_BL_RSP_READ_APP 0xc6 +#define IMS_PCU_BL_RSP_RESET_DEVICE 0 /* originally 0xa7 */ +#define IMS_PCU_BL_RSP_LAUNCH_APP 0 /* originally 0xa8 */ + +#define IMS_PCU_BL_DATA_OFFSET 3 + +static int __ims_pcu_execute_bl_command(struct ims_pcu *pcu, + u8 command, const void *data, size_t len, + u8 expected_response, int response_time) +{ + int error; + + pcu->cmd_buf[0] = command; + if (data) + memcpy(&pcu->cmd_buf[1], data, len); + + error = __ims_pcu_execute_command(pcu, + IMS_PCU_CMD_BOOTLOADER, pcu->cmd_buf, len + 1, + expected_response ? IMS_PCU_RSP_BOOTLOADER : 0, + response_time); + if (error) { + dev_err(pcu->dev, + "Failure when sending 0x%02x command to bootloader, error: %d\n", + pcu->cmd_buf[0], error); + return error; + } + + if (expected_response && pcu->cmd_buf[2] != expected_response) { + dev_err(pcu->dev, + "Unexpected response from bootloader: 0x%02x, wanted 0x%02x\n", + pcu->cmd_buf[2], expected_response); + return -EINVAL; + } + + return 0; +} + +#define ims_pcu_execute_bl_command(pcu, code, data, len, timeout) \ + __ims_pcu_execute_bl_command(pcu, \ + IMS_PCU_BL_CMD_##code, data, len, \ + IMS_PCU_BL_RSP_##code, timeout) \ + +#define IMS_PCU_INFO_PART_OFFSET 2 +#define IMS_PCU_INFO_DOM_OFFSET 17 +#define IMS_PCU_INFO_SERIAL_OFFSET 25 + +#define IMS_PCU_SET_INFO_SIZE 31 + +static int ims_pcu_get_info(struct ims_pcu *pcu) +{ + int error; + + error = ims_pcu_execute_query(pcu, GET_INFO); + if (error) { + dev_err(pcu->dev, + "GET_INFO command failed, error: %d\n", error); + return error; + } + + memcpy(pcu->part_number, + &pcu->cmd_buf[IMS_PCU_INFO_PART_OFFSET], + sizeof(pcu->part_number)); + memcpy(pcu->date_of_manufacturing, + &pcu->cmd_buf[IMS_PCU_INFO_DOM_OFFSET], + sizeof(pcu->date_of_manufacturing)); + memcpy(pcu->serial_number, + &pcu->cmd_buf[IMS_PCU_INFO_SERIAL_OFFSET], + sizeof(pcu->serial_number)); + + return 0; +} + +static int ims_pcu_set_info(struct ims_pcu *pcu) +{ + int error; + + memcpy(&pcu->cmd_buf[IMS_PCU_INFO_PART_OFFSET], + pcu->part_number, sizeof(pcu->part_number)); + memcpy(&pcu->cmd_buf[IMS_PCU_INFO_DOM_OFFSET], + pcu->date_of_manufacturing, sizeof(pcu->date_of_manufacturing)); + memcpy(&pcu->cmd_buf[IMS_PCU_INFO_SERIAL_OFFSET], + pcu->serial_number, sizeof(pcu->serial_number)); + + error = ims_pcu_execute_command(pcu, SET_INFO, + &pcu->cmd_buf[IMS_PCU_DATA_OFFSET], + IMS_PCU_SET_INFO_SIZE); + if (error) { + dev_err(pcu->dev, + "Failed to update device information, error: %d\n", + error); + return error; + } + + return 0; +} + +static int ims_pcu_switch_to_bootloader(struct ims_pcu *pcu) +{ + int error; + + /* Execute jump to the bootoloader */ + error = ims_pcu_execute_command(pcu, JUMP_TO_BTLDR, NULL, 0); + if (error) { + dev_err(pcu->dev, + "Failure when sending JUMP TO BOOLTLOADER command, error: %d\n", + error); + return error; + } + + return 0; +} + +/********************************************************************* + * Firmware Update handling * + *********************************************************************/ + +#define IMS_PCU_FIRMWARE_NAME "imspcu.fw" + +struct ims_pcu_flash_fmt { + __le32 addr; + u8 len; + u8 data[]; +}; + +static unsigned int ims_pcu_count_fw_records(const struct firmware *fw) +{ + const struct ihex_binrec *rec = (const struct ihex_binrec *)fw->data; + unsigned int count = 0; + + while (rec) { + count++; + rec = ihex_next_binrec(rec); + } + + return count; +} + +static int ims_pcu_verify_block(struct ims_pcu *pcu, + u32 addr, u8 len, const u8 *data) +{ + struct ims_pcu_flash_fmt *fragment; + int error; + + fragment = (void *)&pcu->cmd_buf[1]; + put_unaligned_le32(addr, &fragment->addr); + fragment->len = len; + + error = ims_pcu_execute_bl_command(pcu, READ_APP, NULL, 5, + IMS_PCU_CMD_RESPONSE_TIMEOUT); + if (error) { + dev_err(pcu->dev, + "Failed to retrieve block at 0x%08x, len %d, error: %d\n", + addr, len, error); + return error; + } + + fragment = (void *)&pcu->cmd_buf[IMS_PCU_BL_DATA_OFFSET]; + if (get_unaligned_le32(&fragment->addr) != addr || + fragment->len != len) { + dev_err(pcu->dev, + "Wrong block when retrieving 0x%08x (0x%08x), len %d (%d)\n", + addr, get_unaligned_le32(&fragment->addr), + len, fragment->len); + return -EINVAL; + } + + if (memcmp(fragment->data, data, len)) { + dev_err(pcu->dev, + "Mismatch in block at 0x%08x, len %d\n", + addr, len); + return -EINVAL; + } + + return 0; +} + +static int ims_pcu_flash_firmware(struct ims_pcu *pcu, + const struct firmware *fw, + unsigned int n_fw_records) +{ + const struct ihex_binrec *rec = (const struct ihex_binrec *)fw->data; + struct ims_pcu_flash_fmt *fragment; + unsigned int count = 0; + u32 addr; + u8 len; + int error; + + error = ims_pcu_execute_bl_command(pcu, ERASE_APP, NULL, 0, 2000); + if (error) { + dev_err(pcu->dev, + "Failed to erase application image, error: %d\n", + error); + return error; + } + + while (rec) { + /* + * The firmware format is messed up for some reason. + * The address twice that of what is needed for some + * reason and we end up overwriting half of the data + * with the next record. + */ + addr = be32_to_cpu(rec->addr) / 2; + len = be16_to_cpu(rec->len); + + fragment = (void *)&pcu->cmd_buf[1]; + put_unaligned_le32(addr, &fragment->addr); + fragment->len = len; + memcpy(fragment->data, rec->data, len); + + error = ims_pcu_execute_bl_command(pcu, PROGRAM_DEVICE, + NULL, len + 5, + IMS_PCU_CMD_RESPONSE_TIMEOUT); + if (error) { + dev_err(pcu->dev, + "Failed to write block at 0x%08x, len %d, error: %d\n", + addr, len, error); + return error; + } + + if (addr >= pcu->fw_start_addr && addr < pcu->fw_end_addr) { + error = ims_pcu_verify_block(pcu, addr, len, rec->data); + if (error) + return error; + } + + count++; + pcu->update_firmware_status = (count * 100) / n_fw_records; + + rec = ihex_next_binrec(rec); + } + + error = ims_pcu_execute_bl_command(pcu, PROGRAM_COMPLETE, + NULL, 0, 2000); + if (error) + dev_err(pcu->dev, + "Failed to send PROGRAM_COMPLETE, error: %d\n", + error); + + return 0; +} + +static int ims_pcu_handle_firmware_update(struct ims_pcu *pcu, + const struct firmware *fw) +{ + unsigned int n_fw_records; + int retval; + + dev_info(pcu->dev, "Updating firmware %s, size: %zu\n", + IMS_PCU_FIRMWARE_NAME, fw->size); + + n_fw_records = ims_pcu_count_fw_records(fw); + + retval = ims_pcu_flash_firmware(pcu, fw, n_fw_records); + if (retval) + goto out; + + retval = ims_pcu_execute_bl_command(pcu, LAUNCH_APP, NULL, 0, 0); + if (retval) + dev_err(pcu->dev, + "Failed to start application image, error: %d\n", + retval); + +out: + pcu->update_firmware_status = retval; + sysfs_notify(&pcu->dev->kobj, NULL, "update_firmware_status"); + return retval; +} + +static void ims_pcu_process_async_firmware(const struct firmware *fw, + void *context) +{ + struct ims_pcu *pcu = context; + int error; + + if (!fw) { + dev_err(pcu->dev, "Failed to get firmware %s\n", + IMS_PCU_FIRMWARE_NAME); + goto out; + } + + error = ihex_validate_fw(fw); + if (error) { + dev_err(pcu->dev, "Firmware %s is invalid\n", + IMS_PCU_FIRMWARE_NAME); + goto out; + } + + mutex_lock(&pcu->cmd_mutex); + ims_pcu_handle_firmware_update(pcu, fw); + mutex_unlock(&pcu->cmd_mutex); + + release_firmware(fw); + +out: + complete(&pcu->async_firmware_done); +} + +/********************************************************************* + * Backlight LED device support * + *********************************************************************/ + +#define IMS_PCU_MAX_BRIGHTNESS 31998 + +static void ims_pcu_backlight_work(struct work_struct *work) +{ + struct ims_pcu_backlight *backlight = + container_of(work, struct ims_pcu_backlight, work); + struct ims_pcu *pcu = + container_of(backlight, struct ims_pcu, backlight); + int desired_brightness = backlight->desired_brightness; + __le16 br_val = cpu_to_le16(desired_brightness); + int error; + + mutex_lock(&pcu->cmd_mutex); + + error = ims_pcu_execute_command(pcu, SET_BRIGHTNESS, + &br_val, sizeof(br_val)); + if (error && error != -ENODEV) + dev_warn(pcu->dev, + "Failed to set desired brightness %u, error: %d\n", + desired_brightness, error); + + mutex_unlock(&pcu->cmd_mutex); +} + +static void ims_pcu_backlight_set_brightness(struct led_classdev *cdev, + enum led_brightness value) +{ + struct ims_pcu_backlight *backlight = + container_of(cdev, struct ims_pcu_backlight, cdev); + + backlight->desired_brightness = value; + schedule_work(&backlight->work); +} + +static enum led_brightness +ims_pcu_backlight_get_brightness(struct led_classdev *cdev) +{ + struct ims_pcu_backlight *backlight = + container_of(cdev, struct ims_pcu_backlight, cdev); + struct ims_pcu *pcu = + container_of(backlight, struct ims_pcu, backlight); + int brightness; + int error; + + mutex_lock(&pcu->cmd_mutex); + + error = ims_pcu_execute_query(pcu, GET_BRIGHTNESS); + if (error) { + dev_warn(pcu->dev, + "Failed to get current brightness, error: %d\n", + error); + /* Assume the LED is OFF */ + brightness = LED_OFF; + } else { + brightness = + get_unaligned_le16(&pcu->cmd_buf[IMS_PCU_DATA_OFFSET]); + } + + mutex_unlock(&pcu->cmd_mutex); + + return brightness; +} + +static int ims_pcu_setup_backlight(struct ims_pcu *pcu) +{ + struct ims_pcu_backlight *backlight = &pcu->backlight; + int error; + + INIT_WORK(&backlight->work, ims_pcu_backlight_work); + snprintf(backlight->name, sizeof(backlight->name), + "pcu%d::kbd_backlight", pcu->device_no); + + backlight->cdev.name = backlight->name; + backlight->cdev.max_brightness = IMS_PCU_MAX_BRIGHTNESS; + backlight->cdev.brightness_get = ims_pcu_backlight_get_brightness; + backlight->cdev.brightness_set = ims_pcu_backlight_set_brightness; + + error = led_classdev_register(pcu->dev, &backlight->cdev); + if (error) { + dev_err(pcu->dev, + "Failed to register backlight LED device, error: %d\n", + error); + return error; + } + + return 0; +} + +static void ims_pcu_destroy_backlight(struct ims_pcu *pcu) +{ + struct ims_pcu_backlight *backlight = &pcu->backlight; + + led_classdev_unregister(&backlight->cdev); + cancel_work_sync(&backlight->work); +} + + +/********************************************************************* + * Sysfs attributes handling * + *********************************************************************/ + +struct ims_pcu_attribute { + struct device_attribute dattr; + size_t field_offset; + int field_length; +}; + +static ssize_t ims_pcu_attribute_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_attribute *attr = + container_of(dattr, struct ims_pcu_attribute, dattr); + char *field = (char *)pcu + attr->field_offset; + + return scnprintf(buf, PAGE_SIZE, "%.*s\n", attr->field_length, field); +} + +static ssize_t ims_pcu_attribute_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_attribute *attr = + container_of(dattr, struct ims_pcu_attribute, dattr); + char *field = (char *)pcu + attr->field_offset; + size_t data_len; + int error; + + if (count > attr->field_length) + return -EINVAL; + + data_len = strnlen(buf, attr->field_length); + if (data_len > attr->field_length) + return -EINVAL; + + error = mutex_lock_interruptible(&pcu->cmd_mutex); + if (error) + return error; + + memset(field, 0, attr->field_length); + memcpy(field, buf, data_len); + + error = ims_pcu_set_info(pcu); + + /* + * Even if update failed, let's fetch the info again as we just + * clobbered one of the fields. + */ + ims_pcu_get_info(pcu); + + mutex_unlock(&pcu->cmd_mutex); + + return error < 0 ? error : count; +} + +#define IMS_PCU_ATTR(_field, _mode) \ +struct ims_pcu_attribute ims_pcu_attr_##_field = { \ + .dattr = __ATTR(_field, _mode, \ + ims_pcu_attribute_show, \ + ims_pcu_attribute_store), \ + .field_offset = offsetof(struct ims_pcu, _field), \ + .field_length = sizeof(((struct ims_pcu *)NULL)->_field), \ +} + +#define IMS_PCU_RO_ATTR(_field) \ + IMS_PCU_ATTR(_field, S_IRUGO) +#define IMS_PCU_RW_ATTR(_field) \ + IMS_PCU_ATTR(_field, S_IRUGO | S_IWUSR) + +static IMS_PCU_RW_ATTR(part_number); +static IMS_PCU_RW_ATTR(serial_number); +static IMS_PCU_RW_ATTR(date_of_manufacturing); + +static IMS_PCU_RO_ATTR(fw_version); +static IMS_PCU_RO_ATTR(bl_version); +static IMS_PCU_RO_ATTR(reset_reason); + +static ssize_t ims_pcu_reset_device(struct device *dev, + struct device_attribute *dattr, + const char *buf, size_t count) +{ + static const u8 reset_byte = 1; + struct usb_interface *intf = to_usb_interface(dev); + struct ims_pcu *pcu = usb_get_intfdata(intf); + int value; + int error; + + error = kstrtoint(buf, 0, &value); + if (error) + return error; + + if (value != 1) + return -EINVAL; + + dev_info(pcu->dev, "Attempting to reset device\n"); + + error = ims_pcu_execute_command(pcu, PCU_RESET, &reset_byte, 1); + if (error) { + dev_info(pcu->dev, + "Failed to reset device, error: %d\n", + error); + return error; + } + + return count; +} + +static DEVICE_ATTR(reset_device, S_IWUSR, NULL, ims_pcu_reset_device); + +static ssize_t ims_pcu_update_firmware_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); + const struct firmware *fw = NULL; + int value; + int error; + + error = kstrtoint(buf, 0, &value); + if (error) + return error; + + if (value != 1) + return -EINVAL; + + error = mutex_lock_interruptible(&pcu->cmd_mutex); + if (error) + return error; + + error = request_ihex_firmware(&fw, IMS_PCU_FIRMWARE_NAME, pcu->dev); + if (error) { + dev_err(pcu->dev, "Failed to request firmware %s, error: %d\n", + IMS_PCU_FIRMWARE_NAME, error); + goto out; + } + + /* + * If we are already in bootloader mode we can proceed with + * flashing the firmware. + * + * If we are in application mode, then we need to switch into + * bootloader mode, which will cause the device to disconnect + * and reconnect as different device. + */ + if (pcu->bootloader_mode) + error = ims_pcu_handle_firmware_update(pcu, fw); + else + error = ims_pcu_switch_to_bootloader(pcu); + + release_firmware(fw); + +out: + mutex_unlock(&pcu->cmd_mutex); + return error ?: count; +} + +static DEVICE_ATTR(update_firmware, S_IWUSR, + NULL, ims_pcu_update_firmware_store); + +static ssize_t +ims_pcu_update_firmware_status_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); + + return scnprintf(buf, PAGE_SIZE, "%d\n", pcu->update_firmware_status); +} + +static DEVICE_ATTR(update_firmware_status, S_IRUGO, + ims_pcu_update_firmware_status_show, NULL); + +static struct attribute *ims_pcu_attrs[] = { + &ims_pcu_attr_part_number.dattr.attr, + &ims_pcu_attr_serial_number.dattr.attr, + &ims_pcu_attr_date_of_manufacturing.dattr.attr, + &ims_pcu_attr_fw_version.dattr.attr, + &ims_pcu_attr_bl_version.dattr.attr, + &ims_pcu_attr_reset_reason.dattr.attr, + &dev_attr_reset_device.attr, + &dev_attr_update_firmware.attr, + &dev_attr_update_firmware_status.attr, + NULL +}; + +static umode_t ims_pcu_is_attr_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct usb_interface *intf = to_usb_interface(dev); + struct ims_pcu *pcu = usb_get_intfdata(intf); + umode_t mode = attr->mode; + + if (pcu->bootloader_mode) { + if (attr != &dev_attr_update_firmware_status.attr && + attr != &dev_attr_update_firmware.attr && + attr != &dev_attr_reset_device.attr) { + mode = 0; + } + } else { + if (attr == &dev_attr_update_firmware_status.attr) + mode = 0; + } + + return mode; +} + +static struct attribute_group ims_pcu_attr_group = { + .is_visible = ims_pcu_is_attr_visible, + .attrs = ims_pcu_attrs, +}; + +static void ims_pcu_irq(struct urb *urb) +{ + struct ims_pcu *pcu = urb->context; + int retval, status; + + status = urb->status; + + switch (status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dev_dbg(pcu->dev, "%s - urb shutting down with status: %d\n", + __func__, status); + return; + default: + dev_dbg(pcu->dev, "%s - nonzero urb status received: %d\n", + __func__, status); + goto exit; + } + + dev_dbg(pcu->dev, "%s: received %d: %*ph\n", __func__, + urb->actual_length, urb->actual_length, pcu->urb_in_buf); + + if (urb == pcu->urb_in) + ims_pcu_process_data(pcu, urb); + +exit: + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval && retval != -ENODEV) + dev_err(pcu->dev, "%s - usb_submit_urb failed with result %d\n", + __func__, retval); +} + +static int ims_pcu_buffers_alloc(struct ims_pcu *pcu) +{ + int error; + + pcu->urb_in_buf = usb_alloc_coherent(pcu->udev, pcu->max_in_size, + GFP_KERNEL, &pcu->read_dma); + if (!pcu->urb_in_buf) { + dev_err(pcu->dev, + "Failed to allocate memory for read buffer\n"); + return -ENOMEM; + } + + pcu->urb_in = usb_alloc_urb(0, GFP_KERNEL); + if (!pcu->urb_in) { + dev_err(pcu->dev, "Failed to allocate input URB\n"); + error = -ENOMEM; + goto err_free_urb_in_buf; + } + + pcu->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + pcu->urb_in->transfer_dma = pcu->read_dma; + + usb_fill_bulk_urb(pcu->urb_in, pcu->udev, + usb_rcvbulkpipe(pcu->udev, + pcu->ep_in->bEndpointAddress), + pcu->urb_in_buf, pcu->max_in_size, + ims_pcu_irq, pcu); + + /* + * We are using usb_bulk_msg() for sending so there is no point + * in allocating memory with usb_alloc_coherent(). + */ + pcu->urb_out_buf = kmalloc(pcu->max_out_size, GFP_KERNEL); + if (!pcu->urb_out_buf) { + dev_err(pcu->dev, "Failed to allocate memory for write buffer\n"); + error = -ENOMEM; + goto err_free_in_urb; + } + + pcu->urb_ctrl_buf = usb_alloc_coherent(pcu->udev, pcu->max_ctrl_size, + GFP_KERNEL, &pcu->ctrl_dma); + if (!pcu->urb_ctrl_buf) { + dev_err(pcu->dev, + "Failed to allocate memory for read buffer\n"); + goto err_free_urb_out_buf; + } + + pcu->urb_ctrl = usb_alloc_urb(0, GFP_KERNEL); + if (!pcu->urb_ctrl) { + dev_err(pcu->dev, "Failed to allocate input URB\n"); + error = -ENOMEM; + goto err_free_urb_ctrl_buf; + } + + pcu->urb_ctrl->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + pcu->urb_ctrl->transfer_dma = pcu->ctrl_dma; + + usb_fill_int_urb(pcu->urb_ctrl, pcu->udev, + usb_rcvintpipe(pcu->udev, + pcu->ep_ctrl->bEndpointAddress), + pcu->urb_ctrl_buf, pcu->max_ctrl_size, + ims_pcu_irq, pcu, pcu->ep_ctrl->bInterval); + + return 0; + +err_free_urb_ctrl_buf: + usb_free_coherent(pcu->udev, pcu->max_ctrl_size, + pcu->urb_ctrl_buf, pcu->ctrl_dma); +err_free_urb_out_buf: + kfree(pcu->urb_out_buf); +err_free_in_urb: + usb_free_urb(pcu->urb_in); +err_free_urb_in_buf: + usb_free_coherent(pcu->udev, pcu->max_in_size, + pcu->urb_in_buf, pcu->read_dma); + return error; +} + +static void ims_pcu_buffers_free(struct ims_pcu *pcu) +{ + usb_kill_urb(pcu->urb_in); + usb_free_urb(pcu->urb_in); + + usb_free_coherent(pcu->udev, pcu->max_out_size, + pcu->urb_in_buf, pcu->read_dma); + + kfree(pcu->urb_out_buf); + + usb_kill_urb(pcu->urb_ctrl); + usb_free_urb(pcu->urb_ctrl); + + usb_free_coherent(pcu->udev, pcu->max_ctrl_size, + pcu->urb_ctrl_buf, pcu->ctrl_dma); +} + +static const struct usb_cdc_union_desc * +ims_pcu_get_cdc_union_desc(struct usb_interface *intf) +{ + const void *buf = intf->altsetting->extra; + size_t buflen = intf->altsetting->extralen; + struct usb_cdc_union_desc *union_desc; + + if (!buf) { + dev_err(&intf->dev, "Missing descriptor data\n"); + return NULL; + } + + if (!buflen) { + dev_err(&intf->dev, "Zero length descriptor\n"); + return NULL; + } + + while (buflen > 0) { + union_desc = (struct usb_cdc_union_desc *)buf; + + if (union_desc->bDescriptorType == USB_DT_CS_INTERFACE && + union_desc->bDescriptorSubType == USB_CDC_UNION_TYPE) { + dev_dbg(&intf->dev, "Found union header\n"); + return union_desc; + } + + buflen -= union_desc->bLength; + buf += union_desc->bLength; + } + + dev_err(&intf->dev, "Missing CDC union descriptor\n"); + return NULL; +} + +static int ims_pcu_parse_cdc_data(struct usb_interface *intf, struct ims_pcu *pcu) +{ + const struct usb_cdc_union_desc *union_desc; + struct usb_host_interface *alt; + + union_desc = ims_pcu_get_cdc_union_desc(intf); + if (!union_desc) + return -EINVAL; + + pcu->ctrl_intf = usb_ifnum_to_if(pcu->udev, + union_desc->bMasterInterface0); + + alt = pcu->ctrl_intf->cur_altsetting; + pcu->ep_ctrl = &alt->endpoint[0].desc; + pcu->max_ctrl_size = usb_endpoint_maxp(pcu->ep_ctrl); + + pcu->data_intf = usb_ifnum_to_if(pcu->udev, + union_desc->bSlaveInterface0); + + alt = pcu->data_intf->cur_altsetting; + if (alt->desc.bNumEndpoints != 2) { + dev_err(pcu->dev, + "Incorrect number of endpoints on data interface (%d)\n", + alt->desc.bNumEndpoints); + return -EINVAL; + } + + pcu->ep_out = &alt->endpoint[0].desc; + if (!usb_endpoint_is_bulk_out(pcu->ep_out)) { + dev_err(pcu->dev, + "First endpoint on data interface is not BULK OUT\n"); + return -EINVAL; + } + + pcu->max_out_size = usb_endpoint_maxp(pcu->ep_out); + if (pcu->max_out_size < 8) { + dev_err(pcu->dev, + "Max OUT packet size is too small (%zd)\n", + pcu->max_out_size); + return -EINVAL; + } + + pcu->ep_in = &alt->endpoint[1].desc; + if (!usb_endpoint_is_bulk_in(pcu->ep_in)) { + dev_err(pcu->dev, + "Second endpoint on data interface is not BULK IN\n"); + return -EINVAL; + } + + pcu->max_in_size = usb_endpoint_maxp(pcu->ep_in); + if (pcu->max_in_size < 8) { + dev_err(pcu->dev, + "Max IN packet size is too small (%zd)\n", + pcu->max_in_size); + return -EINVAL; + } + + return 0; +} + +static int ims_pcu_start_io(struct ims_pcu *pcu) +{ + int error; + + error = usb_submit_urb(pcu->urb_ctrl, GFP_KERNEL); + if (error) { + dev_err(pcu->dev, + "Failed to start control IO - usb_submit_urb failed with result: %d\n", + error); + return -EIO; + } + + error = usb_submit_urb(pcu->urb_in, GFP_KERNEL); + if (error) { + dev_err(pcu->dev, + "Failed to start IO - usb_submit_urb failed with result: %d\n", + error); + usb_kill_urb(pcu->urb_ctrl); + return -EIO; + } + + return 0; +} + +static void ims_pcu_stop_io(struct ims_pcu *pcu) +{ + usb_kill_urb(pcu->urb_in); + usb_kill_urb(pcu->urb_ctrl); +} + +static int ims_pcu_line_setup(struct ims_pcu *pcu) +{ + struct usb_host_interface *interface = pcu->ctrl_intf->cur_altsetting; + struct usb_cdc_line_coding *line = (void *)pcu->cmd_buf; + int error; + + memset(line, 0, sizeof(*line)); + line->dwDTERate = cpu_to_le32(57600); + line->bDataBits = 8; + + error = usb_control_msg(pcu->udev, usb_sndctrlpipe(pcu->udev, 0), + USB_CDC_REQ_SET_LINE_CODING, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0, interface->desc.bInterfaceNumber, + line, sizeof(struct usb_cdc_line_coding), + 5000); + if (error < 0) { + dev_err(pcu->dev, "Failed to set line coding, error: %d\n", + error); + return error; + } + + error = usb_control_msg(pcu->udev, usb_sndctrlpipe(pcu->udev, 0), + USB_CDC_REQ_SET_CONTROL_LINE_STATE, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0x03, interface->desc.bInterfaceNumber, + NULL, 0, 5000); + if (error < 0) { + dev_err(pcu->dev, "Failed to set line state, error: %d\n", + error); + return error; + } + + return 0; +} + +static int ims_pcu_get_device_info(struct ims_pcu *pcu) +{ + int error; + + error = ims_pcu_get_info(pcu); + if (error) + return error; + + error = ims_pcu_execute_query(pcu, GET_FW_VERSION); + if (error) { + dev_err(pcu->dev, + "GET_FW_VERSION command failed, error: %d\n", error); + return error; + } + + snprintf(pcu->fw_version, sizeof(pcu->fw_version), + "%02d%02d%02d%02d.%c%c", + pcu->cmd_buf[2], pcu->cmd_buf[3], pcu->cmd_buf[4], pcu->cmd_buf[5], + pcu->cmd_buf[6], pcu->cmd_buf[7]); + + error = ims_pcu_execute_query(pcu, GET_BL_VERSION); + if (error) { + dev_err(pcu->dev, + "GET_BL_VERSION command failed, error: %d\n", error); + return error; + } + + snprintf(pcu->bl_version, sizeof(pcu->bl_version), + "%02d%02d%02d%02d.%c%c", + pcu->cmd_buf[2], pcu->cmd_buf[3], pcu->cmd_buf[4], pcu->cmd_buf[5], + pcu->cmd_buf[6], pcu->cmd_buf[7]); + + error = ims_pcu_execute_query(pcu, RESET_REASON); + if (error) { + dev_err(pcu->dev, + "RESET_REASON command failed, error: %d\n", error); + return error; + } + + snprintf(pcu->reset_reason, sizeof(pcu->reset_reason), + "%02x", pcu->cmd_buf[IMS_PCU_DATA_OFFSET]); + + dev_dbg(pcu->dev, + "P/N: %s, MD: %s, S/N: %s, FW: %s, BL: %s, RR: %s\n", + pcu->part_number, + pcu->date_of_manufacturing, + pcu->serial_number, + pcu->fw_version, + pcu->bl_version, + pcu->reset_reason); + + return 0; +} + +static int ims_pcu_identify_type(struct ims_pcu *pcu, u8 *device_id) +{ + int error; + + error = ims_pcu_execute_query(pcu, GET_DEVICE_ID); + if (error) { + dev_err(pcu->dev, + "GET_DEVICE_ID command failed, error: %d\n", error); + return error; + } + + *device_id = pcu->cmd_buf[IMS_PCU_DATA_OFFSET]; + dev_dbg(pcu->dev, "Detected device ID: %d\n", *device_id); + + return 0; +} + +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); + if (error) { + /* Device does not respond to basic queries, hopeless */ + return error; + } + + error = ims_pcu_identify_type(pcu, &device_id); + if (error) { + dev_err(pcu->dev, + "Failed to identify device, error: %d\n", error); + /* + * Do not signal error, but do not create input nor + * backlight devices either, let userspace figure this + * out (flash a new firmware?). + */ + 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); + /* Same as above, punt to userspace */ + return 0; + } + + /* Device appears to be operable, complete initialization */ + pcu->device_no = atomic_inc_return(&device_no) - 1; + + error = ims_pcu_setup_backlight(pcu); + if (error) + return error; + + info = &ims_pcu_device_info[device_id]; + error = ims_pcu_setup_buttons(pcu, info->keymap, info->keymap_len); + if (error) + goto err_destroy_backlight; + + if (info->has_gamepad) { + error = ims_pcu_setup_gamepad(pcu); + if (error) + goto err_destroy_buttons; + } + + pcu->setup_complete = true; + + return 0; + +err_destroy_backlight: + ims_pcu_destroy_backlight(pcu); +err_destroy_buttons: + ims_pcu_destroy_buttons(pcu); + return error; +} + +static void ims_pcu_destroy_application_mode(struct ims_pcu *pcu) +{ + if (pcu->setup_complete) { + pcu->setup_complete = false; + mb(); /* make sure flag setting is not reordered */ + + if (pcu->gamepad) + ims_pcu_destroy_gamepad(pcu); + ims_pcu_destroy_buttons(pcu); + ims_pcu_destroy_backlight(pcu); + } +} + +static int ims_pcu_init_bootloader_mode(struct ims_pcu *pcu) +{ + int error; + + error = ims_pcu_execute_bl_command(pcu, QUERY_DEVICE, NULL, 0, + IMS_PCU_CMD_RESPONSE_TIMEOUT); + if (error) { + dev_err(pcu->dev, "Bootloader does not respond, aborting\n"); + return error; + } + + pcu->fw_start_addr = + get_unaligned_le32(&pcu->cmd_buf[IMS_PCU_DATA_OFFSET + 11]); + pcu->fw_end_addr = + get_unaligned_le32(&pcu->cmd_buf[IMS_PCU_DATA_OFFSET + 15]); + + dev_info(pcu->dev, + "Device is in bootloader mode (addr 0x%08x-0x%08x), requesting firmware\n", + pcu->fw_start_addr, pcu->fw_end_addr); + + error = request_firmware_nowait(THIS_MODULE, true, + IMS_PCU_FIRMWARE_NAME, + pcu->dev, GFP_KERNEL, pcu, + ims_pcu_process_async_firmware); + if (error) { + /* This error is not fatal, let userspace have another chance */ + complete(&pcu->async_firmware_done); + } + + return 0; +} + +static void ims_pcu_destroy_bootloader_mode(struct ims_pcu *pcu) +{ + /* Make sure our initial firmware request has completed */ + wait_for_completion(&pcu->async_firmware_done); +} + +#define IMS_PCU_APPLICATION_MODE 0 +#define IMS_PCU_BOOTLOADER_MODE 1 + +static struct usb_driver ims_pcu_driver; + +static int ims_pcu_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct ims_pcu *pcu; + int error; + + pcu = kzalloc(sizeof(struct ims_pcu), GFP_KERNEL); + if (!pcu) + return -ENOMEM; + + pcu->dev = &intf->dev; + pcu->udev = udev; + pcu->bootloader_mode = id->driver_info == IMS_PCU_BOOTLOADER_MODE; + mutex_init(&pcu->cmd_mutex); + init_completion(&pcu->cmd_done); + init_completion(&pcu->async_firmware_done); + + error = ims_pcu_parse_cdc_data(intf, pcu); + if (error) + goto err_free_mem; + + error = usb_driver_claim_interface(&ims_pcu_driver, + pcu->data_intf, pcu); + if (error) { + dev_err(&intf->dev, + "Unable to claim corresponding data interface: %d\n", + error); + goto err_free_mem; + } + + usb_set_intfdata(pcu->ctrl_intf, pcu); + usb_set_intfdata(pcu->data_intf, pcu); + + error = ims_pcu_buffers_alloc(pcu); + if (error) + goto err_unclaim_intf; + + error = ims_pcu_start_io(pcu); + if (error) + goto err_free_buffers; + + error = ims_pcu_line_setup(pcu); + if (error) + goto err_stop_io; + + error = sysfs_create_group(&intf->dev.kobj, &ims_pcu_attr_group); + if (error) + goto err_stop_io; + + error = pcu->bootloader_mode ? + ims_pcu_init_bootloader_mode(pcu) : + ims_pcu_init_application_mode(pcu); + if (error) + goto err_remove_sysfs; + + return 0; + +err_remove_sysfs: + sysfs_remove_group(&intf->dev.kobj, &ims_pcu_attr_group); +err_stop_io: + ims_pcu_stop_io(pcu); +err_free_buffers: + ims_pcu_buffers_free(pcu); +err_unclaim_intf: + usb_driver_release_interface(&ims_pcu_driver, pcu->data_intf); +err_free_mem: + kfree(pcu); + return error; +} + +static void ims_pcu_disconnect(struct usb_interface *intf) +{ + struct ims_pcu *pcu = usb_get_intfdata(intf); + struct usb_host_interface *alt = intf->cur_altsetting; + + usb_set_intfdata(intf, NULL); + + /* + * See if we are dealing with control or data interface. The cleanup + * happens when we unbind primary (control) interface. + */ + if (alt->desc.bInterfaceClass != USB_CLASS_COMM) + return; + + sysfs_remove_group(&intf->dev.kobj, &ims_pcu_attr_group); + + ims_pcu_stop_io(pcu); + + if (pcu->bootloader_mode) + ims_pcu_destroy_bootloader_mode(pcu); + else + ims_pcu_destroy_application_mode(pcu); + + ims_pcu_buffers_free(pcu); + kfree(pcu); +} + +#ifdef CONFIG_PM +static int ims_pcu_suspend(struct usb_interface *intf, + pm_message_t message) +{ + struct ims_pcu *pcu = usb_get_intfdata(intf); + struct usb_host_interface *alt = intf->cur_altsetting; + + if (alt->desc.bInterfaceClass == USB_CLASS_COMM) + ims_pcu_stop_io(pcu); + + return 0; +} + +static int ims_pcu_resume(struct usb_interface *intf) +{ + struct ims_pcu *pcu = usb_get_intfdata(intf); + struct usb_host_interface *alt = intf->cur_altsetting; + int retval = 0; + + if (alt->desc.bInterfaceClass == USB_CLASS_COMM) { + retval = ims_pcu_start_io(pcu); + if (retval == 0) + retval = ims_pcu_line_setup(pcu); + } + + return retval; +} +#endif + +static const struct usb_device_id ims_pcu_id_table[] = { + { + USB_DEVICE_AND_INTERFACE_INFO(0x04d8, 0x0082, + USB_CLASS_COMM, + USB_CDC_SUBCLASS_ACM, + USB_CDC_ACM_PROTO_AT_V25TER), + .driver_info = IMS_PCU_APPLICATION_MODE, + }, + { + USB_DEVICE_AND_INTERFACE_INFO(0x04d8, 0x0083, + USB_CLASS_COMM, + USB_CDC_SUBCLASS_ACM, + USB_CDC_ACM_PROTO_AT_V25TER), + .driver_info = IMS_PCU_BOOTLOADER_MODE, + }, + { } +}; + +static struct usb_driver ims_pcu_driver = { + .name = "ims_pcu", + .id_table = ims_pcu_id_table, + .probe = ims_pcu_probe, + .disconnect = ims_pcu_disconnect, +#ifdef CONFIG_PM + .suspend = ims_pcu_suspend, + .resume = ims_pcu_resume, + .reset_resume = ims_pcu_resume, +#endif +}; + +module_usb_driver(ims_pcu_driver); + +MODULE_DESCRIPTION("IMS Passenger Control Unit driver"); +MODULE_AUTHOR("Dmitry Torokhov <dmitry.torokhov@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/max8925_onkey.c b/drivers/input/misc/max8925_onkey.c index 369a39de4ff3..f9179b2585a9 100644 --- a/drivers/input/misc/max8925_onkey.c +++ b/drivers/input/misc/max8925_onkey.c @@ -100,9 +100,6 @@ static int max8925_onkey_probe(struct platform_device *pdev) input->dev.parent = &pdev->dev; input_set_capability(input, EV_KEY, KEY_POWER); - irq[0] += chip->irq_base; - irq[1] += chip->irq_base; - error = request_threaded_irq(irq[0], NULL, max8925_onkey_handler, IRQF_ONESHOT, "onkey-down", info); if (error < 0) { diff --git a/drivers/input/misc/mma8450.c b/drivers/input/misc/mma8450.c index 480557f14f23..f3309696d053 100644 --- a/drivers/input/misc/mma8450.c +++ b/drivers/input/misc/mma8450.c @@ -123,9 +123,9 @@ static void mma8450_poll(struct input_polled_dev *dev) if (ret < 0) return; - x = ((buf[1] << 4) & 0xff0) | (buf[0] & 0xf); - y = ((buf[3] << 4) & 0xff0) | (buf[2] & 0xf); - z = ((buf[5] << 4) & 0xff0) | (buf[4] & 0xf); + x = ((int)(s8)buf[1] << 4) | (buf[0] & 0xf); + y = ((int)(s8)buf[3] << 4) | (buf[2] & 0xf); + z = ((int)(s8)buf[5] << 4) | (buf[4] & 0xf); input_report_abs(dev->input, ABS_X, x); input_report_abs(dev->input, ABS_Y, y); diff --git a/drivers/input/misc/twl4030-pwrbutton.c b/drivers/input/misc/twl4030-pwrbutton.c index 27c2bc8aa890..b9a05fda03e4 100644 --- a/drivers/input/misc/twl4030-pwrbutton.c +++ b/drivers/input/misc/twl4030-pwrbutton.c @@ -114,18 +114,8 @@ static struct platform_driver twl4030_pwrbutton_driver = { }, }; -static int __init twl4030_pwrbutton_init(void) -{ - return platform_driver_probe(&twl4030_pwrbutton_driver, +module_platform_driver_probe(twl4030_pwrbutton_driver, twl4030_pwrbutton_probe); -} -module_init(twl4030_pwrbutton_init); - -static void __exit twl4030_pwrbutton_exit(void) -{ - platform_driver_unregister(&twl4030_pwrbutton_driver); -} -module_exit(twl4030_pwrbutton_exit); MODULE_ALIAS("platform:twl4030_pwrbutton"); MODULE_DESCRIPTION("Triton2 Power Button"); diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 0238e0e14335..7c5d72a6a26a 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -1013,8 +1013,8 @@ static int alps_rpt_cmd(struct psmouse *psmouse, int init_command, if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) return -EIO; - psmouse_dbg(psmouse, "%2.2X report: %2.2x %2.2x %2.2x\n", - repeated_command, param[0], param[1], param[2]); + psmouse_dbg(psmouse, "%2.2X report: %3ph\n", + repeated_command, param); return 0; } @@ -1274,9 +1274,7 @@ static int alps_setup_trackstick_v3(struct psmouse *psmouse, int reg_base) psmouse_warn(psmouse, "trackstick E7 report failed\n"); ret = -ENODEV; } else { - psmouse_dbg(psmouse, - "trackstick E7 report: %2.2x %2.2x %2.2x\n", - param[0], param[1], param[2]); + psmouse_dbg(psmouse, "trackstick E7 report: %3ph\n", param); /* * Not sure what this does, but it is absolutely @@ -1323,6 +1321,7 @@ static int alps_hw_init_v3(struct psmouse *psmouse) reg_val = alps_probe_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE); if (reg_val == -EIO) goto error; + if (reg_val == 0 && alps_setup_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE) == -EIO) goto error; @@ -1676,8 +1675,7 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv) } psmouse_info(psmouse, - "Unknown ALPS touchpad: E7=%2.2x %2.2x %2.2x, EC=%2.2x %2.2x %2.2x\n", - e7[0], e7[1], e7[2], ec[0], ec[1], ec[2]); + "Unknown ALPS touchpad: E7=%3ph, EC=%3ph\n", e7, ec); return -EINVAL; } diff --git a/drivers/input/mouse/amimouse.c b/drivers/input/mouse/amimouse.c index 5fa99341a39d..b55d5af217a7 100644 --- a/drivers/input/mouse/amimouse.c +++ b/drivers/input/mouse/amimouse.c @@ -146,18 +146,6 @@ static struct platform_driver amimouse_driver = { }, }; -static int __init amimouse_init(void) -{ - return platform_driver_probe(&amimouse_driver, amimouse_probe); -} - -module_init(amimouse_init); - -static void __exit amimouse_exit(void) -{ - platform_driver_unregister(&amimouse_driver); -} - -module_exit(amimouse_exit); +module_platform_driver_probe(amimouse_driver, amimouse_probe); MODULE_ALIAS("platform:amiga-mouse"); diff --git a/drivers/input/mouse/trackpoint.c b/drivers/input/mouse/trackpoint.c index f3102494237d..ca843b6cf6bd 100644 --- a/drivers/input/mouse/trackpoint.c +++ b/drivers/input/mouse/trackpoint.c @@ -20,9 +20,34 @@ #include "trackpoint.h" /* + * Power-on Reset: Resets all trackpoint parameters, including RAM values, + * to defaults. + * Returns zero on success, non-zero on failure. + */ +static int trackpoint_power_on_reset(struct ps2dev *ps2dev) +{ + unsigned char results[2]; + int tries = 0; + + /* Issue POR command, and repeat up to once if 0xFC00 received */ + do { + if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) || + ps2_command(ps2dev, results, MAKE_PS2_CMD(0, 2, TP_POR))) + return -1; + } while (results[0] == 0xFC && results[1] == 0x00 && ++tries < 2); + + /* Check for success response -- 0xAA00 */ + if (results[0] != 0xAA || results[1] != 0x00) + return -1; + + return 0; +} + +/* * Device IO: read, write and toggle bit */ -static int trackpoint_read(struct ps2dev *ps2dev, unsigned char loc, unsigned char *results) +static int trackpoint_read(struct ps2dev *ps2dev, + unsigned char loc, unsigned char *results) { if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) || ps2_command(ps2dev, results, MAKE_PS2_CMD(0, 1, loc))) { @@ -32,7 +57,8 @@ static int trackpoint_read(struct ps2dev *ps2dev, unsigned char loc, unsigned ch return 0; } -static int trackpoint_write(struct ps2dev *ps2dev, unsigned char loc, unsigned char val) +static int trackpoint_write(struct ps2dev *ps2dev, + unsigned char loc, unsigned char val) { if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) || ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_WRITE_MEM)) || @@ -44,7 +70,8 @@ static int trackpoint_write(struct ps2dev *ps2dev, unsigned char loc, unsigned c return 0; } -static int trackpoint_toggle_bit(struct ps2dev *ps2dev, unsigned char loc, unsigned char mask) +static int trackpoint_toggle_bit(struct ps2dev *ps2dev, + unsigned char loc, unsigned char mask) { /* Bad things will happen if the loc param isn't in this range */ if (loc < 0x20 || loc >= 0x2F) @@ -60,6 +87,18 @@ static int trackpoint_toggle_bit(struct ps2dev *ps2dev, unsigned char loc, unsig return 0; } +static int trackpoint_update_bit(struct ps2dev *ps2dev, unsigned char loc, + unsigned char mask, unsigned char value) +{ + int retval = 0; + unsigned char data; + + trackpoint_read(ps2dev, loc, &data); + if (((data & mask) == mask) != !!value) + retval = trackpoint_toggle_bit(ps2dev, loc, mask); + + return retval; +} /* * Trackpoint-specific attributes @@ -69,6 +108,7 @@ struct trackpoint_attr_data { unsigned char command; unsigned char mask; unsigned char inverted; + unsigned char power_on_default; }; static ssize_t trackpoint_show_int_attr(struct psmouse *psmouse, void *data, char *buf) @@ -102,10 +142,11 @@ static ssize_t trackpoint_set_int_attr(struct psmouse *psmouse, void *data, return count; } -#define TRACKPOINT_INT_ATTR(_name, _command) \ +#define TRACKPOINT_INT_ATTR(_name, _command, _default) \ static struct trackpoint_attr_data trackpoint_attr_##_name = { \ .field_offset = offsetof(struct trackpoint_data, _name), \ .command = _command, \ + .power_on_default = _default, \ }; \ PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \ &trackpoint_attr_##_name, \ @@ -139,31 +180,60 @@ static ssize_t trackpoint_set_bit_attr(struct psmouse *psmouse, void *data, } -#define TRACKPOINT_BIT_ATTR(_name, _command, _mask, _inv) \ - static struct trackpoint_attr_data trackpoint_attr_##_name = { \ - .field_offset = offsetof(struct trackpoint_data, _name), \ - .command = _command, \ - .mask = _mask, \ - .inverted = _inv, \ - }; \ - PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \ - &trackpoint_attr_##_name, \ - trackpoint_show_int_attr, trackpoint_set_bit_attr) - -TRACKPOINT_INT_ATTR(sensitivity, TP_SENS); -TRACKPOINT_INT_ATTR(speed, TP_SPEED); -TRACKPOINT_INT_ATTR(inertia, TP_INERTIA); -TRACKPOINT_INT_ATTR(reach, TP_REACH); -TRACKPOINT_INT_ATTR(draghys, TP_DRAGHYS); -TRACKPOINT_INT_ATTR(mindrag, TP_MINDRAG); -TRACKPOINT_INT_ATTR(thresh, TP_THRESH); -TRACKPOINT_INT_ATTR(upthresh, TP_UP_THRESH); -TRACKPOINT_INT_ATTR(ztime, TP_Z_TIME); -TRACKPOINT_INT_ATTR(jenks, TP_JENKS_CURV); - -TRACKPOINT_BIT_ATTR(press_to_select, TP_TOGGLE_PTSON, TP_MASK_PTSON, 0); -TRACKPOINT_BIT_ATTR(skipback, TP_TOGGLE_SKIPBACK, TP_MASK_SKIPBACK, 0); -TRACKPOINT_BIT_ATTR(ext_dev, TP_TOGGLE_EXT_DEV, TP_MASK_EXT_DEV, 1); +#define TRACKPOINT_BIT_ATTR(_name, _command, _mask, _inv, _default) \ +static struct trackpoint_attr_data trackpoint_attr_##_name = { \ + .field_offset = offsetof(struct trackpoint_data, \ + _name), \ + .command = _command, \ + .mask = _mask, \ + .inverted = _inv, \ + .power_on_default = _default, \ + }; \ +PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \ + &trackpoint_attr_##_name, \ + trackpoint_show_int_attr, trackpoint_set_bit_attr) + +#define TRACKPOINT_UPDATE_BIT(_psmouse, _tp, _name) \ +do { \ + struct trackpoint_attr_data *_attr = &trackpoint_attr_##_name; \ + \ + trackpoint_update_bit(&_psmouse->ps2dev, \ + _attr->command, _attr->mask, _tp->_name); \ +} while (0) + +#define TRACKPOINT_UPDATE(_power_on, _psmouse, _tp, _name) \ +do { \ + if (!_power_on || \ + _tp->_name != trackpoint_attr_##_name.power_on_default) { \ + if (!trackpoint_attr_##_name.mask) \ + trackpoint_write(&_psmouse->ps2dev, \ + trackpoint_attr_##_name.command, \ + _tp->_name); \ + else \ + TRACKPOINT_UPDATE_BIT(_psmouse, _tp, _name); \ + } \ +} while (0) + +#define TRACKPOINT_SET_POWER_ON_DEFAULT(_tp, _name) \ + (_tp->_name = trackpoint_attr_##_name.power_on_default) + +TRACKPOINT_INT_ATTR(sensitivity, TP_SENS, TP_DEF_SENS); +TRACKPOINT_INT_ATTR(speed, TP_SPEED, TP_DEF_SPEED); +TRACKPOINT_INT_ATTR(inertia, TP_INERTIA, TP_DEF_INERTIA); +TRACKPOINT_INT_ATTR(reach, TP_REACH, TP_DEF_REACH); +TRACKPOINT_INT_ATTR(draghys, TP_DRAGHYS, TP_DEF_DRAGHYS); +TRACKPOINT_INT_ATTR(mindrag, TP_MINDRAG, TP_DEF_MINDRAG); +TRACKPOINT_INT_ATTR(thresh, TP_THRESH, TP_DEF_THRESH); +TRACKPOINT_INT_ATTR(upthresh, TP_UP_THRESH, TP_DEF_UP_THRESH); +TRACKPOINT_INT_ATTR(ztime, TP_Z_TIME, TP_DEF_Z_TIME); +TRACKPOINT_INT_ATTR(jenks, TP_JENKS_CURV, TP_DEF_JENKS_CURV); + +TRACKPOINT_BIT_ATTR(press_to_select, TP_TOGGLE_PTSON, TP_MASK_PTSON, 0, + TP_DEF_PTSON); +TRACKPOINT_BIT_ATTR(skipback, TP_TOGGLE_SKIPBACK, TP_MASK_SKIPBACK, 0, + TP_DEF_SKIPBACK); +TRACKPOINT_BIT_ATTR(ext_dev, TP_TOGGLE_EXT_DEV, TP_MASK_EXT_DEV, 1, + TP_DEF_EXT_DEV); static struct attribute *trackpoint_attrs[] = { &psmouse_attr_sensitivity.dattr.attr, @@ -202,73 +272,72 @@ static int trackpoint_start_protocol(struct psmouse *psmouse, unsigned char *fir return 0; } -static int trackpoint_sync(struct psmouse *psmouse) +/* + * Write parameters to trackpad. + * in_power_on_state: Set to true if TP is in default / power-on state (ex. if + * power-on reset was run). If so, values will only be + * written to TP if they differ from power-on default. + */ +static int trackpoint_sync(struct psmouse *psmouse, bool in_power_on_state) { struct trackpoint_data *tp = psmouse->private; - unsigned char toggle; - - /* Disable features that may make device unusable with this driver */ - trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_TWOHAND, &toggle); - if (toggle & TP_MASK_TWOHAND) - trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_TWOHAND, TP_MASK_TWOHAND); - - trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_SOURCE_TAG, &toggle); - if (toggle & TP_MASK_SOURCE_TAG) - trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_SOURCE_TAG, TP_MASK_SOURCE_TAG); - - trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_MB, &toggle); - if (toggle & TP_MASK_MB) - trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_MB, TP_MASK_MB); - - /* Push the config to the device */ - trackpoint_write(&psmouse->ps2dev, TP_SENS, tp->sensitivity); - trackpoint_write(&psmouse->ps2dev, TP_INERTIA, tp->inertia); - trackpoint_write(&psmouse->ps2dev, TP_SPEED, tp->speed); - - trackpoint_write(&psmouse->ps2dev, TP_REACH, tp->reach); - trackpoint_write(&psmouse->ps2dev, TP_DRAGHYS, tp->draghys); - trackpoint_write(&psmouse->ps2dev, TP_MINDRAG, tp->mindrag); - - trackpoint_write(&psmouse->ps2dev, TP_THRESH, tp->thresh); - trackpoint_write(&psmouse->ps2dev, TP_UP_THRESH, tp->upthresh); - trackpoint_write(&psmouse->ps2dev, TP_Z_TIME, tp->ztime); - trackpoint_write(&psmouse->ps2dev, TP_JENKS_CURV, tp->jenks); + if (!in_power_on_state) { + /* + * Disable features that may make device unusable + * with this driver. + */ + trackpoint_update_bit(&psmouse->ps2dev, TP_TOGGLE_TWOHAND, + TP_MASK_TWOHAND, TP_DEF_TWOHAND); - trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_PTSON, &toggle); - if (((toggle & TP_MASK_PTSON) == TP_MASK_PTSON) != tp->press_to_select) - trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_PTSON, TP_MASK_PTSON); + trackpoint_update_bit(&psmouse->ps2dev, TP_TOGGLE_SOURCE_TAG, + TP_MASK_SOURCE_TAG, TP_DEF_SOURCE_TAG); - trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_SKIPBACK, &toggle); - if (((toggle & TP_MASK_SKIPBACK) == TP_MASK_SKIPBACK) != tp->skipback) - trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_SKIPBACK, TP_MASK_SKIPBACK); + trackpoint_update_bit(&psmouse->ps2dev, TP_TOGGLE_MB, + TP_MASK_MB, TP_DEF_MB); + } - trackpoint_read(&psmouse->ps2dev, TP_TOGGLE_EXT_DEV, &toggle); - if (((toggle & TP_MASK_EXT_DEV) == TP_MASK_EXT_DEV) != tp->ext_dev) - trackpoint_toggle_bit(&psmouse->ps2dev, TP_TOGGLE_EXT_DEV, TP_MASK_EXT_DEV); + /* + * These properties can be changed in this driver. Only + * configure them if the values are non-default or if the TP is in + * an unknown state. + */ + TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, sensitivity); + TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, inertia); + TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, speed); + TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, reach); + TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, draghys); + TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, mindrag); + TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, thresh); + TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, upthresh); + TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, ztime); + TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, jenks); + + /* toggles */ + TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, press_to_select); + TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, skipback); + TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, ext_dev); return 0; } static void trackpoint_defaults(struct trackpoint_data *tp) { - tp->press_to_select = TP_DEF_PTSON; - tp->sensitivity = TP_DEF_SENS; - tp->speed = TP_DEF_SPEED; - tp->reach = TP_DEF_REACH; - - tp->draghys = TP_DEF_DRAGHYS; - tp->mindrag = TP_DEF_MINDRAG; - - tp->thresh = TP_DEF_THRESH; - tp->upthresh = TP_DEF_UP_THRESH; - - tp->ztime = TP_DEF_Z_TIME; - tp->jenks = TP_DEF_JENKS_CURV; - - tp->inertia = TP_DEF_INERTIA; - tp->skipback = TP_DEF_SKIPBACK; - tp->ext_dev = TP_DEF_EXT_DEV; + TRACKPOINT_SET_POWER_ON_DEFAULT(tp, sensitivity); + TRACKPOINT_SET_POWER_ON_DEFAULT(tp, speed); + TRACKPOINT_SET_POWER_ON_DEFAULT(tp, reach); + TRACKPOINT_SET_POWER_ON_DEFAULT(tp, draghys); + TRACKPOINT_SET_POWER_ON_DEFAULT(tp, mindrag); + TRACKPOINT_SET_POWER_ON_DEFAULT(tp, thresh); + TRACKPOINT_SET_POWER_ON_DEFAULT(tp, upthresh); + TRACKPOINT_SET_POWER_ON_DEFAULT(tp, ztime); + TRACKPOINT_SET_POWER_ON_DEFAULT(tp, jenks); + TRACKPOINT_SET_POWER_ON_DEFAULT(tp, inertia); + + /* toggles */ + TRACKPOINT_SET_POWER_ON_DEFAULT(tp, press_to_select); + TRACKPOINT_SET_POWER_ON_DEFAULT(tp, skipback); + TRACKPOINT_SET_POWER_ON_DEFAULT(tp, ext_dev); } static void trackpoint_disconnect(struct psmouse *psmouse) @@ -281,10 +350,13 @@ static void trackpoint_disconnect(struct psmouse *psmouse) static int trackpoint_reconnect(struct psmouse *psmouse) { + int reset_fail; + if (trackpoint_start_protocol(psmouse, NULL)) return -1; - if (trackpoint_sync(psmouse)) + reset_fail = trackpoint_power_on_reset(&psmouse->ps2dev); + if (trackpoint_sync(psmouse, !reset_fail)) return -1; return 0; @@ -322,7 +394,12 @@ int trackpoint_detect(struct psmouse *psmouse, bool set_properties) __set_bit(BTN_MIDDLE, psmouse->dev->keybit); trackpoint_defaults(psmouse->private); - trackpoint_sync(psmouse); + + error = trackpoint_power_on_reset(&psmouse->ps2dev); + + /* Write defaults to TP only if reset fails. */ + if (error) + trackpoint_sync(psmouse, false); error = sysfs_create_group(&ps2dev->serio->dev.kobj, &trackpoint_attr_group); if (error) { diff --git a/drivers/input/mouse/trackpoint.h b/drivers/input/mouse/trackpoint.h index e558a7096618..ecd0547964a5 100644 --- a/drivers/input/mouse/trackpoint.h +++ b/drivers/input/mouse/trackpoint.h @@ -126,6 +126,8 @@ #define TP_DEF_PTSON 0x00 #define TP_DEF_SKIPBACK 0x00 #define TP_DEF_EXT_DEV 0x00 /* 0 means enabled */ +#define TP_DEF_TWOHAND 0x00 +#define TP_DEF_SOURCE_TAG 0x00 #define MAKE_PS2_CMD(params, results, cmd) ((params<<12) | (results<<8) | (cmd)) @@ -136,9 +138,9 @@ struct trackpoint_data unsigned char thresh, upthresh; unsigned char ztime, jenks; + /* toggles */ unsigned char press_to_select; unsigned char skipback; - unsigned char ext_dev; }; diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index 560c243bfcaf..aebfe3ecb945 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -22,7 +22,7 @@ config SERIO_I8042 tristate "i8042 PC Keyboard controller" if EXPERT || !X86 default y depends on !PARISC && (!ARM || ARCH_SHARK || FOOTBRIDGE_HOST) && \ - (!SUPERH || SH_CAYMAN) && !M68K && !BLACKFIN + (!SUPERH || SH_CAYMAN) && !M68K && !BLACKFIN && !S390 help i8042 is the chip over which the standard AT keyboard and PS/2 mouse are connected to the computer. If you use these devices, @@ -36,6 +36,7 @@ config SERIO_I8042 config SERIO_SERPORT tristate "Serial port line discipline" default y + depends on TTY help Say Y here if you plan to use an input device (mouse, joystick, tablet, 6dof) that communicates over the RS232 serial (COM) port. @@ -244,4 +245,14 @@ config SERIO_ARC_PS2 To compile this driver as a module, choose M here; the module will be called arc_ps2. +config SERIO_APBPS2 + tristate "GRLIB APBPS2 PS/2 keyboard/mouse controller" + depends on OF + help + Say Y here if you want support for GRLIB APBPS2 peripherals used + to connect to PS/2 keyboard and/or mouse. + + To compile this driver as a module, choose M here: the module will + be called apbps2. + endif diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile index 4b0c8f84f1c1..8edb36c2cdb4 100644 --- a/drivers/input/serio/Makefile +++ b/drivers/input/serio/Makefile @@ -26,3 +26,4 @@ obj-$(CONFIG_SERIO_AMS_DELTA) += ams_delta_serio.o obj-$(CONFIG_SERIO_XILINX_XPS_PS2) += xilinx_ps2.o obj-$(CONFIG_SERIO_ALTERA_PS2) += altera_ps2.o obj-$(CONFIG_SERIO_ARC_PS2) += arc_ps2.o +obj-$(CONFIG_SERIO_APBPS2) += apbps2.o diff --git a/drivers/input/serio/apbps2.c b/drivers/input/serio/apbps2.c new file mode 100644 index 000000000000..17e01a807ddc --- /dev/null +++ b/drivers/input/serio/apbps2.c @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2013 Aeroflex Gaisler + * + * This driver supports the APBPS2 PS/2 core available in the GRLIB + * VHDL IP core library. + * + * Full documentation of the APBPS2 core can be found here: + * http://www.gaisler.com/products/grlib/grip.pdf + * + * See "Documentation/devicetree/bindings/input/ps2keyb-mouse-apbps2.txt" for + * information on open firmware properties. + * + * 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. + * + * Contributors: Daniel Hellstrom <daniel@gaisler.com> + */ +#include <linux/platform_device.h> +#include <linux/of_device.h> +#include <linux/module.h> +#include <linux/serio.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/of_irq.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/io.h> + +struct apbps2_regs { + u32 __iomem data; /* 0x00 */ + u32 __iomem status; /* 0x04 */ + u32 __iomem ctrl; /* 0x08 */ + u32 __iomem reload; /* 0x0c */ +}; + +#define APBPS2_STATUS_DR (1<<0) +#define APBPS2_STATUS_PE (1<<1) +#define APBPS2_STATUS_FE (1<<2) +#define APBPS2_STATUS_KI (1<<3) +#define APBPS2_STATUS_RF (1<<4) +#define APBPS2_STATUS_TF (1<<5) +#define APBPS2_STATUS_TCNT (0x1f<<22) +#define APBPS2_STATUS_RCNT (0x1f<<27) + +#define APBPS2_CTRL_RE (1<<0) +#define APBPS2_CTRL_TE (1<<1) +#define APBPS2_CTRL_RI (1<<2) +#define APBPS2_CTRL_TI (1<<3) + +struct apbps2_priv { + struct serio *io; + struct apbps2_regs *regs; +}; + +static int apbps2_idx; + +static irqreturn_t apbps2_isr(int irq, void *dev_id) +{ + struct apbps2_priv *priv = dev_id; + unsigned long status, data, rxflags; + irqreturn_t ret = IRQ_NONE; + + while ((status = ioread32be(&priv->regs->status)) & APBPS2_STATUS_DR) { + data = ioread32be(&priv->regs->data); + rxflags = (status & APBPS2_STATUS_PE) ? SERIO_PARITY : 0; + rxflags |= (status & APBPS2_STATUS_FE) ? SERIO_FRAME : 0; + + /* clear error bits? */ + if (rxflags) + iowrite32be(0, &priv->regs->status); + + serio_interrupt(priv->io, data, rxflags); + + ret = IRQ_HANDLED; + } + + return ret; +} + +static int apbps2_write(struct serio *io, unsigned char val) +{ + struct apbps2_priv *priv = io->port_data; + unsigned int tleft = 10000; /* timeout in 100ms */ + + /* delay until PS/2 controller has room for more chars */ + while ((ioread32be(&priv->regs->status) & APBPS2_STATUS_TF) && tleft--) + udelay(10); + + if ((ioread32be(&priv->regs->status) & APBPS2_STATUS_TF) == 0) { + iowrite32be(val, &priv->regs->data); + + iowrite32be(APBPS2_CTRL_RE | APBPS2_CTRL_RI | APBPS2_CTRL_TE, + &priv->regs->ctrl); + return 0; + } + + return -ETIMEDOUT; +} + +static int apbps2_open(struct serio *io) +{ + struct apbps2_priv *priv = io->port_data; + int limit; + unsigned long tmp; + + /* clear error flags */ + iowrite32be(0, &priv->regs->status); + + /* Clear old data if available (unlikely) */ + limit = 1024; + while ((ioread32be(&priv->regs->status) & APBPS2_STATUS_DR) && --limit) + tmp = ioread32be(&priv->regs->data); + + /* Enable reciever and it's interrupt */ + iowrite32be(APBPS2_CTRL_RE | APBPS2_CTRL_RI, &priv->regs->ctrl); + + return 0; +} + +static void apbps2_close(struct serio *io) +{ + struct apbps2_priv *priv = io->port_data; + + /* stop interrupts at PS/2 HW level */ + iowrite32be(0, &priv->regs->ctrl); +} + +/* Initialize one APBPS2 PS/2 core */ +static int apbps2_of_probe(struct platform_device *ofdev) +{ + struct apbps2_priv *priv; + int irq, err; + u32 freq_hz; + struct resource *res; + + priv = devm_kzalloc(&ofdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(&ofdev->dev, "memory allocation failed\n"); + return -ENOMEM; + } + + /* Find Device Address */ + res = platform_get_resource(ofdev, IORESOURCE_MEM, 0); + priv->regs = devm_ioremap_resource(&ofdev->dev, res); + if (IS_ERR(priv->regs)) + return PTR_ERR(priv->regs); + + /* Reset hardware, disable interrupt */ + iowrite32be(0, &priv->regs->ctrl); + + /* IRQ */ + irq = irq_of_parse_and_map(ofdev->dev.of_node, 0); + err = devm_request_irq(&ofdev->dev, irq, apbps2_isr, + IRQF_SHARED, "apbps2", priv); + if (err) { + dev_err(&ofdev->dev, "request IRQ%d failed\n", irq); + return err; + } + + /* Get core frequency */ + if (of_property_read_u32(ofdev->dev.of_node, "freq", &freq_hz)) { + dev_err(&ofdev->dev, "unable to get core frequency\n"); + return -EINVAL; + } + + /* Set reload register to core freq in kHz/10 */ + iowrite32be(freq_hz / 10000, &priv->regs->reload); + + priv->io = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!priv->io) + return -ENOMEM; + + priv->io->id.type = SERIO_8042; + priv->io->open = apbps2_open; + priv->io->close = apbps2_close; + priv->io->write = apbps2_write; + priv->io->port_data = priv; + strlcpy(priv->io->name, "APBPS2 PS/2", sizeof(priv->io->name)); + snprintf(priv->io->phys, sizeof(priv->io->phys), + "apbps2_%d", apbps2_idx++); + + dev_info(&ofdev->dev, "irq = %d, base = 0x%p\n", irq, priv->regs); + + serio_register_port(priv->io); + + platform_set_drvdata(ofdev, priv); + + return 0; +} + +static int apbps2_of_remove(struct platform_device *of_dev) +{ + struct apbps2_priv *priv = platform_get_drvdata(of_dev); + + serio_unregister_port(priv->io); + + return 0; +} + +static struct of_device_id apbps2_of_match[] = { + { .name = "GAISLER_APBPS2", }, + { .name = "01_060", }, + {} +}; + +MODULE_DEVICE_TABLE(of, apbps2_of_match); + +static struct platform_driver apbps2_of_driver = { + .driver = { + .name = "grlib-apbps2", + .owner = THIS_MODULE, + .of_match_table = apbps2_of_match, + }, + .probe = apbps2_of_probe, + .remove = apbps2_of_remove, +}; + +module_platform_driver(apbps2_of_driver); + +MODULE_AUTHOR("Aeroflex Gaisler AB."); +MODULE_DESCRIPTION("GRLIB APBPS2 PS/2 serial I/O"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/serio/arc_ps2.c b/drivers/input/serio/arc_ps2.c index b571eb3e4efc..3fb7727c8ea5 100644 --- a/drivers/input/serio/arc_ps2.c +++ b/drivers/input/serio/arc_ps2.c @@ -8,11 +8,13 @@ * Driver is originally developed by Pavel Sokolov <psokolov@synopsys.com> */ +#include <linux/err.h> #include <linux/module.h> #include <linux/interrupt.h> #include <linux/input.h> #include <linux/serio.h> #include <linux/platform_device.h> +#include <linux/of.h> #include <linux/io.h> #include <linux/kernel.h> #include <linux/slab.h> @@ -206,9 +208,9 @@ static int arc_ps2_probe(struct platform_device *pdev) return -ENOMEM; } - arc_ps2->addr = devm_request_and_ioremap(&pdev->dev, res); - if (!arc_ps2->addr) - return -EBUSY; + arc_ps2->addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(arc_ps2->addr)) + return PTR_ERR(arc_ps2->addr); dev_info(&pdev->dev, "irq = %d, address = 0x%p, ports = %i\n", irq, arc_ps2->addr, ARC_PS2_PORTS); @@ -258,10 +260,19 @@ static int arc_ps2_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id arc_ps2_match[] = { + { .compatible = "snps,arc_ps2" }, + { }, +}; +MODULE_DEVICE_TABLE(of, arc_ps2_match); +#endif + static struct platform_driver arc_ps2_driver = { .driver = { - .name = "arc_ps2", - .owner = THIS_MODULE, + .name = "arc_ps2", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(arc_ps2_match), }, .probe = arc_ps2_probe, .remove = arc_ps2_remove, diff --git a/drivers/input/serio/at32psif.c b/drivers/input/serio/at32psif.c index 36e799c31f5e..190ce35af7df 100644 --- a/drivers/input/serio/at32psif.c +++ b/drivers/input/serio/at32psif.c @@ -359,18 +359,7 @@ static struct platform_driver psif_driver = { }, }; -static int __init psif_init(void) -{ - return platform_driver_probe(&psif_driver, psif_probe); -} - -static void __exit psif_exit(void) -{ - platform_driver_unregister(&psif_driver); -} - -module_init(psif_init); -module_exit(psif_exit); +module_platform_driver_probe(psif_driver, psif_probe); MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>"); MODULE_DESCRIPTION("Atmel AVR32 PSIF PS/2 driver"); diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index d6cc77a53c7e..5f306f79da0c 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -921,6 +921,7 @@ static int __init i8042_platform_init(void) int retval; #ifdef CONFIG_X86 + u8 a20_on = 0xdf; /* Just return if pre-detection shows no i8042 controller exist */ if (!x86_platform.i8042_detect()) return -ENODEV; @@ -960,6 +961,14 @@ static int __init i8042_platform_init(void) if (dmi_check_system(i8042_dmi_dritek_table)) i8042_dritek = true; + + /* + * A20 was already enabled during early kernel init. But some buggy + * BIOSes (in MSI Laptops) require A20 to be enabled using 8042 to + * resume from S3. So we do it here and hope that nothing breaks. + */ + i8042_command(&a20_on, 0x10d1); + i8042_command(NULL, 0x00ff); /* Null command for SMM firmware */ #endif /* CONFIG_X86 */ return retval; diff --git a/drivers/input/serio/q40kbd.c b/drivers/input/serio/q40kbd.c index 70fe542839fb..436a3433f8e5 100644 --- a/drivers/input/serio/q40kbd.c +++ b/drivers/input/serio/q40kbd.c @@ -193,15 +193,4 @@ static struct platform_driver q40kbd_driver = { .remove = q40kbd_remove, }; -static int __init q40kbd_init(void) -{ - return platform_driver_probe(&q40kbd_driver, q40kbd_probe); -} - -static void __exit q40kbd_exit(void) -{ - platform_driver_unregister(&q40kbd_driver); -} - -module_init(q40kbd_init); -module_exit(q40kbd_exit); +module_platform_driver_probe(q40kbd_driver, q40kbd_probe); diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 9a647ee1136a..f9a5fd89bc02 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -517,9 +517,9 @@ config TOUCHSCREEN_TOUCHWIN To compile this driver as a module, choose M here: the module will be called touchwin. -config TOUCHSCREEN_TI_TSCADC +config TOUCHSCREEN_TI_AM335X_TSC tristate "TI Touchscreen Interface" - depends on ARCH_OMAP2PLUS + depends on MFD_TI_AM335X_TSCADC help Say Y here if you have 4/5/8 wire touchscreen controller to be connected to the ADC controller on your TI AM335x SoC. @@ -527,7 +527,7 @@ config TOUCHSCREEN_TI_TSCADC If unsure, say N. To compile this driver as a module, choose M here: the - module will be called ti_tscadc. + module will be called ti_am335x_tsc. config TOUCHSCREEN_ATMEL_TSADCC tristate "Atmel Touchscreen Interface" diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 5f949c0bf82f..6bfbeab67c9f 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -51,7 +51,7 @@ obj-$(CONFIG_TOUCHSCREEN_PIXCIR) += pixcir_i2c_ts.o obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o obj-$(CONFIG_TOUCHSCREEN_ST1232) += st1232.o obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o -obj-$(CONFIG_TOUCHSCREEN_TI_TSCADC) += ti_tscadc.o +obj-$(CONFIG_TOUCHSCREEN_TI_AM335X_TSC) += ti_am335x_tsc.o obj-$(CONFIG_TOUCHSCREEN_TNETV107X) += tnetv107x-ts.o obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o diff --git a/drivers/input/touchscreen/ad7877.c b/drivers/input/touchscreen/ad7877.c index 23fa829b869d..f3a174a83c82 100644 --- a/drivers/input/touchscreen/ad7877.c +++ b/drivers/input/touchscreen/ad7877.c @@ -273,7 +273,7 @@ static int ad7877_write(struct spi_device *spi, u16 reg, u16 val) static int ad7877_read_adc(struct spi_device *spi, unsigned command) { - struct ad7877 *ts = dev_get_drvdata(&spi->dev); + struct ad7877 *ts = spi_get_drvdata(spi); struct ser_req *req; int status; int sample; @@ -720,7 +720,7 @@ static int ad7877_probe(struct spi_device *spi) goto err_free_mem; } - dev_set_drvdata(&spi->dev, ts); + spi_set_drvdata(spi, ts); ts->spi = spi; ts->input = input_dev; @@ -806,13 +806,13 @@ err_free_irq: err_free_mem: input_free_device(input_dev); kfree(ts); - dev_set_drvdata(&spi->dev, NULL); + spi_set_drvdata(spi, NULL); return err; } static int ad7877_remove(struct spi_device *spi) { - struct ad7877 *ts = dev_get_drvdata(&spi->dev); + struct ad7877 *ts = spi_get_drvdata(spi); sysfs_remove_group(&spi->dev.kobj, &ad7877_attr_group); @@ -823,7 +823,7 @@ static int ad7877_remove(struct spi_device *spi) kfree(ts); dev_dbg(&spi->dev, "unregistered touchscreen\n"); - dev_set_drvdata(&spi->dev, NULL); + spi_set_drvdata(spi, NULL); return 0; } diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 434c3df250ca..84ccf140c1bb 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -1245,7 +1245,7 @@ static int ads7846_probe(struct spi_device *spi) goto err_free_mem; } - dev_set_drvdata(&spi->dev, ts); + spi_set_drvdata(spi, ts); ts->packet = packet; ts->spi = spi; @@ -1397,7 +1397,7 @@ static int ads7846_probe(struct spi_device *spi) static int ads7846_remove(struct spi_device *spi) { - struct ads7846 *ts = dev_get_drvdata(&spi->dev); + struct ads7846 *ts = spi_get_drvdata(spi); device_init_wakeup(&spi->dev, false); diff --git a/drivers/input/touchscreen/atmel-wm97xx.c b/drivers/input/touchscreen/atmel-wm97xx.c index c5c2dbb93869..2c1e46b7e45b 100644 --- a/drivers/input/touchscreen/atmel-wm97xx.c +++ b/drivers/input/touchscreen/atmel-wm97xx.c @@ -432,17 +432,7 @@ static struct platform_driver atmel_wm97xx_driver = { }, }; -static int __init atmel_wm97xx_init(void) -{ - return platform_driver_probe(&atmel_wm97xx_driver, atmel_wm97xx_probe); -} -module_init(atmel_wm97xx_init); - -static void __exit atmel_wm97xx_exit(void) -{ - platform_driver_unregister(&atmel_wm97xx_driver); -} -module_exit(atmel_wm97xx_exit); +module_platform_driver_probe(atmel_wm97xx_driver, atmel_wm97xx_probe); MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>"); MODULE_DESCRIPTION("wm97xx continuous touch driver for Atmel AT91 and AVR32"); diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index d04f810cb1dd..59aa24002c7b 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -176,11 +176,17 @@ /* Define for MXT_GEN_COMMAND_T6 */ #define MXT_BOOT_VALUE 0xa5 #define MXT_BACKUP_VALUE 0x55 -#define MXT_BACKUP_TIME 25 /* msec */ -#define MXT_RESET_TIME 65 /* msec */ +#define MXT_BACKUP_TIME 50 /* msec */ +#define MXT_RESET_TIME 200 /* msec */ #define MXT_FWRESET_TIME 175 /* msec */ +/* MXT_SPT_GPIOPWM_T19 field */ +#define MXT_GPIO0_MASK 0x04 +#define MXT_GPIO1_MASK 0x08 +#define MXT_GPIO2_MASK 0x10 +#define MXT_GPIO3_MASK 0x20 + /* Command to unlock bootloader */ #define MXT_UNLOCK_CMD_MSB 0xaa #define MXT_UNLOCK_CMD_LSB 0xdc @@ -212,6 +218,8 @@ /* Touchscreen absolute values */ #define MXT_MAX_AREA 0xff +#define MXT_PIXELS_PER_MM 20 + struct mxt_info { u8 family_id; u8 variant_id; @@ -243,6 +251,8 @@ struct mxt_data { const struct mxt_platform_data *pdata; struct mxt_object *object_table; struct mxt_info info; + bool is_tp; + unsigned int irq; unsigned int max_x; unsigned int max_y; @@ -251,6 +261,7 @@ struct mxt_data { u8 T6_reportid; u8 T9_reportid_min; u8 T9_reportid_max; + u8 T19_reportid; }; static bool mxt_object_readable(unsigned int type) @@ -502,6 +513,21 @@ static int mxt_write_object(struct mxt_data *data, return mxt_write_reg(data->client, reg + offset, val); } +static void mxt_input_button(struct mxt_data *data, struct mxt_message *message) +{ + struct input_dev *input = data->input_dev; + bool button; + int i; + + /* Active-low switch */ + for (i = 0; i < MXT_NUM_GPIO; i++) { + if (data->pdata->key_map[i] == KEY_RESERVED) + continue; + button = !(message->message[0] & MXT_GPIO0_MASK << i); + input_report_key(input, data->pdata->key_map[i], button); + } +} + static void mxt_input_touchevent(struct mxt_data *data, struct mxt_message *message, int id) { @@ -585,6 +611,9 @@ static irqreturn_t mxt_interrupt(int irq, void *dev_id) int id = reportid - data->T9_reportid_min; mxt_input_touchevent(data, &message, id); update_input = true; + } else if (message.reportid == data->T19_reportid) { + mxt_input_button(data, &message); + update_input = true; } else { mxt_dump_message(dev, &message); } @@ -764,6 +793,9 @@ static int mxt_get_object_table(struct mxt_data *data) data->T9_reportid_min = min_id; data->T9_reportid_max = max_id; break; + case MXT_SPT_GPIOPWM_T19: + data->T19_reportid = min_id; + break; } } @@ -777,7 +809,7 @@ static void mxt_free_object_table(struct mxt_data *data) data->T6_reportid = 0; data->T9_reportid_min = 0; data->T9_reportid_max = 0; - + data->T19_reportid = 0; } static int mxt_initialize(struct mxt_data *data) @@ -1115,9 +1147,13 @@ static int mxt_probe(struct i2c_client *client, goto err_free_mem; } - input_dev->name = "Atmel maXTouch Touchscreen"; + data->is_tp = pdata && pdata->is_tp; + + input_dev->name = (data->is_tp) ? "Atmel maXTouch Touchpad" : + "Atmel maXTouch Touchscreen"; snprintf(data->phys, sizeof(data->phys), "i2c-%u-%04x/input0", client->adapter->nr, client->addr); + input_dev->phys = data->phys; input_dev->id.bustype = BUS_I2C; @@ -1140,6 +1176,29 @@ static int mxt_probe(struct i2c_client *client, __set_bit(EV_KEY, input_dev->evbit); __set_bit(BTN_TOUCH, input_dev->keybit); + if (data->is_tp) { + int i; + __set_bit(INPUT_PROP_POINTER, input_dev->propbit); + __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); + + for (i = 0; i < MXT_NUM_GPIO; i++) + if (pdata->key_map[i] != KEY_RESERVED) + __set_bit(pdata->key_map[i], input_dev->keybit); + + __set_bit(BTN_TOOL_FINGER, input_dev->keybit); + __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); + __set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit); + __set_bit(BTN_TOOL_QUADTAP, input_dev->keybit); + __set_bit(BTN_TOOL_QUINTTAP, input_dev->keybit); + + input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM); + input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM); + input_abs_set_res(input_dev, ABS_MT_POSITION_X, + MXT_PIXELS_PER_MM); + input_abs_set_res(input_dev, ABS_MT_POSITION_Y, + MXT_PIXELS_PER_MM); + } + /* For single touch */ input_set_abs_params(input_dev, ABS_X, 0, data->max_x, 0, 0); @@ -1258,6 +1317,7 @@ static SIMPLE_DEV_PM_OPS(mxt_pm_ops, mxt_suspend, mxt_resume); static const struct i2c_device_id mxt_id[] = { { "qt602240_ts", 0 }, { "atmel_mxt_ts", 0 }, + { "atmel_mxt_tp", 0 }, { "mXT224", 0 }, { } }; diff --git a/drivers/input/touchscreen/atmel_tsadcc.c b/drivers/input/touchscreen/atmel_tsadcc.c index 55092d1c5cb9..95f6785a94b0 100644 --- a/drivers/input/touchscreen/atmel_tsadcc.c +++ b/drivers/input/touchscreen/atmel_tsadcc.c @@ -22,7 +22,7 @@ #include <linux/clk.h> #include <linux/platform_device.h> #include <linux/io.h> -#include <mach/board.h> +#include <linux/platform_data/atmel.h> #include <mach/cpu.h> /* Register definitions based on AT91SAM9RL64 preliminary draft datasheet */ diff --git a/drivers/input/touchscreen/auo-pixcir-ts.c b/drivers/input/touchscreen/auo-pixcir-ts.c index c6e19a96348e..d3f9f6b0f9b7 100644 --- a/drivers/input/touchscreen/auo-pixcir-ts.c +++ b/drivers/input/touchscreen/auo-pixcir-ts.c @@ -31,6 +31,8 @@ #include <linux/delay.h> #include <linux/gpio.h> #include <linux/input/auo-pixcir-ts.h> +#include <linux/of.h> +#include <linux/of_gpio.h> /* * Coordinate calculation: @@ -111,6 +113,7 @@ struct auo_pixcir_ts { struct i2c_client *client; struct input_dev *input; + const struct auo_pixcir_ts_platdata *pdata; char phys[32]; /* special handling for touch_indicate interupt mode */ @@ -132,7 +135,7 @@ static int auo_pixcir_collect_data(struct auo_pixcir_ts *ts, struct auo_point_t *point) { struct i2c_client *client = ts->client; - const struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data; + const struct auo_pixcir_ts_platdata *pdata = ts->pdata; uint8_t raw_coord[8]; uint8_t raw_area[4]; int i, ret; @@ -178,8 +181,7 @@ static int auo_pixcir_collect_data(struct auo_pixcir_ts *ts, static irqreturn_t auo_pixcir_interrupt(int irq, void *dev_id) { struct auo_pixcir_ts *ts = dev_id; - struct i2c_client *client = ts->client; - const struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data; + const struct auo_pixcir_ts_platdata *pdata = ts->pdata; struct auo_point_t point[AUO_PIXCIR_REPORT_POINTS]; int i; int ret; @@ -290,7 +292,7 @@ static int auo_pixcir_int_config(struct auo_pixcir_ts *ts, int int_setting) { struct i2c_client *client = ts->client; - struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data; + const struct auo_pixcir_ts_platdata *pdata = ts->pdata; int ret; ret = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_INT_SETTING); @@ -479,53 +481,105 @@ unlock: } #endif -static SIMPLE_DEV_PM_OPS(auo_pixcir_pm_ops, auo_pixcir_suspend, - auo_pixcir_resume); +static SIMPLE_DEV_PM_OPS(auo_pixcir_pm_ops, + auo_pixcir_suspend, auo_pixcir_resume); + +#ifdef CONFIG_OF +static struct auo_pixcir_ts_platdata *auo_pixcir_parse_dt(struct device *dev) +{ + struct auo_pixcir_ts_platdata *pdata; + struct device_node *np = dev->of_node; + + if (!np) + return ERR_PTR(-ENOENT); + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(dev, "failed to allocate platform data\n"); + return ERR_PTR(-ENOMEM); + } + + pdata->gpio_int = of_get_gpio(np, 0); + if (!gpio_is_valid(pdata->gpio_int)) { + dev_err(dev, "failed to get interrupt gpio\n"); + return ERR_PTR(-EINVAL); + } + + pdata->gpio_rst = of_get_gpio(np, 1); + if (!gpio_is_valid(pdata->gpio_rst)) { + dev_err(dev, "failed to get reset gpio\n"); + return ERR_PTR(-EINVAL); + } + + if (of_property_read_u32(np, "x-size", &pdata->x_max)) { + dev_err(dev, "failed to get x-size property\n"); + return ERR_PTR(-EINVAL); + } + + if (of_property_read_u32(np, "y-size", &pdata->y_max)) { + dev_err(dev, "failed to get y-size property\n"); + return ERR_PTR(-EINVAL); + } + + /* default to asserting the interrupt when the screen is touched */ + pdata->int_setting = AUO_PIXCIR_INT_TOUCH_IND; + + return pdata; +} +#else +static struct auo_pixcir_ts_platdata *auo_pixcir_parse_dt(struct device *dev) +{ + return ERR_PTR(-EINVAL); +} +#endif + +static void auo_pixcir_reset(void *data) +{ + struct auo_pixcir_ts *ts = data; + + gpio_set_value(ts->pdata->gpio_rst, 0); +} static int auo_pixcir_probe(struct i2c_client *client, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { - const struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data; + const struct auo_pixcir_ts_platdata *pdata; struct auo_pixcir_ts *ts; struct input_dev *input_dev; - int ret; - - if (!pdata) - return -EINVAL; + int version; + int error; + + pdata = dev_get_platdata(&client->dev); + if (!pdata) { + pdata = auo_pixcir_parse_dt(&client->dev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + } - ts = kzalloc(sizeof(struct auo_pixcir_ts), GFP_KERNEL); + ts = devm_kzalloc(&client->dev, + sizeof(struct auo_pixcir_ts), GFP_KERNEL); if (!ts) return -ENOMEM; - ret = gpio_request(pdata->gpio_int, "auo_pixcir_ts_int"); - if (ret) { - dev_err(&client->dev, "request of gpio %d failed, %d\n", - pdata->gpio_int, ret); - goto err_gpio_int; + input_dev = devm_input_allocate_device(&client->dev); + if (!input_dev) { + dev_err(&client->dev, "could not allocate input device\n"); + return -ENOMEM; } - if (pdata->init_hw) - pdata->init_hw(client); - + ts->pdata = pdata; ts->client = client; + ts->input = input_dev; ts->touch_ind_mode = 0; + ts->stopped = true; init_waitqueue_head(&ts->wait); snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&client->dev)); - input_dev = input_allocate_device(); - if (!input_dev) { - dev_err(&client->dev, "could not allocate input device\n"); - goto err_input_alloc; - } - - ts->input = input_dev; - input_dev->name = "AUO-Pixcir touchscreen"; input_dev->phys = ts->phys; input_dev->id.bustype = BUS_I2C; - input_dev->dev.parent = &client->dev; input_dev->open = auo_pixcir_input_open; input_dev->close = auo_pixcir_input_close; @@ -550,70 +604,70 @@ static int auo_pixcir_probe(struct i2c_client *client, AUO_PIXCIR_MAX_AREA, 0, 0); input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0); - ret = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_VERSION); - if (ret < 0) - goto err_fw_vers; - dev_info(&client->dev, "firmware version 0x%X\n", ret); - - ret = auo_pixcir_int_config(ts, pdata->int_setting); - if (ret) - goto err_fw_vers; - input_set_drvdata(ts->input, ts); - ts->stopped = true; - ret = request_threaded_irq(client->irq, NULL, auo_pixcir_interrupt, - IRQF_TRIGGER_RISING | IRQF_ONESHOT, - input_dev->name, ts); - if (ret) { - dev_err(&client->dev, "irq %d requested failed\n", client->irq); - goto err_fw_vers; + error = devm_gpio_request_one(&client->dev, pdata->gpio_int, + GPIOF_DIR_IN, "auo_pixcir_ts_int"); + if (error) { + dev_err(&client->dev, "request of gpio %d failed, %d\n", + pdata->gpio_int, error); + return error; } - /* stop device and put it into deep sleep until it is opened */ - ret = auo_pixcir_stop(ts); - if (ret < 0) - goto err_input_register; - - ret = input_register_device(input_dev); - if (ret) { - dev_err(&client->dev, "could not register input device\n"); - goto err_input_register; + error = devm_gpio_request_one(&client->dev, pdata->gpio_rst, + GPIOF_DIR_OUT | GPIOF_INIT_HIGH, + "auo_pixcir_ts_rst"); + if (error) { + dev_err(&client->dev, "request of gpio %d failed, %d\n", + pdata->gpio_rst, error); + return error; } - i2c_set_clientdata(client, ts); - - return 0; + error = devm_add_action(&client->dev, auo_pixcir_reset, ts); + if (error) { + auo_pixcir_reset(ts); + dev_err(&client->dev, "failed to register reset action, %d\n", + error); + return error; + } -err_input_register: - free_irq(client->irq, ts); -err_fw_vers: - input_free_device(input_dev); -err_input_alloc: - if (pdata->exit_hw) - pdata->exit_hw(client); - gpio_free(pdata->gpio_int); -err_gpio_int: - kfree(ts); + msleep(200); - return ret; -} - -static int auo_pixcir_remove(struct i2c_client *client) -{ - struct auo_pixcir_ts *ts = i2c_get_clientdata(client); - const struct auo_pixcir_ts_platdata *pdata = client->dev.platform_data; + version = i2c_smbus_read_byte_data(client, AUO_PIXCIR_REG_VERSION); + if (version < 0) { + error = version; + return error; + } - free_irq(client->irq, ts); + dev_info(&client->dev, "firmware version 0x%X\n", version); - input_unregister_device(ts->input); + error = auo_pixcir_int_config(ts, pdata->int_setting); + if (error) + return error; - if (pdata->exit_hw) - pdata->exit_hw(client); + error = devm_request_threaded_irq(&client->dev, client->irq, + NULL, auo_pixcir_interrupt, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + input_dev->name, ts); + if (error) { + dev_err(&client->dev, "irq %d requested failed, %d\n", + client->irq, error); + return error; + } - gpio_free(pdata->gpio_int); + /* stop device and put it into deep sleep until it is opened */ + error = auo_pixcir_stop(ts); + if (error) + return error; + + error = input_register_device(input_dev); + if (error) { + dev_err(&client->dev, "could not register input device, %d\n", + error); + return error; + } - kfree(ts); + i2c_set_clientdata(client, ts); return 0; } @@ -624,14 +678,22 @@ static const struct i2c_device_id auo_pixcir_idtable[] = { }; MODULE_DEVICE_TABLE(i2c, auo_pixcir_idtable); +#ifdef CONFIG_OF +static struct of_device_id auo_pixcir_ts_dt_idtable[] = { + { .compatible = "auo,auo_pixcir_ts" }, + {}, +}; +MODULE_DEVICE_TABLE(of, auo_pixcir_ts_dt_idtable); +#endif + static struct i2c_driver auo_pixcir_driver = { .driver = { .owner = THIS_MODULE, .name = "auo_pixcir_ts", .pm = &auo_pixcir_pm_ops, + .of_match_table = of_match_ptr(auo_pixcir_ts_dt_idtable), }, .probe = auo_pixcir_probe, - .remove = auo_pixcir_remove, .id_table = auo_pixcir_idtable, }; diff --git a/drivers/input/touchscreen/da9052_tsi.c b/drivers/input/touchscreen/da9052_tsi.c index 303966ffe1e8..8f561e22bdd4 100644 --- a/drivers/input/touchscreen/da9052_tsi.c +++ b/drivers/input/touchscreen/da9052_tsi.c @@ -27,8 +27,6 @@ struct da9052_tsi { struct input_dev *dev; struct delayed_work ts_pen_work; struct mutex mutex; - unsigned int irq_pendwn; - unsigned int irq_datardy; bool stopped; bool adc_on; }; @@ -45,8 +43,8 @@ static irqreturn_t da9052_ts_pendwn_irq(int irq, void *data) if (!tsi->stopped) { /* Mask PEN_DOWN event and unmask TSI_READY event */ - disable_irq_nosync(tsi->irq_pendwn); - enable_irq(tsi->irq_datardy); + da9052_disable_irq_nosync(tsi->da9052, DA9052_IRQ_PENDOWN); + da9052_enable_irq(tsi->da9052, DA9052_IRQ_TSIREADY); da9052_ts_adc_toggle(tsi, true); @@ -137,8 +135,8 @@ static void da9052_ts_pen_work(struct work_struct *work) return; /* Mask TSI_READY event and unmask PEN_DOWN event */ - disable_irq(tsi->irq_datardy); - enable_irq(tsi->irq_pendwn); + da9052_disable_irq(tsi->da9052, DA9052_IRQ_TSIREADY); + da9052_enable_irq(tsi->da9052, DA9052_IRQ_PENDOWN); } } } @@ -197,7 +195,7 @@ static int da9052_ts_input_open(struct input_dev *input_dev) mb(); /* Unmask PEN_DOWN event */ - enable_irq(tsi->irq_pendwn); + da9052_enable_irq(tsi->da9052, DA9052_IRQ_PENDOWN); /* Enable Pen Detect Circuit */ return da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG, @@ -210,11 +208,11 @@ static void da9052_ts_input_close(struct input_dev *input_dev) tsi->stopped = true; mb(); - disable_irq(tsi->irq_pendwn); + da9052_disable_irq(tsi->da9052, DA9052_IRQ_PENDOWN); cancel_delayed_work_sync(&tsi->ts_pen_work); if (tsi->adc_on) { - disable_irq(tsi->irq_datardy); + da9052_disable_irq(tsi->da9052, DA9052_IRQ_TSIREADY); da9052_ts_adc_toggle(tsi, false); /* @@ -222,7 +220,7 @@ static void da9052_ts_input_close(struct input_dev *input_dev) * twice and we need to enable it to keep enable/disable * counter balanced. IRQ is still off though. */ - enable_irq(tsi->irq_pendwn); + da9052_enable_irq(tsi->da9052, DA9052_IRQ_PENDOWN); } /* Disable Pen Detect Circuit */ @@ -234,21 +232,12 @@ static int da9052_ts_probe(struct platform_device *pdev) struct da9052 *da9052; struct da9052_tsi *tsi; struct input_dev *input_dev; - int irq_pendwn; - int irq_datardy; int error; da9052 = dev_get_drvdata(pdev->dev.parent); if (!da9052) return -EINVAL; - irq_pendwn = platform_get_irq_byname(pdev, "PENDWN"); - irq_datardy = platform_get_irq_byname(pdev, "TSIRDY"); - if (irq_pendwn < 0 || irq_datardy < 0) { - dev_err(da9052->dev, "Unable to determine device interrupts\n"); - return -ENXIO; - } - tsi = kzalloc(sizeof(struct da9052_tsi), GFP_KERNEL); input_dev = input_allocate_device(); if (!tsi || !input_dev) { @@ -258,8 +247,6 @@ static int da9052_ts_probe(struct platform_device *pdev) tsi->da9052 = da9052; tsi->dev = input_dev; - tsi->irq_pendwn = da9052->irq_base + irq_pendwn; - tsi->irq_datardy = da9052->irq_base + irq_datardy; tsi->stopped = true; INIT_DELAYED_WORK(&tsi->ts_pen_work, da9052_ts_pen_work); @@ -287,31 +274,25 @@ static int da9052_ts_probe(struct platform_device *pdev) /* Disable ADC */ da9052_ts_adc_toggle(tsi, false); - error = request_threaded_irq(tsi->irq_pendwn, - NULL, da9052_ts_pendwn_irq, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, - "PENDWN", tsi); + error = da9052_request_irq(tsi->da9052, DA9052_IRQ_PENDOWN, + "pendown-irq", da9052_ts_pendwn_irq, tsi); if (error) { dev_err(tsi->da9052->dev, - "Failed to register PENDWN IRQ %d, error = %d\n", - tsi->irq_pendwn, error); + "Failed to register PENDWN IRQ: %d\n", error); goto err_free_mem; } - error = request_threaded_irq(tsi->irq_datardy, - NULL, da9052_ts_datardy_irq, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, - "TSIRDY", tsi); + error = da9052_request_irq(tsi->da9052, DA9052_IRQ_TSIREADY, + "tsiready-irq", da9052_ts_datardy_irq, tsi); if (error) { dev_err(tsi->da9052->dev, - "Failed to register TSIRDY IRQ %d, error = %d\n", - tsi->irq_datardy, error); + "Failed to register TSIRDY IRQ :%d\n", error); goto err_free_pendwn_irq; } /* Mask PEN_DOWN and TSI_READY events */ - disable_irq(tsi->irq_pendwn); - disable_irq(tsi->irq_datardy); + da9052_disable_irq(tsi->da9052, DA9052_IRQ_PENDOWN); + da9052_disable_irq(tsi->da9052, DA9052_IRQ_TSIREADY); error = da9052_configure_tsi(tsi); if (error) @@ -326,9 +307,9 @@ static int da9052_ts_probe(struct platform_device *pdev) return 0; err_free_datardy_irq: - free_irq(tsi->irq_datardy, tsi); + da9052_free_irq(tsi->da9052, DA9052_IRQ_TSIREADY, tsi); err_free_pendwn_irq: - free_irq(tsi->irq_pendwn, tsi); + da9052_free_irq(tsi->da9052, DA9052_IRQ_PENDOWN, tsi); err_free_mem: kfree(tsi); input_free_device(input_dev); @@ -342,8 +323,8 @@ static int da9052_ts_remove(struct platform_device *pdev) da9052_reg_write(tsi->da9052, DA9052_LDO9_REG, 0x19); - free_irq(tsi->irq_pendwn, tsi); - free_irq(tsi->irq_datardy, tsi); + da9052_free_irq(tsi->da9052, DA9052_IRQ_TSIREADY, tsi); + da9052_free_irq(tsi->da9052, DA9052_IRQ_PENDOWN, tsi); input_unregister_device(tsi->dev); kfree(tsi); diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index a9170157b442..83fa1b15a97f 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -440,8 +440,7 @@ static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata) return -EIO; } - if (tsdata->raw_buffer) - kfree(tsdata->raw_buffer); + kfree(tsdata->raw_buffer); tsdata->raw_buffer = NULL; /* restore parameters */ diff --git a/drivers/input/touchscreen/eeti_ts.c b/drivers/input/touchscreen/eeti_ts.c index 55255a940072..8fe5086c8d2e 100644 --- a/drivers/input/touchscreen/eeti_ts.c +++ b/drivers/input/touchscreen/eeti_ts.c @@ -206,8 +206,7 @@ static int eeti_ts_probe(struct i2c_client *client, if (err < 0) goto err1; - if (pdata) - priv->irq_active_high = pdata->irq_active_high; + priv->irq_active_high = pdata->irq_active_high; irq_flags = priv->irq_active_high ? IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING; diff --git a/drivers/input/touchscreen/mc13783_ts.c b/drivers/input/touchscreen/mc13783_ts.c index 02103b6abb39..89308fe38752 100644 --- a/drivers/input/touchscreen/mc13783_ts.c +++ b/drivers/input/touchscreen/mc13783_ts.c @@ -250,17 +250,7 @@ static struct platform_driver mc13783_ts_driver = { }, }; -static int __init mc13783_ts_init(void) -{ - return platform_driver_probe(&mc13783_ts_driver, &mc13783_ts_probe); -} -module_init(mc13783_ts_init); - -static void __exit mc13783_ts_exit(void) -{ - platform_driver_unregister(&mc13783_ts_driver); -} -module_exit(mc13783_ts_exit); +module_platform_driver_probe(mc13783_ts_driver, mc13783_ts_probe); MODULE_DESCRIPTION("MC13783 input touchscreen driver"); MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c index d9d05e222428..1740a2496371 100644 --- a/drivers/input/touchscreen/st1232.c +++ b/drivers/input/touchscreen/st1232.c @@ -19,13 +19,16 @@ */ #include <linux/delay.h> +#include <linux/gpio.h> #include <linux/i2c.h> #include <linux/input.h> #include <linux/interrupt.h> #include <linux/module.h> +#include <linux/of_gpio.h> #include <linux/pm_qos.h> #include <linux/slab.h> #include <linux/types.h> +#include <linux/platform_data/st1232_pdata.h> #define ST1232_TS_NAME "st1232-ts" @@ -48,6 +51,7 @@ struct st1232_ts_data { struct input_dev *input_dev; struct st1232_ts_finger finger[MAX_FINGERS]; struct dev_pm_qos_request low_latency_req; + int reset_gpio; }; static int st1232_ts_read_data(struct st1232_ts_data *ts) @@ -139,10 +143,17 @@ end: return IRQ_HANDLED; } +static void st1232_ts_power(struct st1232_ts_data *ts, bool poweron) +{ + if (gpio_is_valid(ts->reset_gpio)) + gpio_direction_output(ts->reset_gpio, poweron); +} + static int st1232_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct st1232_ts_data *ts; + struct st1232_pdata *pdata = client->dev.platform_data; struct input_dev *input_dev; int error; @@ -156,17 +167,36 @@ static int st1232_ts_probe(struct i2c_client *client, return -EINVAL; } + ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL); + if (!ts) + return -ENOMEM; - ts = kzalloc(sizeof(struct st1232_ts_data), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!ts || !input_dev) { - error = -ENOMEM; - goto err_free_mem; - } + input_dev = devm_input_allocate_device(&client->dev); + if (!input_dev) + return -ENOMEM; ts->client = client; ts->input_dev = input_dev; + if (pdata) + ts->reset_gpio = pdata->reset_gpio; + else if (client->dev.of_node) + ts->reset_gpio = of_get_gpio(client->dev.of_node, 0); + else + ts->reset_gpio = -ENODEV; + + if (gpio_is_valid(ts->reset_gpio)) { + error = devm_gpio_request(&client->dev, ts->reset_gpio, NULL); + if (error) { + dev_err(&client->dev, + "Unable to request GPIO pin %d.\n", + ts->reset_gpio); + return error; + } + } + + st1232_ts_power(ts, true); + input_dev->name = "st1232-touchscreen"; input_dev->id.bustype = BUS_I2C; input_dev->dev.parent = &client->dev; @@ -179,31 +209,26 @@ static int st1232_ts_probe(struct i2c_client *client, input_set_abs_params(input_dev, ABS_MT_POSITION_X, MIN_X, MAX_X, 0, 0); input_set_abs_params(input_dev, ABS_MT_POSITION_Y, MIN_Y, MAX_Y, 0, 0); - error = request_threaded_irq(client->irq, NULL, st1232_ts_irq_handler, - IRQF_ONESHOT, client->name, ts); + error = devm_request_threaded_irq(&client->dev, client->irq, + NULL, st1232_ts_irq_handler, + IRQF_ONESHOT, + client->name, ts); if (error) { dev_err(&client->dev, "Failed to register interrupt\n"); - goto err_free_mem; + return error; } error = input_register_device(ts->input_dev); if (error) { dev_err(&client->dev, "Unable to register %s input device\n", input_dev->name); - goto err_free_irq; + return error; } i2c_set_clientdata(client, ts); device_init_wakeup(&client->dev, 1); return 0; - -err_free_irq: - free_irq(client->irq, ts); -err_free_mem: - input_free_device(input_dev); - kfree(ts); - return error; } static int st1232_ts_remove(struct i2c_client *client) @@ -211,9 +236,7 @@ static int st1232_ts_remove(struct i2c_client *client) struct st1232_ts_data *ts = i2c_get_clientdata(client); device_init_wakeup(&client->dev, 0); - free_irq(client->irq, ts); - input_unregister_device(ts->input_dev); - kfree(ts); + st1232_ts_power(ts, false); return 0; } @@ -222,11 +245,14 @@ static int st1232_ts_remove(struct i2c_client *client) static int st1232_ts_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); + struct st1232_ts_data *ts = i2c_get_clientdata(client); - if (device_may_wakeup(&client->dev)) + if (device_may_wakeup(&client->dev)) { enable_irq_wake(client->irq); - else + } else { disable_irq(client->irq); + st1232_ts_power(ts, false); + } return 0; } @@ -234,11 +260,14 @@ static int st1232_ts_suspend(struct device *dev) static int st1232_ts_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); + struct st1232_ts_data *ts = i2c_get_clientdata(client); - if (device_may_wakeup(&client->dev)) + if (device_may_wakeup(&client->dev)) { disable_irq_wake(client->irq); - else + } else { + st1232_ts_power(ts, true); enable_irq(client->irq); + } return 0; } diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c new file mode 100644 index 000000000000..51e7b87827a4 --- /dev/null +++ b/drivers/input/touchscreen/ti_am335x_tsc.c @@ -0,0 +1,398 @@ +/* + * TI Touch Screen driver + * + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/input.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/clk.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/input/ti_am335x_tsc.h> +#include <linux/delay.h> + +#include <linux/mfd/ti_am335x_tscadc.h> + +#define ADCFSM_STEPID 0x10 +#define SEQ_SETTLE 275 +#define MAX_12BIT ((1 << 12) - 1) + +struct titsc { + struct input_dev *input; + struct ti_tscadc_dev *mfd_tscadc; + unsigned int irq; + unsigned int wires; + unsigned int x_plate_resistance; + bool pen_down; + int steps_to_configure; +}; + +static unsigned int titsc_readl(struct titsc *ts, unsigned int reg) +{ + return readl(ts->mfd_tscadc->tscadc_base + reg); +} + +static void titsc_writel(struct titsc *tsc, unsigned int reg, + unsigned int val) +{ + writel(val, tsc->mfd_tscadc->tscadc_base + reg); +} + +static void titsc_step_config(struct titsc *ts_dev) +{ + unsigned int config; + int i, total_steps; + + /* Configure the Step registers */ + total_steps = 2 * ts_dev->steps_to_configure; + + config = STEPCONFIG_MODE_HWSYNC | + STEPCONFIG_AVG_16 | STEPCONFIG_XPP; + switch (ts_dev->wires) { + case 4: + config |= STEPCONFIG_INP_AN2 | STEPCONFIG_XNN; + break; + case 5: + config |= STEPCONFIG_YNN | + STEPCONFIG_INP_AN4 | STEPCONFIG_XNN | + STEPCONFIG_YPP; + break; + case 8: + config |= STEPCONFIG_INP_AN2 | STEPCONFIG_XNN; + break; + } + + for (i = 1; i <= ts_dev->steps_to_configure; i++) { + titsc_writel(ts_dev, REG_STEPCONFIG(i), config); + titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); + } + + config = 0; + config = STEPCONFIG_MODE_HWSYNC | + STEPCONFIG_AVG_16 | STEPCONFIG_YNN | + STEPCONFIG_INM_ADCREFM | STEPCONFIG_FIFO1; + switch (ts_dev->wires) { + case 4: + config |= STEPCONFIG_YPP; + break; + case 5: + config |= STEPCONFIG_XPP | STEPCONFIG_INP_AN4 | + STEPCONFIG_XNP | STEPCONFIG_YPN; + break; + case 8: + config |= STEPCONFIG_YPP; + break; + } + + for (i = (ts_dev->steps_to_configure + 1); i <= total_steps; i++) { + titsc_writel(ts_dev, REG_STEPCONFIG(i), config); + titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); + } + + config = 0; + /* Charge step configuration */ + config = STEPCONFIG_XPP | STEPCONFIG_YNN | + STEPCHARGE_RFP_XPUL | STEPCHARGE_RFM_XNUR | + STEPCHARGE_INM_AN1 | STEPCHARGE_INP_AN1; + + titsc_writel(ts_dev, REG_CHARGECONFIG, config); + titsc_writel(ts_dev, REG_CHARGEDELAY, CHARGEDLY_OPENDLY); + + config = 0; + /* Configure to calculate pressure */ + config = STEPCONFIG_MODE_HWSYNC | + STEPCONFIG_AVG_16 | STEPCONFIG_YPP | + STEPCONFIG_XNN | STEPCONFIG_INM_ADCREFM; + titsc_writel(ts_dev, REG_STEPCONFIG(total_steps + 1), config); + titsc_writel(ts_dev, REG_STEPDELAY(total_steps + 1), + STEPCONFIG_OPENDLY); + + config |= STEPCONFIG_INP_AN3 | STEPCONFIG_FIFO1; + titsc_writel(ts_dev, REG_STEPCONFIG(total_steps + 2), config); + titsc_writel(ts_dev, REG_STEPDELAY(total_steps + 2), + STEPCONFIG_OPENDLY); + + titsc_writel(ts_dev, REG_SE, STPENB_STEPENB_TC); +} + +static void titsc_read_coordinates(struct titsc *ts_dev, + unsigned int *x, unsigned int *y) +{ + unsigned int fifocount = titsc_readl(ts_dev, REG_FIFO0CNT); + unsigned int prev_val_x = ~0, prev_val_y = ~0; + unsigned int prev_diff_x = ~0, prev_diff_y = ~0; + unsigned int read, diff; + unsigned int i, channel; + + /* + * Delta filter is used to remove large variations in sampled + * values from ADC. The filter tries to predict where the next + * coordinate could be. This is done by taking a previous + * coordinate and subtracting it form current one. Further the + * algorithm compares the difference with that of a present value, + * if true the value is reported to the sub system. + */ + for (i = 0; i < fifocount - 1; i++) { + read = titsc_readl(ts_dev, REG_FIFO0); + channel = read & 0xf0000; + channel = channel >> 0x10; + if ((channel >= 0) && (channel < ts_dev->steps_to_configure)) { + read &= 0xfff; + diff = abs(read - prev_val_x); + if (diff < prev_diff_x) { + prev_diff_x = diff; + *x = read; + } + prev_val_x = read; + } + + read = titsc_readl(ts_dev, REG_FIFO1); + channel = read & 0xf0000; + channel = channel >> 0x10; + if ((channel >= ts_dev->steps_to_configure) && + (channel < (2 * ts_dev->steps_to_configure - 1))) { + read &= 0xfff; + diff = abs(read - prev_val_y); + if (diff < prev_diff_y) { + prev_diff_y = diff; + *y = read; + } + prev_val_y = read; + } + } +} + +static irqreturn_t titsc_irq(int irq, void *dev) +{ + struct titsc *ts_dev = dev; + struct input_dev *input_dev = ts_dev->input; + unsigned int status, irqclr = 0; + unsigned int x = 0, y = 0; + unsigned int z1, z2, z; + unsigned int fsm; + unsigned int fifo1count, fifo0count; + int i; + + status = titsc_readl(ts_dev, REG_IRQSTATUS); + if (status & IRQENB_FIFO0THRES) { + titsc_read_coordinates(ts_dev, &x, &y); + + z1 = titsc_readl(ts_dev, REG_FIFO0) & 0xfff; + z2 = titsc_readl(ts_dev, REG_FIFO1) & 0xfff; + + fifo1count = titsc_readl(ts_dev, REG_FIFO1CNT); + for (i = 0; i < fifo1count; i++) + titsc_readl(ts_dev, REG_FIFO1); + + fifo0count = titsc_readl(ts_dev, REG_FIFO0CNT); + for (i = 0; i < fifo0count; i++) + titsc_readl(ts_dev, REG_FIFO0); + + if (ts_dev->pen_down && z1 != 0 && z2 != 0) { + /* + * Calculate pressure using formula + * Resistance(touch) = x plate resistance * + * x postion/4096 * ((z2 / z1) - 1) + */ + z = z2 - z1; + z *= x; + z *= ts_dev->x_plate_resistance; + z /= z1; + z = (z + 2047) >> 12; + + if (z <= MAX_12BIT) { + input_report_abs(input_dev, ABS_X, x); + input_report_abs(input_dev, ABS_Y, y); + input_report_abs(input_dev, ABS_PRESSURE, z); + input_report_key(input_dev, BTN_TOUCH, 1); + input_sync(input_dev); + } + } + irqclr |= IRQENB_FIFO0THRES; + } + + /* + * Time for sequencer to settle, to read + * correct state of the sequencer. + */ + udelay(SEQ_SETTLE); + + status = titsc_readl(ts_dev, REG_RAWIRQSTATUS); + if (status & IRQENB_PENUP) { + /* Pen up event */ + fsm = titsc_readl(ts_dev, REG_ADCFSM); + if (fsm == ADCFSM_STEPID) { + ts_dev->pen_down = false; + input_report_key(input_dev, BTN_TOUCH, 0); + input_report_abs(input_dev, ABS_PRESSURE, 0); + input_sync(input_dev); + } else { + ts_dev->pen_down = true; + } + irqclr |= IRQENB_PENUP; + } + + titsc_writel(ts_dev, REG_IRQSTATUS, irqclr); + + titsc_writel(ts_dev, REG_SE, STPENB_STEPENB_TC); + return IRQ_HANDLED; +} + +/* + * The functions for inserting/removing driver as a module. + */ + +static int titsc_probe(struct platform_device *pdev) +{ + struct titsc *ts_dev; + struct input_dev *input_dev; + struct ti_tscadc_dev *tscadc_dev = pdev->dev.platform_data; + struct mfd_tscadc_board *pdata; + int err; + + pdata = tscadc_dev->dev->platform_data; + + if (!pdata) { + dev_err(&pdev->dev, "Could not find platform data\n"); + return -EINVAL; + } + + /* Allocate memory for device */ + ts_dev = kzalloc(sizeof(struct titsc), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ts_dev || !input_dev) { + dev_err(&pdev->dev, "failed to allocate memory.\n"); + err = -ENOMEM; + goto err_free_mem; + } + + tscadc_dev->tsc = ts_dev; + ts_dev->mfd_tscadc = tscadc_dev; + ts_dev->input = input_dev; + ts_dev->irq = tscadc_dev->irq; + ts_dev->wires = pdata->tsc_init->wires; + ts_dev->x_plate_resistance = pdata->tsc_init->x_plate_resistance; + ts_dev->steps_to_configure = pdata->tsc_init->steps_to_configure; + + err = request_irq(ts_dev->irq, titsc_irq, + 0, pdev->dev.driver->name, ts_dev); + if (err) { + dev_err(&pdev->dev, "failed to allocate irq.\n"); + goto err_free_mem; + } + + titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO0THRES); + titsc_step_config(ts_dev); + titsc_writel(ts_dev, REG_FIFO0THR, ts_dev->steps_to_configure); + + input_dev->name = "ti-tsc"; + input_dev->dev.parent = &pdev->dev; + + 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, 0, MAX_12BIT, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0); + + /* register to the input system */ + err = input_register_device(input_dev); + if (err) + goto err_free_irq; + + platform_set_drvdata(pdev, ts_dev); + return 0; + +err_free_irq: + free_irq(ts_dev->irq, ts_dev); +err_free_mem: + input_free_device(input_dev); + kfree(ts_dev); + return err; +} + +static int titsc_remove(struct platform_device *pdev) +{ + struct ti_tscadc_dev *tscadc_dev = pdev->dev.platform_data; + struct titsc *ts_dev = tscadc_dev->tsc; + + free_irq(ts_dev->irq, ts_dev); + + input_unregister_device(ts_dev->input); + + platform_set_drvdata(pdev, NULL); + kfree(ts_dev); + return 0; +} + +#ifdef CONFIG_PM +static int titsc_suspend(struct device *dev) +{ + struct ti_tscadc_dev *tscadc_dev = dev->platform_data; + struct titsc *ts_dev = tscadc_dev->tsc; + unsigned int idle; + + if (device_may_wakeup(tscadc_dev->dev)) { + idle = titsc_readl(ts_dev, REG_IRQENABLE); + titsc_writel(ts_dev, REG_IRQENABLE, + (idle | IRQENB_HW_PEN)); + titsc_writel(ts_dev, REG_IRQWAKEUP, IRQWKUP_ENB); + } + return 0; +} + +static int titsc_resume(struct device *dev) +{ + struct ti_tscadc_dev *tscadc_dev = dev->platform_data; + struct titsc *ts_dev = tscadc_dev->tsc; + + if (device_may_wakeup(tscadc_dev->dev)) { + titsc_writel(ts_dev, REG_IRQWAKEUP, + 0x00); + titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN); + } + titsc_step_config(ts_dev); + titsc_writel(ts_dev, REG_FIFO0THR, + ts_dev->steps_to_configure); + return 0; +} + +static const struct dev_pm_ops titsc_pm_ops = { + .suspend = titsc_suspend, + .resume = titsc_resume, +}; +#define TITSC_PM_OPS (&titsc_pm_ops) +#else +#define TITSC_PM_OPS NULL +#endif + +static struct platform_driver ti_tsc_driver = { + .probe = titsc_probe, + .remove = titsc_remove, + .driver = { + .name = "tsc", + .owner = THIS_MODULE, + .pm = TITSC_PM_OPS, + }, +}; +module_platform_driver(ti_tsc_driver); + +MODULE_DESCRIPTION("TI touchscreen controller driver"); +MODULE_AUTHOR("Rachna Patil <rachna@ti.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/ti_tscadc.c b/drivers/input/touchscreen/ti_tscadc.c deleted file mode 100644 index bcedf2e7468c..000000000000 --- a/drivers/input/touchscreen/ti_tscadc.c +++ /dev/null @@ -1,486 +0,0 @@ -/* - * TI Touch Screen driver - * - * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ - * - * 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. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - - -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/err.h> -#include <linux/module.h> -#include <linux/input.h> -#include <linux/slab.h> -#include <linux/interrupt.h> -#include <linux/clk.h> -#include <linux/platform_device.h> -#include <linux/io.h> -#include <linux/input/ti_tscadc.h> -#include <linux/delay.h> - -#define REG_IRQEOI 0x020 -#define REG_RAWIRQSTATUS 0x024 -#define REG_IRQSTATUS 0x028 -#define REG_IRQENABLE 0x02C -#define REG_IRQWAKEUP 0x034 -#define REG_CTRL 0x040 -#define REG_ADCFSM 0x044 -#define REG_CLKDIV 0x04C -#define REG_SE 0x054 -#define REG_IDLECONFIG 0x058 -#define REG_CHARGECONFIG 0x05C -#define REG_CHARGEDELAY 0x060 -#define REG_STEPCONFIG(n) (0x64 + ((n - 1) * 8)) -#define REG_STEPDELAY(n) (0x68 + ((n - 1) * 8)) -#define REG_STEPCONFIG13 0x0C4 -#define REG_STEPDELAY13 0x0C8 -#define REG_STEPCONFIG14 0x0CC -#define REG_STEPDELAY14 0x0D0 -#define REG_FIFO0CNT 0xE4 -#define REG_FIFO1THR 0xF4 -#define REG_FIFO0 0x100 -#define REG_FIFO1 0x200 - -/* Register Bitfields */ -#define IRQWKUP_ENB BIT(0) -#define STPENB_STEPENB 0x7FFF -#define IRQENB_FIFO1THRES BIT(5) -#define IRQENB_PENUP BIT(9) -#define STEPCONFIG_MODE_HWSYNC 0x2 -#define STEPCONFIG_SAMPLES_AVG (1 << 4) -#define STEPCONFIG_XPP (1 << 5) -#define STEPCONFIG_XNN (1 << 6) -#define STEPCONFIG_YPP (1 << 7) -#define STEPCONFIG_YNN (1 << 8) -#define STEPCONFIG_XNP (1 << 9) -#define STEPCONFIG_YPN (1 << 10) -#define STEPCONFIG_INM (1 << 18) -#define STEPCONFIG_INP (1 << 20) -#define STEPCONFIG_INP_5 (1 << 21) -#define STEPCONFIG_FIFO1 (1 << 26) -#define STEPCONFIG_OPENDLY 0xff -#define STEPCONFIG_Z1 (3 << 19) -#define STEPIDLE_INP (1 << 22) -#define STEPCHARGE_RFP (1 << 12) -#define STEPCHARGE_INM (1 << 15) -#define STEPCHARGE_INP (1 << 19) -#define STEPCHARGE_RFM (1 << 23) -#define STEPCHARGE_DELAY 0x1 -#define CNTRLREG_TSCSSENB (1 << 0) -#define CNTRLREG_STEPID (1 << 1) -#define CNTRLREG_STEPCONFIGWRT (1 << 2) -#define CNTRLREG_4WIRE (1 << 5) -#define CNTRLREG_5WIRE (1 << 6) -#define CNTRLREG_8WIRE (3 << 5) -#define CNTRLREG_TSCENB (1 << 7) -#define ADCFSM_STEPID 0x10 - -#define SEQ_SETTLE 275 -#define ADC_CLK 3000000 -#define MAX_12BIT ((1 << 12) - 1) -#define TSCADC_DELTA_X 15 -#define TSCADC_DELTA_Y 15 - -struct tscadc { - struct input_dev *input; - struct clk *tsc_ick; - void __iomem *tsc_base; - unsigned int irq; - unsigned int wires; - unsigned int x_plate_resistance; - bool pen_down; -}; - -static unsigned int tscadc_readl(struct tscadc *ts, unsigned int reg) -{ - return readl(ts->tsc_base + reg); -} - -static void tscadc_writel(struct tscadc *tsc, unsigned int reg, - unsigned int val) -{ - writel(val, tsc->tsc_base + reg); -} - -static void tscadc_step_config(struct tscadc *ts_dev) -{ - unsigned int config; - int i; - - /* Configure the Step registers */ - - config = STEPCONFIG_MODE_HWSYNC | - STEPCONFIG_SAMPLES_AVG | STEPCONFIG_XPP; - switch (ts_dev->wires) { - case 4: - config |= STEPCONFIG_INP | STEPCONFIG_XNN; - break; - case 5: - config |= STEPCONFIG_YNN | - STEPCONFIG_INP_5 | STEPCONFIG_XNN | - STEPCONFIG_YPP; - break; - case 8: - config |= STEPCONFIG_INP | STEPCONFIG_XNN; - break; - } - - for (i = 1; i < 7; i++) { - tscadc_writel(ts_dev, REG_STEPCONFIG(i), config); - tscadc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); - } - - config = 0; - config = STEPCONFIG_MODE_HWSYNC | - STEPCONFIG_SAMPLES_AVG | STEPCONFIG_YNN | - STEPCONFIG_INM | STEPCONFIG_FIFO1; - switch (ts_dev->wires) { - case 4: - config |= STEPCONFIG_YPP; - break; - case 5: - config |= STEPCONFIG_XPP | STEPCONFIG_INP_5 | - STEPCONFIG_XNP | STEPCONFIG_YPN; - break; - case 8: - config |= STEPCONFIG_YPP; - break; - } - - for (i = 7; i < 13; i++) { - tscadc_writel(ts_dev, REG_STEPCONFIG(i), config); - tscadc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); - } - - config = 0; - /* Charge step configuration */ - config = STEPCONFIG_XPP | STEPCONFIG_YNN | - STEPCHARGE_RFP | STEPCHARGE_RFM | - STEPCHARGE_INM | STEPCHARGE_INP; - - tscadc_writel(ts_dev, REG_CHARGECONFIG, config); - tscadc_writel(ts_dev, REG_CHARGEDELAY, STEPCHARGE_DELAY); - - config = 0; - /* Configure to calculate pressure */ - config = STEPCONFIG_MODE_HWSYNC | - STEPCONFIG_SAMPLES_AVG | STEPCONFIG_YPP | - STEPCONFIG_XNN | STEPCONFIG_INM; - tscadc_writel(ts_dev, REG_STEPCONFIG13, config); - tscadc_writel(ts_dev, REG_STEPDELAY13, STEPCONFIG_OPENDLY); - - config |= STEPCONFIG_Z1 | STEPCONFIG_FIFO1; - tscadc_writel(ts_dev, REG_STEPCONFIG14, config); - tscadc_writel(ts_dev, REG_STEPDELAY14, STEPCONFIG_OPENDLY); - - tscadc_writel(ts_dev, REG_SE, STPENB_STEPENB); -} - -static void tscadc_idle_config(struct tscadc *ts_config) -{ - unsigned int idleconfig; - - idleconfig = STEPCONFIG_YNN | - STEPCONFIG_INM | - STEPCONFIG_YPN | STEPIDLE_INP; - tscadc_writel(ts_config, REG_IDLECONFIG, idleconfig); -} - -static void tscadc_read_coordinates(struct tscadc *ts_dev, - unsigned int *x, unsigned int *y) -{ - unsigned int fifocount = tscadc_readl(ts_dev, REG_FIFO0CNT); - unsigned int prev_val_x = ~0, prev_val_y = ~0; - unsigned int prev_diff_x = ~0, prev_diff_y = ~0; - unsigned int read, diff; - unsigned int i; - - /* - * Delta filter is used to remove large variations in sampled - * values from ADC. The filter tries to predict where the next - * coordinate could be. This is done by taking a previous - * coordinate and subtracting it form current one. Further the - * algorithm compares the difference with that of a present value, - * if true the value is reported to the sub system. - */ - for (i = 0; i < fifocount - 1; i++) { - read = tscadc_readl(ts_dev, REG_FIFO0) & 0xfff; - diff = abs(read - prev_val_x); - if (diff < prev_diff_x) { - prev_diff_x = diff; - *x = read; - } - prev_val_x = read; - - read = tscadc_readl(ts_dev, REG_FIFO1) & 0xfff; - diff = abs(read - prev_val_y); - if (diff < prev_diff_y) { - prev_diff_y = diff; - *y = read; - } - prev_val_y = read; - } -} - -static irqreturn_t tscadc_irq(int irq, void *dev) -{ - struct tscadc *ts_dev = dev; - struct input_dev *input_dev = ts_dev->input; - unsigned int status, irqclr = 0; - unsigned int x = 0, y = 0; - unsigned int z1, z2, z; - unsigned int fsm; - - status = tscadc_readl(ts_dev, REG_IRQSTATUS); - if (status & IRQENB_FIFO1THRES) { - tscadc_read_coordinates(ts_dev, &x, &y); - - z1 = tscadc_readl(ts_dev, REG_FIFO0) & 0xfff; - z2 = tscadc_readl(ts_dev, REG_FIFO1) & 0xfff; - - if (ts_dev->pen_down && z1 != 0 && z2 != 0) { - /* - * Calculate pressure using formula - * Resistance(touch) = x plate resistance * - * x postion/4096 * ((z2 / z1) - 1) - */ - z = z2 - z1; - z *= x; - z *= ts_dev->x_plate_resistance; - z /= z1; - z = (z + 2047) >> 12; - - if (z <= MAX_12BIT) { - input_report_abs(input_dev, ABS_X, x); - input_report_abs(input_dev, ABS_Y, y); - input_report_abs(input_dev, ABS_PRESSURE, z); - input_report_key(input_dev, BTN_TOUCH, 1); - input_sync(input_dev); - } - } - irqclr |= IRQENB_FIFO1THRES; - } - - /* - * Time for sequencer to settle, to read - * correct state of the sequencer. - */ - udelay(SEQ_SETTLE); - - status = tscadc_readl(ts_dev, REG_RAWIRQSTATUS); - if (status & IRQENB_PENUP) { - /* Pen up event */ - fsm = tscadc_readl(ts_dev, REG_ADCFSM); - if (fsm == ADCFSM_STEPID) { - ts_dev->pen_down = false; - input_report_key(input_dev, BTN_TOUCH, 0); - input_report_abs(input_dev, ABS_PRESSURE, 0); - input_sync(input_dev); - } else { - ts_dev->pen_down = true; - } - irqclr |= IRQENB_PENUP; - } - - tscadc_writel(ts_dev, REG_IRQSTATUS, irqclr); - /* check pending interrupts */ - tscadc_writel(ts_dev, REG_IRQEOI, 0x0); - - tscadc_writel(ts_dev, REG_SE, STPENB_STEPENB); - return IRQ_HANDLED; -} - -/* - * The functions for inserting/removing driver as a module. - */ - -static int tscadc_probe(struct platform_device *pdev) -{ - const struct tsc_data *pdata = pdev->dev.platform_data; - struct resource *res; - struct tscadc *ts_dev; - struct input_dev *input_dev; - struct clk *clk; - int err; - int clk_value, ctrl, irq; - - if (!pdata) { - dev_err(&pdev->dev, "missing platform data.\n"); - return -EINVAL; - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "no memory resource defined.\n"); - return -EINVAL; - } - - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "no irq ID is specified.\n"); - return -EINVAL; - } - - /* Allocate memory for device */ - ts_dev = kzalloc(sizeof(struct tscadc), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!ts_dev || !input_dev) { - dev_err(&pdev->dev, "failed to allocate memory.\n"); - err = -ENOMEM; - goto err_free_mem; - } - - ts_dev->input = input_dev; - ts_dev->irq = irq; - ts_dev->wires = pdata->wires; - ts_dev->x_plate_resistance = pdata->x_plate_resistance; - - res = request_mem_region(res->start, resource_size(res), pdev->name); - if (!res) { - dev_err(&pdev->dev, "failed to reserve registers.\n"); - err = -EBUSY; - goto err_free_mem; - } - - ts_dev->tsc_base = ioremap(res->start, resource_size(res)); - if (!ts_dev->tsc_base) { - dev_err(&pdev->dev, "failed to map registers.\n"); - err = -ENOMEM; - goto err_release_mem_region; - } - - err = request_irq(ts_dev->irq, tscadc_irq, - 0, pdev->dev.driver->name, ts_dev); - if (err) { - dev_err(&pdev->dev, "failed to allocate irq.\n"); - goto err_unmap_regs; - } - - ts_dev->tsc_ick = clk_get(&pdev->dev, "adc_tsc_ick"); - if (IS_ERR(ts_dev->tsc_ick)) { - dev_err(&pdev->dev, "failed to get TSC ick\n"); - goto err_free_irq; - } - clk_enable(ts_dev->tsc_ick); - - clk = clk_get(&pdev->dev, "adc_tsc_fck"); - if (IS_ERR(clk)) { - dev_err(&pdev->dev, "failed to get TSC fck\n"); - err = PTR_ERR(clk); - goto err_disable_clk; - } - - clk_value = clk_get_rate(clk) / ADC_CLK; - clk_put(clk); - - if (clk_value < 7) { - dev_err(&pdev->dev, "clock input less than min clock requirement\n"); - goto err_disable_clk; - } - /* CLKDIV needs to be configured to the value minus 1 */ - tscadc_writel(ts_dev, REG_CLKDIV, clk_value - 1); - - /* Enable wake-up of the SoC using touchscreen */ - tscadc_writel(ts_dev, REG_IRQWAKEUP, IRQWKUP_ENB); - - ctrl = CNTRLREG_STEPCONFIGWRT | - CNTRLREG_TSCENB | - CNTRLREG_STEPID; - switch (ts_dev->wires) { - case 4: - ctrl |= CNTRLREG_4WIRE; - break; - case 5: - ctrl |= CNTRLREG_5WIRE; - break; - case 8: - ctrl |= CNTRLREG_8WIRE; - break; - } - tscadc_writel(ts_dev, REG_CTRL, ctrl); - - tscadc_idle_config(ts_dev); - tscadc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO1THRES); - tscadc_step_config(ts_dev); - tscadc_writel(ts_dev, REG_FIFO1THR, 6); - - ctrl |= CNTRLREG_TSCSSENB; - tscadc_writel(ts_dev, REG_CTRL, ctrl); - - input_dev->name = "ti-tsc-adc"; - input_dev->dev.parent = &pdev->dev; - - 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, 0, MAX_12BIT, 0, 0); - input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0); - input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0); - - /* register to the input system */ - err = input_register_device(input_dev); - if (err) - goto err_disable_clk; - - platform_set_drvdata(pdev, ts_dev); - return 0; - -err_disable_clk: - clk_disable(ts_dev->tsc_ick); - clk_put(ts_dev->tsc_ick); -err_free_irq: - free_irq(ts_dev->irq, ts_dev); -err_unmap_regs: - iounmap(ts_dev->tsc_base); -err_release_mem_region: - release_mem_region(res->start, resource_size(res)); -err_free_mem: - input_free_device(input_dev); - kfree(ts_dev); - return err; -} - -static int __devexit tscadc_remove(struct platform_device *pdev) -{ - struct tscadc *ts_dev = platform_get_drvdata(pdev); - struct resource *res; - - free_irq(ts_dev->irq, ts_dev); - - input_unregister_device(ts_dev->input); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - iounmap(ts_dev->tsc_base); - release_mem_region(res->start, resource_size(res)); - - clk_disable(ts_dev->tsc_ick); - clk_put(ts_dev->tsc_ick); - - kfree(ts_dev); - - platform_set_drvdata(pdev, NULL); - return 0; -} - -static struct platform_driver ti_tsc_driver = { - .probe = tscadc_probe, - .remove = __devexit_p(tscadc_remove), - .driver = { - .name = "tsc", - .owner = THIS_MODULE, - }, -}; -module_platform_driver(ti_tsc_driver); - -MODULE_DESCRIPTION("TI touchscreen controller driver"); -MODULE_AUTHOR("Rachna Patil <rachna@ti.com>"); -MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/wm9712.c b/drivers/input/touchscreen/wm9712.c index 6e743e3dfda4..16b52115c27f 100644 --- a/drivers/input/touchscreen/wm9712.c +++ b/drivers/input/touchscreen/wm9712.c @@ -162,14 +162,14 @@ static void wm9712_phy_init(struct wm97xx *wm) if (rpu) { dig2 &= 0xffc0; dig2 |= WM9712_RPU(rpu); - dev_dbg(wm->dev, "setting pen detect pull-up to %d Ohms", + dev_dbg(wm->dev, "setting pen detect pull-up to %d Ohms\n", 64000 / rpu); } /* WM9712 five wire */ if (five_wire) { dig2 |= WM9712_45W; - dev_dbg(wm->dev, "setting 5-wire touchscreen mode."); + dev_dbg(wm->dev, "setting 5-wire touchscreen mode.\n"); if (pil) { dev_warn(wm->dev, "pressure measurement is not " @@ -182,21 +182,21 @@ static void wm9712_phy_init(struct wm97xx *wm) if (pil == 2) { dig2 |= WM9712_PIL; dev_dbg(wm->dev, - "setting pressure measurement current to 400uA."); + "setting pressure measurement current to 400uA.\n"); } else if (pil) dev_dbg(wm->dev, - "setting pressure measurement current to 200uA."); + "setting pressure measurement current to 200uA.\n"); if (!pil) pressure = 0; /* polling mode sample settling delay */ if (delay < 0 || delay > 15) { - dev_dbg(wm->dev, "supplied delay out of range."); + dev_dbg(wm->dev, "supplied delay out of range.\n"); delay = 4; } dig1 &= 0xff0f; dig1 |= WM97XX_DELAY(delay); - dev_dbg(wm->dev, "setting adc sample delay to %d u Secs.", + dev_dbg(wm->dev, "setting adc sample delay to %d u Secs.\n", delay_table[delay]); /* mask */ @@ -285,7 +285,7 @@ static int wm9712_poll_sample(struct wm97xx *wm, int adcsel, int *sample) if (is_pden(wm)) wm->pen_probably_down = 0; else - dev_dbg(wm->dev, "adc sample timeout"); + dev_dbg(wm->dev, "adc sample timeout\n"); return RC_PENUP; } @@ -295,15 +295,19 @@ static int wm9712_poll_sample(struct wm97xx *wm, int adcsel, int *sample) /* check we have correct sample */ if ((*sample ^ adcsel) & WM97XX_ADCSEL_MASK) { - dev_dbg(wm->dev, "adc wrong sample, wanted %x got %x", + dev_dbg(wm->dev, "adc wrong sample, wanted %x got %x\n", adcsel & WM97XX_ADCSEL_MASK, *sample & WM97XX_ADCSEL_MASK); - return RC_PENUP; + return RC_AGAIN; } if (wants_pen && !(*sample & WM97XX_PEN_DOWN)) { - wm->pen_probably_down = 0; - return RC_PENUP; + /* Sometimes it reads a wrong value the first time. */ + *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); + if (!(*sample & WM97XX_PEN_DOWN)) { + wm->pen_probably_down = 0; + return RC_PENUP; + } } return RC_VALID; @@ -345,7 +349,7 @@ static int wm9712_poll_coord(struct wm97xx *wm, struct wm97xx_data *data) if (is_pden(wm)) wm->pen_probably_down = 0; else - dev_dbg(wm->dev, "adc sample timeout"); + dev_dbg(wm->dev, "adc sample timeout\n"); return RC_PENUP; } diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c index 5dbe73af2f8f..7e45c9f6e6b7 100644 --- a/drivers/input/touchscreen/wm97xx-core.c +++ b/drivers/input/touchscreen/wm97xx-core.c @@ -442,6 +442,16 @@ static int wm97xx_read_samples(struct wm97xx *wm) "pen down: x=%x:%d, y=%x:%d, pressure=%x:%d\n", data.x >> 12, data.x & 0xfff, data.y >> 12, data.y & 0xfff, data.p >> 12, data.p & 0xfff); + + if (abs_x[0] > (data.x & 0xfff) || + abs_x[1] < (data.x & 0xfff) || + abs_y[0] > (data.y & 0xfff) || + abs_y[1] < (data.y & 0xfff)) { + dev_dbg(wm->dev, "Measurement out of range, dropping it\n"); + rc = RC_AGAIN; + goto out; + } + input_report_abs(wm->input_dev, ABS_X, data.x & 0xfff); input_report_abs(wm->input_dev, ABS_Y, data.y & 0xfff); input_report_abs(wm->input_dev, ABS_PRESSURE, data.p & 0xfff); @@ -455,6 +465,7 @@ static int wm97xx_read_samples(struct wm97xx *wm) wm->ts_reader_interval = wm->ts_reader_min_interval; } +out: mutex_unlock(&wm->codec_mutex); return rc; } |