S3C2440 触摸屏驱动(针对Android版)


和原来的触摸屏驱动区别不是很大,增加了report函数来将事件发送到应用层。

驱动结构:

很简单的字符设备+平台设备驱动,总的结构来说,主要四个部分构成:

probe

remove

resume

suspend

工作机制则是注册设备,然后发生ts按下事件后产生ts中断以及adc中断,获得按下坐标。

没有读写函数,重点就是在两个中断处理函数上。

1,平台设备架构部分分析:

probe函数:

流程:ts基址的重映射->获得并启动时钟->ADCCON、ADCDLY、ADCTSC的初始化->初始化input设备完善ts结构体->建立ts_filter_chain->申请中断->注册input设备(2.6.27后为event0不再是ts0)。

static int __init s3c2410ts_probe(struct platform_device *pdev)
{
    int rc;
    struct s3c2410_ts_mach_info *info;
    struct input_dev *input_dev;
    int ret = 0;

    dev_info(&pdev->dev, "Starting\n");

    info = (struct s3c2410_ts_mach_info *)pdev->dev.platform_data;
//获得平台设备数据


    if (!info)
    {
        dev_err(&pdev->dev, "Hm... too bad: no platform data for ts\n");
        return -EINVAL;
    }

#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
    printk(DEBUG_LVL "Entering s3c2410ts_init\n");
#endif

    adc_clock = clk_get(NULL, "adc");
    if (!adc_clock) {
        dev_err(&pdev->dev, "failed to get adc clock source\n");
        return -ENOENT;
    }
    clk_enable(adc_clock);

#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
    printk(DEBUG_LVL "got and enabled clock\n");
#endif

    base_addr = ioremap(S3C2410_PA_ADC,0x20);
//将PA_ADC寄存器重映射到内存上

    if (base_addr == NULL) {
        dev_err(&pdev->dev, "Failed to remap register block\n");
        ret = -ENOMEM;
        goto bail0;
    }


    /* If we acutally are a S3C2410: Configure GPIOs */
    if (!strcmp(pdev->name, "s3c2410-ts"))
        s3c2410_ts_connect();
//初始化相关gpio口


    if ((info->presc & 0xff) > 0)
        writel(S3C2410_ADCCON_PRSCEN |
         S3C2410_ADCCON_PRSCVL(info->presc&0xFF),
                         base_addr + S3C2410_ADCCON);
    else
        writel(0, base_addr+S3C2410_ADCCON);

    /* Initialise registers */
    if ((info->delay & 0xffff) > 0)
        writel(info->delay & 0xffff, base_addr + S3C2410_ADCDLY);

    writel(WAIT4INT(0), base_addr + S3C2410_ADCTSC);

    /* Initialise input stuff */
    memset(&ts, 0, sizeof(struct s3c2410ts));
    input_dev = input_allocate_device();

    if (!input_dev) {
        dev_err(&pdev->dev, "Unable to allocate the input device\n");
        ret = -ENOMEM;
        goto bail1;
    }

//初始化input设备

    ts.dev = input_dev;
    ts.dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) |
             BIT_MASK(EV_ABS);
    ts.dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
    input_set_abs_params(ts.dev, ABS_X, 0, 0x3FF, 0, 0);
    input_set_abs_params(ts.dev, ABS_Y, 0, 0x3FF, 0, 0);
    input_set_abs_params(ts.dev, ABS_PRESSURE, 0, 1, 0, 0);

    ts.dev->name = s3c2410ts_name;
    ts.dev->id.bustype = BUS_RS232;
    ts.dev->id.vendor = 0xDEAD;
    ts.dev->id.product = 0xBEEF;
    ts.dev->id.version = S3C2410TSVERSION;
    ts.state = TS_STATE_STANDBY;
//设置ts状态为就绪

    ts.event_fifo = kfifo_alloc(TS_EVENT_FIFO_SIZE, GFP_KERNEL, NULL);
//为event队列申请内存空间

    if (IS_ERR(ts.event_fifo)) {
        ret = -EIO;
        goto bail2;
    }

    /* create the filter chain set up for the 2 coordinates we produce */
    ts.chain = ts_filter_chain_create(pdev, info->filter_config, 2);
