首页 2430中tsc2046的触摸驱动(参考)

2430中tsc2046的触摸驱动(参考)

举报
开通vip

2430中tsc2046的触摸驱动(参考)2430中tsc2046的触摸驱动(参考) arch/arm/mach-omap2/board-2430sdp.c | 32 + drivers/input/touchscreen/Kconfig | 6 drivers/input/touchscreen/Makefile | 1 drivers/input/touchscreen/tsc2046_ts.c | 555 +++++++++++++++++++++++++++++++++ include/linux/spi/tsc2046.h | 60 +...

2430中tsc2046的触摸驱动(参考)
2430中tsc2046的触摸驱动(参考) arch/arm/mach-omap2/board-2430sdp.c | 32 + drivers/input/touchscreen/Kconfig | 6 drivers/input/touchscreen/Makefile | 1 drivers/input/touchscreen/tsc2046_ts.c | 555 +++++++++++++++++++++++++++++++++ include/linux/spi/tsc2046.h | 60 +++ 5 files changed, 654 insertions(+) Index: dev/arch/arm/mach-omap2/board-2430sdp.c =================================================================== --- dev.orig/arch/arm/mach-omap2/board-2430sdp.c +++ dev/arch/arm/mach-omap2/board-2430sdp.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include @@ -33,6 +35,7 @@ #include #include #include +#include #include "prcm-regs.h" #include @@ -40,6 +43,9 @@ #define SDP2430_FLASH_CS 0 #define SDP2430_SMC91X_CS 5 +/* TSC2046 (touchscreen) */ +#define TS_GPIO 24 + static struct mtd_partition sdp2430_partitions[] = { /* bootloader (U-Boot, etc) in first sector */ { @@ -119,6 +125,29 @@ static struct platform_device *sdp2430_d &sdp2430_flash_device, }; +static struct tsc2046_platform_data tsc2046_config = { + .dav_gpio = TS_GPIO, + .gpio_debounce = 0xa, +}; + +static struct omap2_mcspi_device_config tsc2046_mcspi_config = { + .turbo_mode = 0, + .single_channel = 0, /* 0: slave, 1: master */ +}; + +static struct spi_board_info sdp2430_spi_board_info[] __initdata = { + [0] = { + /* TSC2046 operates at a max freqency of 2MHz, so + * operate slightly below at 1.5MHz */ + .modalias = "tsc2046", + .bus_num = 1, + .chip_select = 0, + .max_speed_hz = 1500000, + .controller_data= &tsc2046_mcspi_config, + .platform_data = &tsc2046_config, + }, +}; + static inline void __init sdp2430_init_smc91x(void) { int eth_cs; @@ -198,6 +227,9 @@ static void __init omap_2430sdp_init(voi omap_board_config = sdp2430_config; omap_board_config_size = ARRAY_SIZE(sdp2430_config); omap_serial_init(); + + spi_register_board_info(omap3devkit9100_spi_board_info, + ARRAY_SIZE(omap3devkit9100_spi_board_info)); } static void __init omap_2430sdp_map_io(void) Index: dev/drivers/input/touchscreen/Kconfig =================================================================== --- dev.orig/drivers/input/touchscreen/Kconfig +++ dev/drivers/input/touchscreen/Kconfig @@ -165,4 +165,10 @@ config TOUCHSCREEN_TSC2301 help Say Y here for if you are using the touchscreen features of TSC2301. +config TOUCHSCREEN_TSC2046 + tristate "TSC2046 touchscreen support" + default MACH_OMAP2430SDP + help + Say Y here for if you are using the touchscreen features of TSC2046 + endif Index: dev/drivers/input/touchscreen/Makefile =================================================================== --- dev.orig/drivers/input/touchscreen/Makefile +++ dev/drivers/input/touchscreen/Makefile @@ -19,3 +19,4 @@ obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb obj-$(CONFIG_TOUCHSCREEN_TSC2102) += tsc2102_ts.o obj-$(CONFIG_TOUCHSCREEN_OMAP) += omap/ obj-$(CONFIG_TOUCHSCREEN_TSC2301) += tsc2301_ts.o +obj-$(CONFIG_TOUCHSCREEN_TSC2046) += tsc2046_ts.o Index: dev/drivers/input/touchscreen/tsc2046_ts.c =================================================================== --- /dev/null +++ dev/drivers/input/touchscreen/tsc2046_ts.c @@ -0,0 +1,555 @@ +/* + * TSC2046 Touchscreen driver + * + * Author: Kevin Hilman, MontaVista Software, Inc. + * + * Communication details from original TI driver + * Copyright (C) 2004-2005 Texas Instruments, Inc. + * + * Structure based heavily on TSC2301 driver + * Copyright (C) 2005-2006 Nokia Corporation + * + * 2007 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + * + */ + +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_ARCH_OMAP +#include +#endif + +#include + +/* TSC2046 commands */ +#define START_BYTE 0x8000 +#define X_CMDWD 0xd300 +#define Y_CMDWD 0x9300 +#define Z1_CMDWD 0xb300 +#define Z2_CMDWD 0xc300 + +#define TSC2046_TS_SCAN_TIME 1 +#define MAX_12BIT ((1 << 12) - 1) + +struct tsc2046_ts { + struct input_dev *idev; + char phys[32]; + struct timer_list timer; + spinlock_t lock; + + struct spi_transfer read_xfer[3]; + struct spi_message read_msg; + struct spi_message enable_msg; + u16 data[5]; + + u16 x; + u16 y; + u16 p; + int sample_cnt; + + int ignore_last : 1; + u16 x_plate_ohm; + int max_pressure; + int touch_pressure; + int pressure_limit; + + u16 irq_enabled:1; + u16 pen_down:1; + u16 disabled:1; + u16 pending:1; + + s16 dav_gpio; + int irq; +}; + +static const u16 tsc2046_ts_cmd_data[] = { + START_BYTE, X_CMDWD, Y_CMDWD, Z1_CMDWD, Z2_CMDWD, +}; + +static int device_suspended(struct device *dev) +{ + struct tsc2046 *tsc = dev_get_drvdata(dev); + return dev->power.power_state.event != PM_EVENT_ON || tsc->ts->disabled; +} + +static void update_pen_state(struct tsc2046 *tsc, int x, int y, int pressure) +{ + struct tsc2046_ts *ts = tsc->ts; + int sync = 0; + + if (pressure) { + input_report_abs(ts->idev, ABS_X, x); + input_report_abs(ts->idev, ABS_Y, y); + input_report_abs(ts->idev, ABS_PRESSURE, pressure); + if (!ts->pen_down) + input_report_key(ts->idev, BTN_TOUCH, 1); + sync = 1; + } else if (ts->pen_down) { + input_report_abs(ts->idev, ABS_PRESSURE, 0); + input_report_key(ts->idev, BTN_TOUCH, 0); + sync = 1; + } + + if (sync) + input_sync(ts->idev); + + ts->pen_down = pressure ? 1 : 0; + +#ifdef VERBOSE + dev_dbg(&tsc->spi->dev, "x %4d y %4d p %4d\n", x, y, pressure); +#endif +} + +#define CONV_DATA(d1, d2) \ + (((d1 & 0x7f) << 5) | ((d2 >> 11) & 0x1f)) + +/* + * This procedure is called by the SPI framework after the coordinates + * have been read from TSC2046 + */ +static void tsc2046_ts_rx(void *arg) +{ + struct tsc2046 *tsc = arg; + struct tsc2046_ts *ts = tsc->ts; + unsigned int x, y, z1, z2, pressure; + + x = CONV_DATA(ts->data[2], ts->data[3]); + y = CONV_DATA(ts->data[1], ts->data[2]); + z1 = CONV_DATA(ts->data[3], ts->data[4]); + z2 = CONV_DATA(ts->data[4], 0); + + if (z1) { + pressure = ts->x_plate_ohm * x; + pressure /= 4096; + pressure *= z2 - z1; + pressure /= z1; + } else + pressure = 0; + + /* If pressure value is above a preset limit (pen is barely + * touching the screen) we can't trust the coordinate values. + */ + if (pressure < ts->pressure_limit && x < MAX_12BIT && y < MAX_12BIT) { + ts->pressure_limit = ts->max_pressure; + if (ts->ignore_last) { + if (ts->sample_cnt) + update_pen_state(tsc, ts->x, ts->y, ts->p); + ts->x = x; + ts->y = y; + ts->p = pressure; + } else + update_pen_state(tsc, x, y, pressure); + ts->sample_cnt++; + } + + mod_timer(&ts->timer, + jiffies + msecs_to_jiffies(TSC2046_TS_SCAN_TIME)); +} + +static int is_pen_down(struct tsc2046_ts *ts) +{ + return ts->pen_down; +} + +/* + * Timer is called every TSC2046_TS_SCAN_TIME when the pen is down + */ +static void tsc2046_ts_timer(unsigned long arg) +{ + struct tsc2046 *tsc = (void *) arg; + struct tsc2046_ts *ts = tsc->ts; + unsigned long flags; + int ndav; + int r; + + spin_lock_irqsave(&ts->lock, flags); + ndav = omap_get_gpio_datain(ts->dav_gpio); + if (ndav || device_suspended(&tsc->spi->dev)) { + /* Pen has been lifted */ + if (!device_suspended(&tsc->spi->dev) && + !ts->irq_enabled) { + ts->irq_enabled = 1; + enable_irq(ts->irq); + } + update_pen_state(tsc, 0, 0, 0); + ts->pending = 0; + spin_unlock_irqrestore(&ts->lock, flags); + + } else { + ts->pen_down = 1; + spin_unlock_irqrestore(&ts->lock, flags); + + r = spi_async(tsc->spi, &ts->read_msg); + if (r) + dev_err(&tsc->spi->dev, "ts: spi_async() failed"); + } +} + +/* + * This interrupt is called when pen is down and first coordinates are + * available. That is indicated by a falling edge on DEV line. IRQ is + * disabled here because while the pen is down the coordinates are + * read by a timer. + */ +static irqreturn_t tsc2046_ts_irq_handler(int irq, void *dev_id) +{ + struct tsc2046 *tsc = dev_id; + struct tsc2046_ts *ts = tsc->ts; + unsigned long flags; + + spin_lock_irqsave(&ts->lock, flags); + if (ts->irq_enabled) { + ts->irq_enabled = 0; + disable_irq(ts->irq); + ts->pending = 1; + ts->pressure_limit = ts->touch_pressure; + ts->sample_cnt = 0; + mod_timer(&ts->timer, + jiffies + msecs_to_jiffies(TSC2046_TS_SCAN_TIME)); + } + spin_unlock_irqrestore(&ts->lock, flags); + + return IRQ_HANDLED; +} + +/* Must be called with ts->lock held */ +static void tsc2046_ts_disable(struct tsc2046 *tsc) +{ + struct tsc2046_ts *ts = tsc->ts; + + if (ts->disabled) + return; + + ts->disabled = 1; + if (!ts->pending) { + ts->irq_enabled = 0; + disable_irq(ts->irq); + } else { + while (ts->pending) { + spin_unlock_irq(&ts->lock); + msleep(1); + spin_lock_irq(&ts->lock); + } + } +} + +static void tsc2046_ts_enable(struct tsc2046 *tsc) +{ + struct tsc2046_ts *ts = tsc->ts; + + if (!ts->disabled) + return; + + ts->disabled = 0; + ts->irq_enabled = 1; + enable_irq(ts->irq); +} + +#ifdef CONFIG_PM +int tsc2046_ts_suspend(struct tsc2046 *tsc) +{ + struct tsc2046_ts *ts = tsc->ts; + + spin_lock_irq(&ts->lock); + tsc2046_ts_disable(tsc); + spin_unlock_irq(&ts->lock); + + return 0; +} + +void tsc2046_ts_resume(struct tsc2046 *tsc) +{ + struct tsc2046_ts *ts = tsc->ts; + + spin_lock_irq(&ts->lock); + tsc2046_ts_enable(tsc); + spin_unlock_irq(&ts->lock); +} +#endif + +static void tsc2046_ts_setup_spi_xfer(struct tsc2046 *tsc) +{ + struct tsc2046_ts *ts = tsc->ts; + struct spi_message *m = &ts->read_msg; + struct spi_transfer *x = &ts->read_xfer[1]; + + spi_message_init(m); + + /* read and write data in one transaction */ + x->tx_buf = &tsc2046_ts_cmd_data; + x->rx_buf = &ts->data; + x->len = 10; + spi_message_add_tail(x, m); + + /* send another START_BYTE to (re)enable pen interrupts */ + x++; + x->tx_buf = &tsc2046_ts_cmd_data[0]; + x->len = 2; + spi_message_add_tail(x, m); + + m->complete = tsc2046_ts_rx; + m->context = tsc; +} + +static ssize_t tsc2046_ts_pen_down_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct tsc2046 *tsc = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", is_pen_down(tsc->ts)); +} + +static DEVICE_ATTR(pen_down, S_IRUGO, tsc2046_ts_pen_down_show, NULL); + +static ssize_t tsc2046_ts_disable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tsc2046 *tsc = dev_get_drvdata(dev); + struct tsc2046_ts *ts = tsc->ts; + + return sprintf(buf, "%u\n", ts->disabled); +} + +static ssize_t tsc2046_ts_disable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct tsc2046 *tsc = dev_get_drvdata(dev); + struct tsc2046_ts *ts = tsc->ts; + char *endp; + int i; + + i = simple_strtoul(buf, &endp, 10); + spin_lock_irq(&ts->lock); + + if (i) + tsc2046_ts_disable(tsc); + else + tsc2046_ts_enable(tsc); + + spin_unlock_irq(&ts->lock); + + return count; +} + +static DEVICE_ATTR(disable_ts, 0664, tsc2046_ts_disable_show, + tsc2046_ts_disable_store); + +int __devinit tsc2046_ts_init(struct tsc2046 *tsc, + struct tsc2046_platform_data *pdata) +{ + struct tsc2046_ts *ts; + struct input_dev *idev; + int dav_gpio, r; + + if (pdata->dav_gpio < 0) { + dev_err(&tsc->spi->dev, "need DAV GPIO"); + return -EINVAL; + } + dav_gpio = pdata->dav_gpio; + + ts = kzalloc(sizeof(*ts), GFP_KERNEL); + if (ts == NULL) + return -ENOMEM; + tsc->ts = ts; + + ts->dav_gpio = dav_gpio; +#ifdef CONFIG_ARCH_OMAP + r = omap_request_gpio(dav_gpio); + if (r < 0) { + dev_err(&tsc->spi->dev, "unable to get DAV GPIO"); + goto err1; + } + omap_set_gpio_direction(dav_gpio, 1); + if (pdata->gpio_debounce) { + omap_set_gpio_debounce(tsc->gpio, 1); + omap_set_gpio_debounce_time(tsc->gpio, pdata->gpio_debounce); + } + + ts->irq = OMAP_GPIO_IRQ(dav_gpio); +#endif + init_timer(&ts->timer); + ts->timer.data = (unsigned long) tsc; + ts->timer.function = tsc2046_ts_timer; + + spin_lock_init(&ts->lock); + + ts->x_plate_ohm = pdata->ts_x_plate_ohm ? : 280; + ts->max_pressure= pdata->ts_max_pressure ? : MAX_12BIT; + ts->touch_pressure = pdata->ts_touch_pressure ? : ts->max_pressure; + ts->ignore_last = pdata->ts_ignore_last; + + idev = input_allocate_device(); + if (idev == NULL) { + r = -ENOMEM; + goto err2; + } + idev->name = "TSC2046 touchscreen"; + snprintf(ts->phys, sizeof(ts->phys), + "%s/input-ts", tsc->spi->dev.bus_id); + idev->phys = ts->phys; + + idev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY); + idev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE); + ts->idev = idev; + + tsc2046_ts_setup_spi_xfer(tsc); + + /* These parameters should perhaps be configurable? */ + input_set_abs_params(idev, ABS_X, 0, 4096, 0, 0); + input_set_abs_params(idev, ABS_Y, 0, 4096, 0, 0); + input_set_abs_params(idev, ABS_PRESSURE, 0, 1024, 0, 0); + + ts->irq_enabled = 1; + r = request_irq(ts->irq, tsc2046_ts_irq_handler, + SA_SAMPLE_RANDOM | SA_TRIGGER_FALLING, + "tsc2046-ts", tsc); + if (r < 0) { + dev_err(&tsc->spi->dev, "unable to get DAV IRQ"); + goto err3; + } + set_irq_wake(ts->irq, 1); + + if (device_create_file(&tsc->spi->dev, &dev_attr_pen_down) < 0) + goto err4; + if (device_create_file(&tsc->spi->dev, &dev_attr_disable_ts) < 0) + goto err5; + + r = input_register_device(idev); + if (r < 0) { + dev_err(&tsc->spi->dev, "can't register touchscreen device\n"); + goto err6; + } + + /* kick off a transaction to enable pen interrupts */ + spi_async(tsc->spi, &ts->read_msg); + + return 0; +err6: + device_remove_file(&tsc->spi->dev, &dev_attr_disable_ts); +err5: + device_remove_file(&tsc->spi->dev, &dev_attr_pen_down); +err4: + free_irq(ts->irq, tsc); +err3: + input_free_device(idev); +err2: +#ifdef CONFIG_ARCH_OMAP + omap_free_gpio(dav_gpio); +#endif + err1: + kfree(ts); + return r; +} +EXPORT_SYMBOL(tsc2046_ts_init); + +void __devexit tsc2046_ts_exit(struct tsc2046 *tsc) +{ + struct tsc2046_ts *ts = tsc->ts; + unsigned long flags; + + spin_lock_irqsave(&ts->lock, flags); + tsc2046_ts_disable(tsc); + spin_unlock_irqrestore(&ts->lock, flags); + + device_remove_file(&tsc->spi->dev, &dev_attr_disable_ts); + device_remove_file(&tsc->spi->dev, &dev_attr_pen_down); + + free_irq(ts->irq, tsc); + input_unregister_device(ts->idev); + +#ifdef CONFIG_ARCH_OMAP + omap_free_gpio(ts->dav_gpio); +#endif + kfree(ts); +} +EXPORT_SYMBOL(tsc2046_ts_exit); + + +static int __devinit tsc2046_probe(struct spi_device *spi) +{ + struct tsc2046 *tsc; + struct tsc2046_platform_data *pdata = spi->dev.platform_data; + int r = -ENODEV; + + dev_dbg(&spi->dev, "%s\n", __FUNCTION__); + + if (!pdata) { + dev_dbg(&spi->dev, "no platform data?\n"); + return -ENODEV; + } + + tsc = kzalloc(sizeof(*tsc), GFP_KERNEL); + if (tsc == NULL) + return -ENOMEM; + + dev_set_drvdata(&spi->dev, tsc); + tsc->spi = spi; + spi->dev.power.power_state = PMSG_ON; + + spi->mode = SPI_MODE_1; + spi->bits_per_word = 16; + + /* The max speed might've been defined by the board-specific + * struct */ + if (!spi->max_speed_hz) + spi->max_speed_hz = TSC2046_HZ; + spi_setup(spi); + + r = tsc2046_ts_init(tsc, pdata); + if (r) + goto err1; + + return 0; + err1: + kfree(tsc); + return r; +} + +static int __devexit tsc2046_remove(struct spi_device *spi) +{ + struct tsc2046 *tsc = dev_get_drvdata(&spi->dev); + + dev_dbg(&tsc->spi->dev, "%s\n", __FUNCTION__); + + tsc2046_ts_exit(tsc); + kfree(tsc); + + return 0; +} + +static struct spi_driver tsc2046_driver = { + .driver = { + .name = "tsc2046", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = tsc2046_probe, + .remove = __devexit_p(tsc2046_remove), +}; + +static int __init tsc2046_init(void) +{ + printk("TSC2046 driver initializing\n"); + + return spi_register_driver(&tsc2046_driver); +} +module_init(tsc2046_init); + +static void __exit tsc2046_exit(void) +{ + spi_unregister_driver(&tsc2046_driver); +} +module_exit(tsc2046_exit); + +MODULE_AUTHOR("Kevin Hilman "); +MODULE_LICENSE("GPL"); Index: dev/include/linux/spi/tsc2046.h =================================================================== --- /dev/null +++ dev/include/linux/spi/tsc2046.h @@ -0,0 +1,60 @@ +#ifndef _LINUX_SPI_TSC2046_H +#define _LINUX_SPI_TSC2046_H + +#include +#include + +struct tsc2046_platform_data { + s16 dav_gpio; + s16 gpio_debounce; + u16 ts_x_plate_ohm; + u32 ts_max_pressure; /* Samples with bigger pressure value will + be ignored, since the corresponding X, Y + values are unreliable */ + u32 ts_touch_pressure;/* Pressure limit until we report a + touch event. After that we switch + to ts_max_pressure. */ + unsigned ts_ignore_last : 1; + +}; + +struct tsc2046_ts; + +struct tsc2046 { + struct spi_device *spi; + int gpio; + + struct tsc2046_ts *ts; +}; + +/* The TSC2046 operates at a maximum speed of 2MHz */ +#define TSC2046_HZ 2000000 + +#define TSC2046_DECL_MOD(module) \ +extern int tsc2046_##module##_init(struct tsc2046 *tsc, \ + struct tsc2046_platform_data *pdata); \ +extern void tsc2046_##module##_exit(struct tsc2046 *tsc); \ +extern int tsc2046_##module##_suspend(struct tsc2046 *tsc); \ +extern void tsc2046_##module##_resume(struct tsc2046 *tsc); + +#define TSC2046_DECL_EMPTY_MOD(module) \ +static inline int tsc2046_##module##_init(struct tsc2046 *tsc, \ + struct tsc2046_platform_data *pdata) \ +{ \ + return 0; \ +} \ +static inline void tsc2046_##module##_exit(struct tsc2046 *tsc) {} \ +static inline int tsc2046_##module##_suspend(struct tsc2046 *tsc) \ +{ \ + return 0; \ +} \ +static inline void tsc2046_##module##_resume(struct tsc2046 *tsc) {} + +#if defined(CONFIG_TOUCHSCREEN_TSC2046) || \ + defined(CONFIG_TOUCHSCREEN_TSC2046_MODULE) +TSC2046_DECL_MOD(ts) +#else +TSC2046_DECL_EMPTY_MOD(ts) +#endif + +#endif
本文档为【2430中tsc2046的触摸驱动(参考)】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_601191
暂无简介~
格式:doc
大小:63KB
软件:Word
页数:27
分类:互联网
上传时间:2018-01-17
浏览量:113