ckfwq/linux-3.0.4/drivers/input/touchscreen/gsc3280_ts.c

398 lines
9.8 KiB
C
Executable File

/*
* GSC3280 SoC ADC(SPI0) Controller Driver
*
* Copyright (C) 2013 BLX IC Design Corp.,Ltd.
* Author: Davied, apple_guet@126.com
*
*/
#include <linux/types.h>
#include <linux/mutex.h>
#include <linux/device.h>
#include <linux/spi/spi.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/irqreturn.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/clk.h>
#include <linux/spi/gsc3280_spi.h>
#include <gsc3280/gsc3280_int.h>
#ifdef CONFIG_GSC3280_TS_DEBUG
#define DBG(msg...) do { \
printk(KERN_INFO msg); \
} while (0)
#else
#define DBG(msg...) do { } while(0)
#endif
#define MAX_TRY_CNT 10000
#define MAX_VALID_CNT 1
/* Touch screen coordinates */
#define GSC_X_MIN 0
#define GSC_X_MAX 4096
#define GSC_X_FUZZ 5
#define GSC_Y_MIN 0
#define GSC_Y_MAX 4096
#define GSC_Y_FUZZ 5
#define GSC_PRESSURE_MIN 0
#define GSC_PRESSURE_MAX 2000
#define ICTL_RAW_STATUS_REG 0xbc048018 //irq register
#define ICTL_SPI0_BITS 1 << 29
/* gsc3280 ts press state */
#define TS_PRESS_UP 0x00
#define TS_PRESS_DOWN 0x01
#define SPI0_STATUS_REG *((volatile unsigned int *)0xbc101018)
#define SPI_BUSY_STAT 0x10
/* receive fifo not empty, then can read data */
#define SPI_RX_N_EMPTY BIT(2)
#define SPI_RX_FULL BIT(3) //transmit fifo full
#define SPI0_DATA_REG *((volatile unsigned int *)0xbc101024)
#define GSC3280_TS_NAME_SIZE 20
#define GSC3280_TS_NAME "gsc-ts"
/***************** ADC DECLARE ***************/
#define ADC_CMD_MEASUREX 0x4000
#define ADC_CMD_MEASUREY 0x5000
#define ADC_CMD_MEASUREZ 0x6000
/* Touch screen device structure */
struct gsc3280_ts {
char phys[32];
spinlock_t slock;
struct clk *clk;
void __iomem *regs;
struct device *dev;
struct resource *irq;
struct input_dev *input;
};
/*
* desc: prepare to read data from adc
* write data to adc with spi interface
*/
unsigned short spi0_read(void)
{
int cnt= 0;
//DBG("enter adc_read()\n");
do {
while (SPI0_STATUS_REG & 0x10)
if (cnt++ > 10000)
break;
} while(0);
cnt = 0;
/*spi0 fifo receive not empty*/
while (!(SPI0_STATUS_REG & SPI_RX_N_EMPTY))
if (cnt++>10000000)
break;
//DBG("leave adc_read()\n");
return (unsigned short)(SPI0_DATA_REG);
}
void spi0_write(unsigned short v)
{
int cnt=0;
//DBG("enter adc_write()\n");
do {
while (SPI0_STATUS_REG & 0x10)
if (cnt++ > 10000)
break;
} while(0);
cnt = 0;
/*spi0 fifo can write, transmit fifo empty */
while (SPI0_STATUS_REG & SPI_RX_FULL)
if (cnt++ > 1000000)
break;
SPI0_DATA_REG = v;
//DBG("leave adc_write()\n");
}
unsigned short adc_cmd(unsigned short cmd)
{
unsigned short res = 0;
DBG("enter adc_cmd\n");
spi0_write( cmd );
while (1) {
res = spi0_read();
if ((res == 0xF000) || (res == (0x8000 | (cmd >> 12)))) {
spi0_write(0xF000);
}
else if ( res < 0x1000 ) { //data
char buf[20] ;
sprintf(buf, "adc_cmd %0x result %0x\n", cmd, res);
//DBG(buf);
return res;
}
else{
spi0_write(cmd);
}
}
//DBG("leave adc_cmd\n");
return res;
}
static void gsc3280_report_event(struct gsc3280_ts *ts, u32 x, u32 y, u32 z)
{
#ifdef CONFIG_GSC3280_POS_PRINT
printk(KERN_INFO "x = %d\n", x);
printk(KERN_INFO "y = %d\n", y);
printk(KERN_INFO "z = %d\n", z);
#endif
input_report_abs(ts->input, ABS_PRESSURE, z);
input_report_abs(ts->input, ABS_X, x);
input_report_abs(ts->input, ABS_Y, y);
if (z > 0)
input_report_key(ts->input, BTN_TOUCH, 1);
else
input_report_key(ts->input, BTN_TOUCH, 0);
input_sync(ts->input);
}
static int test_ts_state(void)
{
u32 state = *((volatile unsigned int *)(ICTL_RAW_STATUS_REG)) ;
if (state & ICTL_SPI0_BITS) {
return TS_PRESS_DOWN;
} else
return TS_PRESS_UP;
}
/* PENDET interrupt handler */
static irqreturn_t gsc3280_ts_irq(int irq, void *dev_id)
{
u8 flg = 0;
u32 x = 0, y = 0, z = 0;
struct gsc3280_ts *ts = dev_id;
begin:
x = adc_cmd(ADC_CMD_MEASUREX);
y = adc_cmd(ADC_CMD_MEASUREY);
z = adc_cmd(ADC_CMD_MEASUREZ);
x = ((x -0x83) * 800) / (0xf5d - 0x83);
y = ((0xeea - y) * 480) / (0xeea - 0xcc);
if ((z < 700) && (z > 10) && (x > 0) && (x < 800) && (y > 0) && (y < 480)) {
if (flg == 0) {
flg = 1;
gsc3280_report_event(ts, x, y, 0);
}
gsc3280_report_event(ts, x, y, z);
msleep(10);
}
if (test_ts_state() == TS_PRESS_UP)
goto Up;
else
goto begin;
Up:
if (flg == 0)
return IRQ_HANDLED;
if ((x == 0) || (y == 0))
return IRQ_HANDLED;
gsc3280_report_event(ts, x, y, 0);
return IRQ_HANDLED;
}
static void adc_sysctl(void)
{
int c = 0;
unsigned int rsl = 0;
*(volatile unsigned int *)0xbc04a00c |= 0x10;
*(volatile unsigned int *)0xbc04a008 |= 0x1000000;
*(volatile unsigned int *)(0xbc04a0ac) = 1;
*(volatile unsigned int *)(0xbc04a008) |= 0x8;
//setup sysctrl module io reuse
*(volatile unsigned int *)(0xbc04a0b0) |= 1 << 29;
//devide freq
*(volatile unsigned int *)(0xbc04a034) = 0x13;
//*(volatile unsigned int *)(0xbc04a034) = 0x9;
*(volatile unsigned int *)(0xbc101004) = 0x1;
*(volatile unsigned int *)(0xbc101000) = 0xf07;
msleep(300);
*(volatile unsigned int *)(0xbc101050) = 0x0;
c = 0;
while(1){
*(volatile unsigned int *)(0xbc101024) = 0x0;
while ( *(volatile unsigned int *)(0xbc101018) & 0x10 ) {
;
}
rsl = *(volatile unsigned int *)(0xbc101024);
if (rsl == 0x8000)
break;
if (c++ > 1000)
break;
}
}
static int __devinit gsc3280_ts_probe(struct platform_device *pdev)
{
int ret = 0, size = 0;
struct gsc3280_ts *ts;
unsigned long rate = 0;
struct input_dev *input;
struct resource *mem, *ioarea;
adc_sysctl();
DBG("############\n");
printk(KERN_INFO "gsc3280 touch screen probe start.\n");
ts = kzalloc(sizeof(struct gsc3280_ts), GFP_KERNEL);
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem) {
DBG("no mem resource.\n");
ret = -EINVAL;
goto err_alloc;
}
size = resource_size(mem);
ioarea = request_mem_region(mem->start, size, pdev->name);
if (!ioarea) {
DBG("SPI region already claimed.\n");
ret = -EBUSY;
goto err_alloc;
}
ts->regs = ioremap_nocache(mem->start, resource_size(mem));
if (!ts->regs) {
DBG("SPI region already mapped.\n");
ret = -ENOMEM;
goto err_mem;
}
DBG("probe: mapped spi0 base=%p.\n", ts->regs);
ts->clk = clk_get(NULL, "spi0");
if (IS_ERR(ts->clk)) {
DBG("failed to find watchdog clock source.\n");
ret = PTR_ERR(ts->clk);
goto err_map;
}
rate = clk_get_rate(ts->clk);
DBG("rate is %ld.\n", rate);
clk_enable(ts->clk);
input = input_allocate_device();
if (!input) {
ret = -ENOMEM;
goto err_clk;
}
ts->irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (ts->irq == NULL) {
DBG("!!!!no irq resource specified!\n");
ret = -ENOENT;
goto err_free_input_mem;
}
ret = request_threaded_irq(ts->irq ->start, NULL, gsc3280_ts_irq, IRQF_ONESHOT, "adcirq", ts);
if (ret) {
DBG("!!!!request_threaded_irq error!\n");
goto err_free_input_mem;
}
ts->dev = &pdev->dev;
spin_lock_init(&ts->slock);
snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(ts->dev));
input->name = "gsc3280-ts";
input->phys = ts->phys;
input->dev.parent = ts->dev;
input->id.vendor = 0x00; //tsdev->vendor;
input->id.version = 0x00; //tsdev->rev;
input->id.product = 0x03; //tsdev->rev;
input->id.bustype = BUS_HOST; //should be spi
input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
__set_bit(EV_REP, input->evbit);
__set_bit(EV_KEY, input->evbit);
__set_bit(BTN_TOUCH, input->keybit);
__set_bit(EV_ABS, input->evbit);
__set_bit(ABS_X, input->absbit);
__set_bit(ABS_Y, input->absbit);
__set_bit(ABS_PRESSURE, input->absbit);
ts->input = input;
input_set_abs_params(ts->input, ABS_X, GSC_X_MIN, GSC_X_MAX, GSC_X_FUZZ, 0);
input_set_abs_params(ts->input, ABS_Y, GSC_Y_MIN, GSC_Y_MAX, GSC_Y_FUZZ, 0);
input_set_abs_params(ts->input, ABS_PRESSURE, GSC_PRESSURE_MIN, GSC_PRESSURE_MAX, 0, 0);
ret = input_register_device(ts->input);
if (ret) {
DBG("!!!!input register device error!\n");
goto err_free_irq;
}
platform_set_drvdata(pdev, ts);
printk(KERN_INFO "gsc3280 touch screen probe success.\n");
DBG("############\n");
return 0;
err_free_irq:
free_irq(ts->irq->start, ts);
err_free_input_mem:
input_free_device(input);
err_clk:
clk_disable(ts->clk);
clk_put(ts->clk);
err_map:
iounmap(ts->regs);
err_mem:
release_mem_region(mem->start, size);
mem = NULL;
err_alloc:
kfree(ts);
printk(KERN_INFO "!!!!gsc3280 touch screen probe error!\n");
return ret;
}
static int __devexit gsc3280_ts_remove(struct platform_device *pdev)
{
struct gsc3280_ts *ts = platform_get_drvdata(pdev);
free_irq(ts->irq->start, ts);
input_unregister_device(ts->input);
iounmap(ts->regs);
clk_disable(ts->clk);
clk_put(ts->clk);
kfree(ts);
platform_set_drvdata(pdev, NULL);
printk(KERN_INFO "gsc3280 touch screen remove\n");
return 0;
}
static struct platform_driver gsc3280_ts_driver = {
.driver = {
.name = "gsc3280-ts",
.owner = THIS_MODULE,
},
.probe = gsc3280_ts_probe,
.remove = __devexit_p(gsc3280_ts_remove),
};
static int __init gsc3280_ts_init(void)
{
int ret = 0;
ret = platform_driver_register(&gsc3280_ts_driver);
if (ret)
printk(KERN_ERR "!!!!!!gsc ts init register error!!!!!!\n");
return ret;
}
static void __exit gsc3280_ts_exit(void)
{
platform_driver_unregister(&gsc3280_ts_driver);
}
subsys_initcall(gsc3280_ts_init);
module_exit(gsc3280_ts_exit);
MODULE_AUTHOR("Davied<apple_guet@126.com>");
MODULE_DESCRIPTION("GSC3280 touchscreen Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("gsc3280 touch screen");