由于S5PV210的SPI读数据出现读到的全是0x00,改用gpio模拟spi的方式调试触摸屏驱动,采用sx8652芯片。本驱动调试成功,但尚未考虑休眠唤醒等问题,更新中......
/* * SX8652 based touchscreen and sensor driver * * 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/errno.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/input.h> #include <linux/init.h> #include <linux/serio.h> #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/fs.h> #include <linux/poll.h> #include <linux/irq.h> #include <linux/interrupt.h> #include <linux/cdev.h> #include <linux/miscdevice.h> #include <asm/uaccess.h> #include <asm/io.h> #include <asm/irq.h> #include <mach/hardware.h> #include <mach/map.h> #include <mach/regs-clock.h> #include <mach/regs-gpio.h> #include <plat/gpio-cfg.h> #include <mach/gpio.h> #define SX8652_TS_PENUP_TIME 100 /* analog channels */ #define CH_X 0 #define CH_Y 1 #define CH_Z1 2 #define CH_Z2 3 #define CH_AUX 4 #define CH_SEQ 7 /* commands */ #define SX8652_CMD_WRITEREG 0x00 #define SX8652_CMD_READCHAN 0x20 #define SX8652_CMD_READREG 0x40 #define SX8652_CMD_SELECT 0x80 #define SX8652_CMD_CONVERT 0x90 #define SX8652_CMD_MANAUTO 0xb0 #define SX8652_CMD_PENDET 0xc0 #define SX8652_CMD_PENTRG 0xe0 /* register addresses */ #define SX8652_REG_CTRL0 0x00 #define SX8652_REG_CTRL1 0x01 #define SX8652_REG_CTRL2 0x02 #define SX8652_REG_CHANMSK 0x04 #define SX8652_REG_STATUS 0x05 #define SX8652_REG_RESET 0x1f // 5 wires touch pannel #define FIVE_WIRE 0x10 /* for POWDLY or SETDLY: */ #define DLY_0_5US 0x00 #define DLY_1_1US 0x01 #define DLY_2_2US 0x02 #define DLY_4_4US 0x03 #define DLY_9US 0x04 #define DLY_18US 0x05 #define DLY_35uS 0x06 #define DLY_71US 0x07 #define DLY_140US 0x08 #define DLY_280US 0x09 #define DLY_570US 0x0a #define DLY_1_1MS 0x0b #define DLY_2_3MS 0x0c #define DLY_4_5MS 0x0d #define DLY_9MS 0x0e #define DLY_18MS 0x0f // RegCtrl1 #define CONDIRQ 0x20 #define FIVEWIRE 0x10 #define FILT_NONE 0x00 #define FILT_3SA 0x01 #define FILT_5SA 0x02 #define FILT_7SA 0x03 #define CONV_X 0x80 #define CONV_Y 0x40 #define CONV_Z1 0x20 #define CONV_Z2 0x10 #define CONV_AUX 0x08 #define CHAN_MASK (CONV_X | CONV_Y ) #define RESET_VALUE 0xde #define NUM_READ_REGS 2 /* count of words to read */ #define X_MIN 200 #define X_MAX 3850 #define Y_MIN 200 #define Y_MAX 3850 #define PRESSURE_MIN 0 #define PRESSURE_MAX 1 //define gpio #define SX8652_GPIO_CS S5PV210_GPB(1) #define SX8652_GPIO_CLK S5PV210_GPB(0) #define SX8652_GPIO_MOSI S5PV210_GPB(3) #define SX8652_GPIO_MISO S5PV210_GPB(2) #define SX8652_GPIO_IRQ S5PV210_GPH0(6) #define SX8652_IRQ IRQ_EINT6 struct sx8652 { struct input_dev *input; char phys[32]; spinlock_t lock; struct mutex mutex; unsigned disabled:1; struct timer_list penup_timer; struct workqueue_struct *ts_workq; struct work_struct pen_event_work; int pre_x_coordinate; int pre_y_coordinate; u8 pen_down; u8 data[(NUM_READ_REGS << 1) + 1]; }; //读8bit static u8 sx8652_byte_read(void) { u8 i,temp=0x00; gpio_set_value(SX8652_GPIO_CLK, 0); for(i=0;i<8;i++) { temp <<=1; gpio_set_value(SX8652_GPIO_CLK, 1); udelay(10); if(gpio_get_value(SX8652_GPIO_MISO) != 0) temp++; gpio_set_value(SX8652_GPIO_CLK, 0); udelay(10); } return (temp); } //写8bit static void sx8652_byte_write(u8 n) { u8 i; gpio_set_value(SX8652_GPIO_CLK, 0); for(i=0;i<8;i++) { if((n&0x80)==0x80) gpio_set_value(SX8652_GPIO_MOSI, 1); else gpio_set_value(SX8652_GPIO_MOSI, 0); n <<= 1; gpio_set_value(SX8652_GPIO_CLK, 1); udelay(10); gpio_set_value(SX8652_GPIO_CLK, 0); udelay(10); } } static void sx8652_write_reg(u8 reg, u8 value) { /* CS# Low */ gpio_set_value(SX8652_GPIO_CS, 0); sx8652_byte_write(reg | SX8652_CMD_WRITEREG); sx8652_byte_write(value); /* CS# High */ gpio_set_value(SX8652_GPIO_CS, 1); } static void sx8652_issue_cmd(u8 cmd) { /* CS# Low */ gpio_set_value(SX8652_GPIO_CS, 0); sx8652_byte_write(cmd); /* CS# High */ gpio_set_value(SX8652_GPIO_CS, 1); } static u8 sx8652_read_reg(u8 reg) { u8 ret; /* CS# Low */ gpio_set_value(SX8652_GPIO_CS, 0); sx8652_byte_write(reg | SX8652_CMD_READREG); ret = sx8652_byte_read(); /* CS# High */ gpio_set_value(SX8652_GPIO_CS, 1); return ret; } static void sx8652_read_chan_data(u8 *buf) { u8 i; /* CS# Low */ gpio_set_value(SX8652_GPIO_CS, 0); sx8652_byte_write(SX8652_CMD_READCHAN); for(i=0; i<NUM_READ_REGS * 2; i++) { buf[i] = sx8652_byte_read(); } /* CS# High */ gpio_set_value(SX8652_GPIO_CS, 1); } static int get_pendown_state(void) { return !gpio_get_value(SX8652_GPIO_IRQ); } static void sx8652_ts_penup_timer_handler(unsigned long data) { struct sx8652 *ts = (struct sx8652 *)data; input_report_abs(ts->input, ABS_PRESSURE, 0); input_report_key(ts->input, BTN_TOUCH, 0); input_sync(ts->input); ts->pen_down = 0; ts->pre_x_coordinate = 0; ts->pre_y_coordinate = 0; printk("%s\n", __func__); } static void sx8652_async_rx(struct sx8652 *ts) { u16 *data_ptr; u8 invalid = 0, i, bad_point = 0; int x = 0, y = 0; sx8652_read_chan_data(ts->data); data_ptr = (u16 *)&ts->data[0]; for (i = 0; i < NUM_READ_REGS; i++) { u16 data = swab16(data_ptr[i]); u8 ch = data >> 12; switch (ch) { case CH_X: x = data & 0xfff; break; case CH_Y: y = data & 0xfff; break; default: printk(KERN_ERR "? %d: %x\n", i, data); invalid = 1; break; } } if (!invalid) { // record the first point if (!ts->pen_down) { ts->pre_x_coordinate = x; ts->pre_y_coordinate = y; } // 在触摸没有抬起的情况下,新读到的数据与上一次的数据做比较,大于或者小于一定范围的值认为是有效值,否则不上报 if((ts->pre_x_coordinate > 0) && (ts->pre_y_coordinate > 0) && (abs(ts->pre_x_coordinate - x) < 160) && (abs(ts->pre_y_coordinate - y) < 160)) { if (!ts->pen_down) { //printk(KERN_ERR "pendown\n"); input_report_key(ts->input, BTN_TOUCH, 1); ts->pen_down = 1; } input_report_abs(ts->input, ABS_X, x); input_report_abs(ts->input, ABS_Y, y); input_report_abs(ts->input, ABS_PRESSURE, 1); input_sync(ts->input); ts->pre_x_coordinate = x; ts->pre_y_coordinate = y; } else bad_point = 1; printk("point(%4d,%4d), bad_point=%d\n", x, y, bad_point); } } static irqreturn_t sx8652_irq(int irq, void *handle) { struct sx8652 *ts = handle; unsigned long flags; //printk("%s\n", __func__); /* If insufficient pullup resistor on nIRQ line: * may need to make sure that pen is really down here, due to spurious interrupts */ if (likely(get_pendown_state())) { spin_lock_irqsave(&ts->lock, flags); queue_work(ts->ts_workq, &ts->pen_event_work); spin_unlock_irqrestore(&ts->lock, flags); } else printk(KERN_ERR "irq: pen up\n"); return IRQ_HANDLED; } static void sx8652_pen_irq_worker(struct work_struct *work) { struct sx8652 *ts = container_of(work, struct sx8652, pen_event_work); //printk("%s\n", __func__); /* the pen is down */ if (likely(get_pendown_state())) { /* valid data was read in */ sx8652_async_rx(ts); } else printk(KERN_ERR "fail\n"); /* kick pen up timer - to make sure it expires again(!) */ mod_timer(&ts->penup_timer, jiffies + msecs_to_jiffies(SX8652_TS_PENUP_TIME)); } static int __devinit sx8652_probe(struct platform_device *pdev) { struct sx8652 *ts; struct input_dev *input_dev; int err = -1; ts = kzalloc(sizeof(struct sx8652), GFP_KERNEL); input_dev = input_allocate_device(); if (!ts || !input_dev) { err = -ENOMEM; goto err_free_mem; } err = gpio_request(SX8652_GPIO_IRQ, "sx8652 pendown"); if (err) { dev_err(&pdev->dev, "failed to request pendown gpio\n"); goto err_free_mem; } err = gpio_request(SX8652_GPIO_CS, "sx8652 cs"); if (err) { dev_err(&pdev->dev, "failed to request sx8652 cs\n"); goto err_free_mem; } err = gpio_request(SX8652_GPIO_CLK, "sx8652 clk"); if (err) { dev_err(&pdev->dev, "failed to request sx8652 clk\n"); goto err_free_mem; } err = gpio_request(SX8652_GPIO_MOSI, "sx8652 mosi"); if (err) { dev_err(&pdev->dev, "failed to request sx8652 mosi\n"); goto err_free_mem; } err = gpio_request(SX8652_GPIO_MISO, "sx8652 miso"); if (err) { dev_err(&pdev->dev, "failed to request sx8652 miso\n"); goto err_free_mem; } s3c_gpio_setpull(SX8652_GPIO_IRQ, S3C_GPIO_PULL_UP); gpio_direction_input(SX8652_GPIO_IRQ); s3c_gpio_cfgpin(SX8652_GPIO_IRQ, S3C_GPIO_SFN(0xf0000000)); //设置为外部中断 eint0 ,参考GP0CON【】 s3c_gpio_setpull(SX8652_GPIO_MISO, S3C_GPIO_PULL_UP); gpio_direction_input(SX8652_GPIO_MISO); s3c_gpio_setpull(SX8652_GPIO_CS, S3C_GPIO_PULL_UP); s3c_gpio_setpull(SX8652_GPIO_MOSI, S3C_GPIO_PULL_UP); s3c_gpio_setpull(SX8652_GPIO_CLK, S3C_GPIO_PULL_UP); gpio_direction_output(SX8652_GPIO_MOSI,1); //MOSI gpio_direction_output(SX8652_GPIO_CLK,1); //clk gpio_direction_output(SX8652_GPIO_CS,1); //cs gpio_set_value(SX8652_GPIO_MOSI ,1); gpio_set_value(SX8652_GPIO_CLK ,1); gpio_set_value(SX8652_GPIO_CS ,1); ts->input = input_dev; platform_set_drvdata(pdev, ts); /* Send a software reset command */ sx8652_write_reg(SX8652_REG_RESET, RESET_VALUE); /* sx8652 nirq is momentarily asserted after software reset */ udelay(250); init_timer(&ts->penup_timer); setup_timer(&ts->penup_timer, sx8652_ts_penup_timer_handler, (unsigned long)ts); spin_lock_init(&ts->lock); mutex_init(&ts->mutex); ts->ts_workq = create_singlethread_workqueue("sx8652"); if (ts->ts_workq == NULL) { printk("failed to create workqueue\n"); goto err_free_mem; } INIT_WORK(&ts->pen_event_work, sx8652_pen_irq_worker); //for older kernel: snprintf(ts->phys, sizeof(ts->phys), "%s/input0", spi->dev.bus_id); //for 2.6.32: snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&pdev->dev)); input_dev->name = "SX8652 Touchscreen"; input_dev->phys = ts->phys; 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, X_MIN, X_MAX, 0, 0); input_set_abs_params(input_dev, ABS_Y, Y_MIN, Y_MAX, 0, 0); input_set_abs_params(input_dev, ABS_PRESSURE, PRESSURE_MIN, PRESSURE_MAX, 0, 0); err = input_register_device(input_dev); if (err) goto err_free_mem; sx8652_write_reg(SX8652_REG_CTRL0, DLY_1_1MS); sx8652_write_reg(SX8652_REG_CTRL1, CONDIRQ | FILT_3SA | FIVE_WIRE); sx8652_write_reg(SX8652_REG_CHANMSK, CHAN_MASK); sx8652_issue_cmd(SX8652_CMD_PENTRG); printk("%02x,%02x,%02x\n", sx8652_read_reg(SX8652_REG_CTRL0), sx8652_read_reg(SX8652_REG_CTRL1), sx8652_read_reg(SX8652_REG_CHANMSK)); if (request_irq(SX8652_IRQ, sx8652_irq, IRQF_TRIGGER_FALLING, "sx8652", ts)) { printk("IRQ_EINT6 busy\n"); err = -EBUSY; goto err_free_mem; } printk("%s end\n", __func__); return 0; err_free_mem: input_free_device(input_dev); kfree(ts); return err; } /* Must be called with ts->lock held */ static void sx8652_disable(struct sx8652 *ts) { } static int sx8652_suspend(struct platform_device *pdev, pm_message_t message) { struct sx8652 *ts = dev_get_drvdata(&pdev->dev); mutex_lock(&ts->mutex); sx8652_disable(ts); mutex_unlock(&ts->mutex); printk("%s\n", __func__); return 0; } static int __devexit sx8652_remove(struct platform_device *pdev) { struct sx8652 *ts = dev_get_drvdata(&pdev->dev); input_unregister_device(ts->input); sx8652_suspend(pdev, PMSG_SUSPEND); cancel_work_sync(&ts->pen_event_work); destroy_workqueue(ts->ts_workq); free_irq(SX8652_IRQ, ts); kfree(ts); dev_dbg(&pdev->dev, "unregistered touchscreen\n"); return 0; } /* Must be called with ts->lock held */ static void sx8652_enable(struct sx8652 *ts) { } static int sx8652_resume(struct platform_device *pdev) { struct sx8652 *ts = dev_get_drvdata(&pdev->dev); mutex_lock(&ts->mutex); sx8652_enable(ts); mutex_unlock(&ts->mutex); printk("%s\n", __func__); return 0; } static struct platform_driver sx8652_driver = { .driver = { .name = "sx8652", .owner = THIS_MODULE, }, .probe = sx8652_probe, .remove = __devexit_p(sx8652_remove), .suspend = sx8652_suspend, .resume = sx8652_resume, }; static int __init sx8652_init(void) { return platform_driver_register(&sx8652_driver); } module_init(sx8652_init); static void __exit sx8652_exit(void) { platform_driver_unregister(&sx8652_driver); } module_exit(sx8652_exit); MODULE_AUTHOR("ldh <[email protected]>"); MODULE_DESCRIPTION("SX8652 TouchScreen Driver"); MODULE_LICENSE("GPL");