由于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 <lindonghai@malata.com>");
MODULE_DESCRIPTION("SX8652 TouchScreen Driver");
MODULE_LICENSE("GPL");