#include <linux/module.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zhanglong");
struct touchscreen {
unsigned long adccon,
adctsc,
adcdly,
adcdat0,
adcdat1,
adcupdn;
struct clk *ts_clk;
int down;
void (*wait4irq)(struct touchscreen *, int); //set ts in wait-for-irq mode
void (*autoxy)(struct touchscreen *); //set ts in auto x/y conversion mode
int (*isdown)(struct touchscreen *); //check stylus down or not
void (*start_conversion)(struct touchscreen *); //start conversion
int (*get_x)(struct touchscreen *); //get x
int (*get_y)(struct touchscreen *); //get y
//--------------------
struct timer_list con_timer;
struct input_dev dev;
int inval;
};
unsigned long ts_virt;
struct touchscreen s3c2440_ts;
void s3c2440_ts_wait4irq(struct touchscreen *ts, int down)
{
if (down) {
iowrite32( 0x3 | ( 1 << 4) | ( 1 << 6) | (1 << 7) , ts->adctsc);
} else {
iowrite32( 0x3 | ( 1 << 4) | ( 1 << 6) | (1 << 7) | (1 << 8) , ts->adctsc);
}
}
int s3c2440_ts_isdown(struct touchscreen *ts)
{
int down;
down = ioread32(ts->adcupdn);
iowrite32(0, ts->adcupdn);
return (down & 1);
}
void s3c2440_ts_autoxy(struct touchscreen *ts)
{
iowrite32(1 << 2, ts->adctsc);
}
void s3c2440_ts_start_conversion(struct touchscreen *ts)
{
iowrite32( 1 | (49 << 6) | (1 << 14), ts->adccon);
}
int s3c2440_ts_get_x(struct touchscreen *ts)
{
return (ioread32(ts->adcdat0) & 0x3ff);
}
int s3c2440_ts_get_y(struct touchscreen *ts)
{
return (ioread32(ts->adcdat1) & 0x3ff);
}
void timer_handle(struct touchscreen *ts)
{
ts->autoxy(ts);
ts->start_conversion(ts);
ts->con_timer.expires = jiffies + ts->inval;
add_timer(&ts->con_timer);
}
irqreturn_t s3c2440_tstc_handle(int irq, struct touchscreen *ts)
{
if(IRQ_TC == irq)
{
if(ts->isdown(ts))
{ //stylus down
ts->autoxy(ts);
ts->start_conversion(ts);
ts->down = 1;
input_report_abs(&ts->dev, ABS_PRESSURE, 1);
add_timer(&ts->con_timer);
s3c2410_gpio_setpin(S3C2410_GPF4, 0); //LED on
}
else
{ //stylus up
del_timer(&ts->con_timer);
input_report_abs(&ts->dev, ABS_PRESSURE, 0);
input_sync(&ts->dev);
ts->down = 0;
ts->wait4irq(ts, 1);
s3c2410_gpio_setpin(S3C2410_GPF4, 1); //LED off
}
}
return IRQ_HANDLED;
}
irqreturn_t s3c2440_tsadc_handle(int irq, struct touchscreen *ts)
{
if(IRQ_ADC == irq)
{
int x, y;
x = ts->get_x(ts);
y = ts->get_y(ts);
input_report_abs(&ts->dev, ABS_X, x);
input_report_abs(&ts->dev, ABS_Y, y);
if(ts->down)
{
ts->wait4irq(ts, 0);
}
}
return IRQ_HANDLED;
}
int test_init(void)
{
int ret;
ret = request_mem_region(0x58000000, 0x1000, "s3c2440-ts");
if(NULL == ret)
{
printk("request_mem_region failed .\n");
ret = -EBUSY;
goto err0;
}
ts_virt = ioremap(0x58000000, 0x1000);
s3c2440_ts.down = 0;
s3c2440_ts.adccon = ts_virt;
s3c2440_ts.adctsc = ts_virt + 0x04;
s3c2440_ts.adcdly = ts_virt + 0x08;
s3c2440_ts.adcdat0 = ts_virt + 0x0c;
s3c2440_ts.adcdat1 = ts_virt + 0x10;
s3c2440_ts.adcupdn = ts_virt + 0x14;
//---
s3c2440_ts.wait4irq = s3c2440_ts_wait4irq;
s3c2440_ts.isdown = s3c2440_ts_isdown;
s3c2440_ts.autoxy = s3c2440_ts_autoxy;
s3c2440_ts.start_conversion = s3c2440_ts_start_conversion;
s3c2440_ts.get_x = s3c2440_ts_get_x;
s3c2440_ts.get_y = s3c2440_ts_get_y;
s3c2440_ts.dev.evbit[0] = BIT(EV_ABS) | BIT(EV_SYN);
input_set_abs_params(&s3c2440_ts.dev, ABS_X, 0, 0x3ff, 0, 0);
input_set_abs_params(&s3c2440_ts.dev, ABS_Y, 0, 0x3ff, 0, 0);
input_set_abs_params(&s3c2440_ts.dev, ABS_PRESSURE, 0, 1, 0, 0);
input_register_device(&s3c2440_ts.dev);
ret = request_irq(IRQ_TC, s3c2440_tstc_handle, 0, "s3c2440-ts", &s3c2440_ts);
if(0 != ret)
{
printk("request irq[1] failed .\n");
ret = -EBUSY;
goto err1;
}
ret = request_irq(IRQ_ADC, s3c2440_tsadc_handle, 0, "s3c2440-ts", &s3c2440_ts);
if(0 != ret)
{
printk("request irq[2] failed .\n");
ret = -EBUSY;
goto err2;
}
s3c2440_ts.ts_clk = clk_get(NULL, "adc");
clk_use(s3c2440_ts.ts_clk);
clk_enable(s3c2440_ts.ts_clk);
init_timer(&s3c2440_ts.con_timer);
s3c2440_ts.con_timer.function = timer_handle;
s3c2440_ts.con_timer.data = &s3c2440_ts;
s3c2440_ts.con_timer.expires = jiffies + s3c2440_ts.inval;
s3c2440_ts.wait4irq(&s3c2440_ts, 1);
return 0;
err2:
free_irq(IRQ_TC, &s3c2440_ts);
err1:
iounmap(ts_virt);
release_mem_region(0x58000000, 0x1000);
err0:
return ret;
}
void test_exit(void)
{
input_unregister_device(&s3c2440_ts.dev);
free_irq(IRQ_TC, &s3c2440_ts);
free_irq(IRQ_ADC, &s3c2440_ts);
clk_disable(s3c2440_ts.ts_clk);
clk_unuse(s3c2440_ts.ts_clk);
clk_put(s3c2440_ts.ts_clk);
iounmap(ts_virt);
release_mem_region(0x58000000, 0x1000);
}
module_init(test_init);
module_exit(test_exit);
|