//针对Android的,建立filter_chain


    if (IS_ERR(ts.chain))
        goto bail2;

    ts_filter_chain_clear(ts.chain);

    /* Get irqs */
    if (request_irq(IRQ_ADC, stylus_action, IRQF_SAMPLE_RANDOM,
                         "s3c2410_action", ts.dev)) {
        dev_err(&pdev->dev, "Could not allocate ts IRQ_ADC !\n");
        iounmap(base_addr);
        ret = -EIO;
        goto bail3;
    }
    if (request_irq(IRQ_TC, stylus_updown, IRQF_SAMPLE_RANDOM,
            "s3c2410_action", ts.dev)) {
        dev_err(&pdev->dev, "Could not allocate ts IRQ_TC !\n");
        free_irq(IRQ_ADC, ts.dev);
        iounmap(base_addr);
        ret = -EIO;
        goto bail4;
    }

    dev_info(&pdev->dev, "Successfully loaded\n");

    /* All went ok, so register to the input system */
    rc = input_register_device(ts.dev);
    if (rc) {
        ret = -EIO;
        goto bail5;
    }

    return 0;

bail5:
    free_irq(IRQ_TC, ts.dev);
    free_irq(IRQ_ADC, ts.dev);
    clk_disable(adc_clock);
    iounmap(base_addr);
    disable_irq(IRQ_TC);
bail4:
    disable_irq(IRQ_ADC);
bail3:
    ts_filter_chain_destroy(ts.chain);
    kfifo_free(ts.event_fifo);
bail2:
    input_unregister_device(ts.dev);
bail1:
    iounmap(base_addr);
bail0:

    return ret;
}


remove:
就是probe的逆运算,

static int s3c2410ts_remove(struct platform_device *pdev)
{
    disable_irq(IRQ_ADC);
    disable_irq(IRQ_TC);
    free_irq(IRQ_TC,ts.dev);
    free_irq(IRQ_ADC,ts.dev);

    if (adc_clock) {
        clk_disable(adc_clock);
        clk_put(adc_clock);
        adc_clock = NULL;
    }

    input_unregister_device(ts.dev);
    iounmap(base_addr);

    ts_filter_chain_destroy(ts.chain);

    kfifo_free(ts.event_fifo);

    return 0;
}


resume与suspend函数可有可无,完成触摸屏的激活和挂起,

2,
中断处理分析:

三种模式转换过程:等待down中断模式->x,y连续坐标转换模式->等待up中断模式->等待down中断模式->..
两个中断的发生:触摸屏按下,发生ts中断,开始ad转换,ad转换结束,发生adc中断。
              触摸屏抬起,发生ts中断。
ts中断处理函数:

//判断ts中断是up还是down

static irqreturn_t stylus_updown(int irq, void *dev_id)
{
    unsigned long data0;
    unsigned long data1;
    int event_type;

    data0 = readl(base_addr+S3C2410_ADCDAT0);
    data1 = readl(base_addr+S3C2410_ADCDAT1);

    ts.is_down = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) &&
                     (!(data1 & S3C2410_ADCDAT0_UPDOWN));

    event_type = ts.is_down ? 'D' : 'U';

    if (unlikely(__kfifo_put(ts.event_fifo, (unsigned char *)&event_type,
         sizeof(int)) != sizeof(int))) /* should not happen */
        printk(KERN_ERR __FILE__": stylus_updown lost event!\n");

    if (ts.is_down)
        s3c2410_ts_start_adc_conversion();
//down

    else
        writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
//up


    mod_timer(&event_send_timer, jiffies + 1);
//延时


    return IRQ_HANDLED;
}


adc中断处理函数:

static irqreturn_t stylus_action(int irq, void *dev_id)
{
//新加入进来的机制,通过filter来处理adc转换得到的数据,并用过fifo来存储

    int buf[3];

    /* Grab the ADC results. */
    buf[1] = readl(base_addr + S3C2410_ADCDAT0) &
         S3C2410_ADCDAT0_XPDATA_MASK;
    buf[2] = readl(base_addr + S3C2410_ADCDAT1) &
         S3C2410_ADCDAT1_YPDATA_MASK;

    switch (ts_filter_chain_feed(ts.chain, &buf[1])) {
    case 0:
        /* The filter wants more points. */
        s3c2410_ts_start_adc_conversion();
        return IRQ_HANDLED;
    case 1:
        /* We have a point from the filters or no filtering enabled. */
        buf[0] = 'P';
        break;
    default:
        printk(KERN_ERR __FILE__
         ":%d Invalid ts_filter_chain_feed return value.\n",
         __LINE__);
    case -1:
        /* Error. Ignore the event. */
        ts_filter_chain_clear(ts.chain);
        writel(WAIT4INT(1), base_addr + S3C2410_ADCTSC);
        return IRQ_HANDLED;
    };


    if (unlikely(__kfifo_put(ts.event_fifo, (unsigned char *)buf,
         sizeof(int) * 3) != sizeof(int) * 3))
        printk(KERN_ERR __FILE__":stylus_action bug.\n");

    writel(WAIT4INT(1), base_addr + S3C2410_ADCTSC);
    mod_timer(&event_send_timer, jiffies + 1);

    return IRQ_HANDLED;
}

 

相关内容