目录
概述
1 LCD(P43)背光问题
1 移植LCD驱动程序
1.1 编写驱动代码
1.2 编写LCD的配置参数
1.3 配置LCD驱动至内核
2 移植触摸功能程序
2.1 移植ADC驱动程序
2.1.1 编写ADC驱动代码
2.1.2 编写配置文件
2.2 移植P43的一线触摸程序
2.2.1 编写代码
2.2.2 编写配置链接文件
2.3 移植S3C2410触摸功能程序
2.3.1 编写驱动代码
2.3.2 编写配置链接文件
2.4 在内核中查看触摸功能的配置
3 功能测试
概述
本文介绍了 解决mini2440 LCD(型号:P43)驱动的背光失效问题的方法,并通过实操方式,详细记录整个LCD driver的移植过程,还移植触摸功能的驱动,并且烧写到mini2440开发板上,且能正常的工作。
1 LCD(P43)背光问题
根据官方网址给的信息,得知P43型号的LCD采用的是一线触摸的方式驱动触摸功能,而背光控制也被集成在这个功能里。采用文档《Mini2440 Linux移植开发实战指南.pdf》中的方法是不行的,要解决背光问题,必须移植整个LCD的触摸驱动功能,顺便也移植了LCD的驱动,彻底解决LCD不能正常工作的问题。
本程序参考mini2440光盘中的内核源码: linux-2.6.32.2-mini2440-20150709.tgz
笔者选取的内核源码为: linux-2.6.32.24.tar.bz2,下载地址:
wget http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.32.24.tar.gz
官方LCD P43资料介绍网址:
https://wiki.friendlyelec.com/wiki/index.php/LCD-P43/zh
2 移植LCD驱动程序
2.1 编写驱动代码
Linux-2.6.32.24 内核已经支持 S3C2440 的 LCD 控制器驱动,打开 linux-2.6.32.24/arch/arm/mach-s3c2440/mach-mini2440.c,先删除之前的 LCD 设备平台代码,添加新的代码,笔者使用的LCD硬件为P43,所以驱动中只支持该屏的驱动。具体代码如下:
代码86 ~ 100行: 定义和LCD相关的参数
源代码内容:
#define LCD_WIDTH 480
#define LCD_HEIGHT 272
#define LCD_PIXCLOCK 100000
#define LCD_RIGHT_MARGIN 0x27
#define LCD_LEFT_MARGIN 0x03
#define LCD_HSYNC_LEN 0x01
#define LCD_UPPER_MARGIN 0x08
#define LCD_LOWER_MARGIN 0x09
#define LCD_VSYNC_LEN 0x01
#define LCD_CON5 (S3C2410_LCDCON5_FRM565 |S3C2410_LCDCON5_INVVLINE |\S3C2410_LCDCON5_INVVFRAME |S3C2410_LCDCON5_PWREN |\S3C2410_LCDCON5_HWSWP | S3C2410_LCDCON5_INVVCLK)
代码104 ~ 129行: 将定义的参数填充到s3c2410fb_display 的数据结构中
源代码内容:
static struct s3c2410fb_display mini2440_lcd_cfg __initdata = {
#if !defined (LCD_CON5).lcdcon5 = S3C2410_LCDCON5_FRM565 |S3C2410_LCDCON5_INVVLINE |S3C2410_LCDCON5_INVVFRAME |S3C2410_LCDCON5_PWREN |S3C2410_LCDCON5_HWSWP,
#else.lcdcon5 = LCD_CON5,
#endif
.type = S3C2410_LCDCON1_TFT,
.width = 0,.height = 0,
.pixclock = LCD_PIXCLOCK,.xres = LCD_WIDTH,.yres = LCD_HEIGHT,.bpp = 16,
.left_margin = LCD_LEFT_MARGIN + 1,.right_margin = LCD_RIGHT_MARGIN + 1,.hsync_len = LCD_HSYNC_LEN + 1,.upper_margin = LCD_UPPER_MARGIN + 1,.lower_margin = LCD_LOWER_MARGIN + 1,.vsync_len = LCD_VSYNC_LEN + 1,
};
static struct s3c2410fb_mach_info mini2440_fb_info __initdata = {.displays = &mini2440_lcd_cfg,.num_displays = 1,.default_display = 0,
.gpccon = 0xaa955699,.gpccon_mask = 0xffc003cc,.gpcup = 0x0000ffff,.gpcup_mask = 0xffffffff,
.gpdcon = 0xaa95aaa1,.gpdcon_mask = 0xffc0fff0,.gpdup = 0x0000faff,.gpdup_mask = 0xffffffff,.lpcsel = 0xf82,
};
代码261行:注册LCD的驱动到mini2440板卡上
源代码内容:
static struct platform_device *mini2440_devices[] __initdata = {&s3c_device_usb,&s3c_device_lcd, // LCD driver&s3c_device_wdt,&s3c_device_i2c0,&s3c_device_iis,&s3c_device_nand,&mini2440_device_eth,&s3c_device_rtc,&s3c24xx_uda134x,&s3c_device_sdi,&s3c_device_usbgadget,
};
2.2 编写LCD的配置参数
在linux-2.6.32.24/drivers/video/Kconfig文件中,编写如下内容:
源代码内容:
config FB_S3C2410_P480272tristate "4.3 inch 480X272 LCD(P43)"depends on MACH_MINI2440 && FB_S3C2410help4.3 inch 480x272 LCD(P43)
2.3 配置LCD驱动至内核
使用make menuconfig命令,打开配置页面:
Device Drivers ---> Graphics support --->
选中这两项配置:
在进入代下一级目录中:
Device Drivers --->
Graphics support --->
<*> Support for frame buffer devices --->
选中如下两项配置
配置完成后,保存配置文件
3 移植触摸功能程序
3.1 移植ADC驱动程序
根据P43的屏资料可得,其使用一线触摸功能,实现方式和ADC相关,所以在移植其他驱动前,先移植ADC驱动
3.1.1 编写ADC驱动代码
在linux-2.6.32.24/drivers/char/mini2440_adc.c文件中编写如下代码:
#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/clk.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <mach/regs-clock.h>
#include <plat/regs-timer.h>#include <plat/regs-adc.h>
#include <mach/regs-gpio.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
#include "s3c24xx-adc.h"
#undef DEBUG
//#define DEBUG
#ifdef DEBUG
#define DPRINTK(x...) {printk(__FUNCTION__"(%d): ",__LINE__);printk(##x);}
#else
#define DPRINTK(x...) (void)(0)
#endif
#define DEVICE_NAME "adc"
static void __iomem *base_addr;
typedef struct {wait_queue_head_t wait;int channel;int prescale;
}ADC_DEV;
DECLARE_MUTEX(ADC_LOCK);
static int OwnADC = 0;
static ADC_DEV adcdev;
static volatile int ev_adc = 0;
static int adc_data;
static struct clk *adc_clock;
#define ADCCON (*(volatile unsigned long *)(base_addr + S3C2410_ADCCON)) //ADC control
#define ADCTSC (*(volatile unsigned long *)(base_addr + S3C2410_ADCTSC)) //ADC touch screen control
#define ADCDLY (*(volatile unsigned long *)(base_addr + S3C2410_ADCDLY)) //ADC start or Interval Delay
#define ADCDAT0 (*(volatile unsigned long *)(base_addr + S3C2410_ADCDAT0)) //ADC conversion data 0
#define ADCDAT1 (*(volatile unsigned long *)(base_addr + S3C2410_ADCDAT1)) //ADC conversion data 1
#define ADCUPDN (*(volatile unsigned long *)(base_addr + 0x14)) //Stylus Up/Down interrupt status
#define PRESCALE_DIS (0 << 14)
#define PRESCALE_EN (1 << 14)
#define PRSCVL(x) ((x) << 6)
#define ADC_INPUT(x) ((x) << 3)
#define ADC_START (1 << 0)
#define ADC_ENDCVT (1 << 15)
#define START_ADC_AIN(ch, prescale) \do{ \ADCCON = PRESCALE_EN | PRSCVL(prescale) | ADC_INPUT((ch)) ; \ADCCON |= ADC_START; \}while(0)
static irqreturn_t adcdone_int_handler(int irq, void *dev_id)
{if (OwnADC) {adc_data = ADCDAT0 & 0x3ff;
ev_adc = 1;wake_up_interruptible(&adcdev.wait);}
return IRQ_HANDLED;
}
static ssize_t s3c2410_adc_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)
{char str[20];int value;size_t len;if (down_trylock(&ADC_LOCK) == 0) {OwnADC = 1;START_ADC_AIN(adcdev.channel, adcdev.prescale);wait_event_interruptible(adcdev.wait, ev_adc);
ev_adc = 0;
DPRINTK("AIN[%d] = 0x%04x, %d\n", adcdev.channel, adc_data, ADCCON & 0x80 ? 1:0);
value = adc_data;
OwnADC = 0;up(&ADC_LOCK);} else {value = -1;}
len = sprintf(str, "%d\n", value);if (count >= len) {int r = copy_to_user(buffer, str, len);return r ? r : len;} else {return -EINVAL;}
}
static int s3c2410_adc_open(struct inode *inode, struct file *filp)
{init_waitqueue_head(&(adcdev.wait));
adcdev.channel=0;adcdev.prescale=0xff;
DPRINTK( "adc opened\n");return 0;
}
static int s3c2410_adc_release(struct inode *inode, struct file *filp)
{DPRINTK( "adc closed\n");return 0;
}
static struct file_operations dev_fops = {owner: THIS_MODULE,open: s3c2410_adc_open,read: s3c2410_adc_read, release: s3c2410_adc_release,
};
static struct miscdevice misc = {.minor = MISC_DYNAMIC_MINOR,.name = DEVICE_NAME,.fops = &dev_fops,
};
static int __init dev_init(void)
{int ret;
base_addr=ioremap(S3C2410_PA_ADC,0x20);if (base_addr == NULL) {printk(KERN_ERR "Failed to remap register block\n");return -ENOMEM;}
adc_clock = clk_get(NULL, "adc");if (!adc_clock) {printk(KERN_ERR "failed to get adc clock source\n");return -ENOENT;}clk_enable(adc_clock);/* normal ADC */ADCTSC = 0;
ret = request_irq(IRQ_ADC, adcdone_int_handler, IRQF_SHARED, DEVICE_NAME, &adcdev);if (ret) {iounmap(base_addr);return ret;}
ret = misc_register(&misc);
printk (DEVICE_NAME"\tinitialized\n");return ret;
}
static void __exit dev_exit(void)
{free_irq(IRQ_ADC, &adcdev);iounmap(base_addr);
if (adc_clock) {clk_disable(adc_clock);clk_put(adc_clock);adc_clock = NULL;}
misc_deregister(&misc);
}
EXPORT_SYMBOL(ADC_LOCK);
module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("redistributed by tangmingfei2013@126.com");
MODULE_DESCRIPTION("Mini2440 adc Driver");
3.1.2 编写配置文件
在linux-2.6.32.24/drivers/char/Kconfig中添加如下代码:
源代码内容:
config MINI2440_ADCbool "ADC driver for mftang's Mini2440 development boards"depends on MACH_MINI2440default y if MACH_MINI2440helpthis is ADC driver for mftang's Mini2440 development boardsNotes: the touch-screen-driver required this option
在linux-2.6.32.24/drivers/char/Makefile中添加如下链接代码
3.2 移植P43的一线触摸程序
3.2.1 编写代码
在linux-2.6.32.24/drivers/input/touchscreen/mini2440_1wire_host.c文件中编写如下代码:
/* * mini2440_1wire_host.c* 2010-10-14: Russell Guo <russell.grey@gmail.com>* - Initial version* -- request touch-screen data* -- request LCD type, Firmware version* - Backlight control* the CRC-8 functions is based on web page from http://lfh1986.blogspot.com*/#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/clk.h>#include <linux/miscdevice.h>#include <linux/timer.h>#include <linux/param.h>#include <linux/poll.h>#include <linux/proc_fs.h>
#include <asm/io.h>#include <asm/irq.h>#include <asm/uaccess.h>#include <plat/regs-timer.h>#include <plat/gpio-cfg.h>#include <mach/regs-clock.h>#include <mach/regs-gpio.h>
#include <mach/gpio.h>
#include <linux/cdev.h>
#include <linux/sched.h>#include <linux/interrupt.h>#include <linux/irq.h>#include <linux/err.h>#include <linux/io.h>
#include <asm/system.h>#include <asm/leds.h>#include <asm/mach-types.h>
#include <asm/irq.h>#include <mach/map.h>#include <mach/regs-irq.h>#include <asm/mach/time.h>
#include <plat/clock.h>#include <plat/cpu.h>
#undef DEBUG#define DEBUG#ifdef DEBUG#define DPRINTK(x...) {printk("%s(%d): ",__FUNCTION__ ,__LINE__);printk(x);}#else#define DPRINTK(x...) (void)(0)#endif
#define TOUCH_DEVICE_NAME "touchscreen-1wire"#define BACKLIGHT_DEVICE_NAME "backlight-1wire"#define SAMPLE_BPS 9600
#define REQ_TS 0x40U#define REQ_INFO 0x60U
// Touch Screen driver interface//static DECLARE_WAIT_QUEUE_HEAD(ts_waitq);static int ts_ready;static unsigned ts_status;
static inline void notify_ts_data(unsigned x, unsigned y, unsigned down){if (!down && !(ts_status &(1U << 31))) {// up repeat, give it upreturn;}
ts_status = ((x << 16) | (y)) | (down << 31);ts_ready = 1;
wake_up_interruptible(&ts_waitq);}
static ssize_t ts_read(struct file *filp, char *buffer, size_t count, loff_t *ppos){unsigned long err;
if (!ts_ready) {if (filp->f_flags & O_NONBLOCK)return -EAGAIN;elsewait_event_interruptible(ts_waitq, ts_ready);}
ts_ready = 0;
if (count < sizeof ts_status) {return -EINVAL;} else {count = sizeof ts_status;}
err = copy_to_user((void *)buffer, (const void *)(&ts_status), sizeof ts_status);return err ? -EFAULT : sizeof ts_status;}
static unsigned int ts_poll( struct file *file, struct poll_table_struct *wait){unsigned int mask = 0;
poll_wait(file, &ts_waitq, wait);
if (ts_ready){mask |= POLLIN | POLLRDNORM;}
return mask;}
static struct file_operations ts_fops = {owner: THIS_MODULE,read: ts_read, poll: ts_poll,};
static struct miscdevice ts_misc = {.minor = 181,.name = TOUCH_DEVICE_NAME,.fops = &ts_fops,};
static DECLARE_WAIT_QUEUE_HEAD(bl_waitq);static int bl_ready;static unsigned char backlight_req = 0;static unsigned char backlight_init_success;
static inline void notify_bl_data(unsigned char a, unsigned char b, unsigned char c){bl_ready = 1;backlight_init_success = 1;wake_up_interruptible(&bl_waitq);}
static ssize_t bl_write(struct file *file, const char *buffer, size_t count, loff_t * ppos){int ret;char buf[4] = {0, 0, 0, 0};unsigned v;unsigned len;
if (count == 0) {return -EINVAL;}
if (count > sizeof buf - 1) {len = sizeof buf - 1;} else {len = count;}
ret = copy_from_user(buf, buffer, len);if (ret) {return -EFAULT;}
if (sscanf(buf, "%u", &v) != 1) {return -EINVAL;}
if (v > 127) {v = 127;}
bl_ready = 0;backlight_req = v + 0x80U;
ret = wait_event_interruptible_timeout(bl_waitq, bl_ready, HZ / 10);if (ret < 0) {return ret;}if (ret == 0) {return -ETIMEDOUT;}
return count;}
static struct file_operations bl_fops = {owner: THIS_MODULE,write: bl_write,};
static struct miscdevice bl_misc = {.minor = MISC_DYNAMIC_MINOR,.name = BACKLIGHT_DEVICE_NAME,.fops = &bl_fops,};
// for query base info//static unsigned lcd_type, firmware_ver;
static inline void notify_info_data(unsigned char _lcd_type, unsigned char ver_year, unsigned char week){if (_lcd_type != 0xFF) {lcd_type = _lcd_type;firmware_ver = ver_year * 100 + week;}}
// Pin access//static inline void set_pin_up(void){s3c2410_gpio_pullup(S3C2410_GPB(1), 0);}
static inline void set_pin_as_input(void){s3c2410_gpio_cfgpin(S3C2410_GPB(1), S3C2410_GPIO_INPUT);}
static inline void set_pin_as_output(void){s3c2410_gpio_cfgpin(S3C2410_GPB(1), S3C2410_GPIO_OUTPUT);}
static inline void set_pin_value(int v){s3c2410_gpio_setpin(S3C2410_GPB(1), !!v);}
static inline int get_pin_value(void){int v = !!s3c2410_gpio_getpin(S3C2410_GPB(1));return v;}
// CRC//static const unsigned char crc8_tab[] = {0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15,0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65,0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5,0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85,0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2,0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2,0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32,0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42,0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C,0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC,0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C,0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C,0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B,0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B,0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB,0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB,0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3,};
#define crc8_init(crc) ((crc) = 0XACU)#define crc8(crc, v) ( (crc) = crc8_tab[(crc) ^(v)])
// once a session completestatic unsigned total_received, total_error;static unsigned last_req, last_res;static void one_wire_session_complete(unsigned char req, unsigned int res){unsigned char crc;const unsigned char *p = (const unsigned char*)&res;total_received ++;
last_res = res;
crc8_init(crc);crc8(crc, p[3]);crc8(crc, p[2]);crc8(crc, p[1]);
if (crc != p[0]) {// CRC dismatchif (total_received > 100) {total_error++;}return;}
switch(req) {case REQ_TS:{unsigned short x,y;unsigned pressed;x = ((p[3] >> 4U) << 8U) + p[2];y = ((p[3] & 0xFU) << 8U) + p[1];pressed = (x != 0xFFFU) && (y != 0xFFFU); notify_ts_data(x, y, pressed);}break;
case REQ_INFO:notify_info_data(p[3], p[2], p[1]);break;
default:notify_bl_data(p[3], p[2], p[1]);break;}}
// one-wire protocol corestatic unsigned long TCNT_FOR_SAMPLE_BIT;static int init_timer_for_1wire(void){unsigned long tcfg1;unsigned long tcfg0;
unsigned prescale1_value;
unsigned long pclk;struct clk *clk;
// get pclkclk = clk_get(NULL, "timers");
if (IS_ERR(clk)) {DPRINTK("ERROR to get PCLK\n");return -EIO;}
pclk = clk_get_rate(clk);
// get prescalertcfg0 = __raw_readl(S3C2410_TCFG0);// we use system prescaler value because timer 4 uses same oneprescale1_value = (tcfg0 >> 8) & 0xFF;
// calc the TCNT_FOR_SAMPLE_BIT, that is one of the goalTCNT_FOR_SAMPLE_BIT = pclk / 2 / (prescale1_value + 1) / SAMPLE_BPS - 1;
// select timer 3, the 2rd goaltcfg1 = __raw_readl(S3C2410_TCFG1);tcfg1 &= ~S3C2410_TCFG1_MUX3_MASK;writel(tcfg1, S3C2410_TCFG1);
return 0;}
static inline void stop_timer_for_1wire(void){unsigned long tcon;
tcon = __raw_readl(S3C2410_TCON);tcon &= ~S3C2410_TCON_T3START;
writel(tcon, S3C2410_TCON);}
enum {IDLE,START,REQUEST,WAITING,RESPONSE,STOPING,} one_wire_status = IDLE;
static volatile unsigned int io_bit_count;static volatile unsigned int io_data;static volatile unsigned char one_wire_request;static irqreturn_t timer_for_1wire_interrupt(int irq, void *dev_id){io_bit_count--;switch(one_wire_status) {case START:if (io_bit_count == 0) {io_bit_count = 16;one_wire_status = REQUEST;}break;
case REQUEST:// Send a bitset_pin_value(io_data & (1U << 31));io_data <<= 1;if (io_bit_count == 0) {io_bit_count = 2;one_wire_status = WAITING;}break;case WAITING:if (io_bit_count == 0) {io_bit_count = 32;one_wire_status = RESPONSE;}if (io_bit_count == 1) {set_pin_as_input();set_pin_value(1);}break;case RESPONSE:// Get a bitio_data = (io_data << 1) | get_pin_value();if (io_bit_count == 0) {io_bit_count = 2;one_wire_status = STOPING;set_pin_value(1);set_pin_as_output();one_wire_session_complete(one_wire_request, io_data);}break;
case STOPING:if (io_bit_count == 0) {one_wire_status = IDLE;stop_timer_for_1wire();}break;default:stop_timer_for_1wire();}return IRQ_HANDLED;}
static struct irqaction timer_for_1wire_irq = {.name = "1-wire Timer Tick",.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,.handler = timer_for_1wire_interrupt,.dev_id = &timer_for_1wire_irq,};
static void start_one_wire_session(unsigned char req){unsigned long tcon;unsigned long flags;if (one_wire_status != IDLE) {printk("one_wire_status: %d\n", one_wire_status);return;}
one_wire_status = START;
set_pin_value(1);set_pin_as_output();// IDLE to START{unsigned char crc;crc8_init(crc);crc8(crc, req);io_data = (req << 8) + crc;io_data <<= 16;}last_req = (io_data >> 16);one_wire_request = req;io_bit_count = 1;set_pin_as_output();
writel(TCNT_FOR_SAMPLE_BIT, S3C2410_TCNTB(3));// init tranfer and start timertcon = __raw_readl(S3C2410_TCON);tcon &= ~(0xF << 16);tcon |= S3C2410_TCON_T3MANUALUPD;writel(tcon, S3C2410_TCON);
tcon |= S3C2410_TCON_T3START;tcon |= S3C2410_TCON_T3RELOAD;tcon &= ~S3C2410_TCON_T3MANUALUPD;
local_irq_save(flags);writel(tcon, S3C2410_TCON);set_pin_value(0);local_irq_restore(flags);}
// poll the device// following is Linux timer not HW timerstatic int exitting;static struct timer_list one_wire_timer;
void one_wire_timer_proc(unsigned long v){unsigned char req;if (exitting) {return;}one_wire_timer.expires = jiffies + HZ / 50;add_timer(&one_wire_timer);if (lcd_type == 0) {req = REQ_INFO;} else if (!backlight_init_success) {req = 127;} else if (backlight_req) {req = backlight_req;backlight_req = 0;} else {req = REQ_TS;}start_one_wire_session(req);
}
static struct timer_list one_wire_timer = {.function = one_wire_timer_proc,};
static int read_proc(char *buf, char **start, off_t offset, int count, int *eof, void *data){int len;len = sprintf(buf, "%u %u %u %u %04X %08X\n", lcd_type, firmware_ver, total_received, total_error, last_req, last_res);*eof = 1;
return len;}static int __init dev_init(void){int ret;
ret = misc_register(&ts_misc) | misc_register(&bl_misc) ;set_pin_up();set_pin_value(1);set_pin_as_output();
if (ret == 0) {setup_irq(IRQ_TIMER3, &timer_for_1wire_irq);ret = init_timer_for_1wire();init_timer(&one_wire_timer);one_wire_timer_proc(0);create_proc_read_entry("driver/one-wire-info", 0, NULL, read_proc, NULL);}
if (ret == 0) {printk (TOUCH_DEVICE_NAME"\tinitialized\n");printk (BACKLIGHT_DEVICE_NAME"\tinitialized\n");}
return ret;}
static void __exit dev_exit(void){exitting = 1;
remove_proc_entry("driver/one-wire-info", NULL);del_timer_sync(&one_wire_timer);free_irq(IRQ_TIMER3, &timer_for_1wire_irq);misc_deregister(&ts_misc);misc_deregister(&bl_misc);}
module_init(dev_init);module_exit(dev_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("redistributed by tangmingfei2013@126.com");MODULE_DESCRIPTION("Mini2440 one-wire host and Touch Screen Driver");
3.2.2 编写配置链接文件
在linux-2.6.32.24/drivers/input/touchscreen/Kconfig文件中编写如下代码:
代码内容:
config TOUCHSCREEN_1WIREtristate "Mini2440 1-Wire host and Touch Screen Driver by mftang "depends on MACH_MINI2440helpSay Y here to enable the 1-Wire host and Touch Screen driver forFriendlyARM Mini2440 development board.
If unsure, say N.
To compile this driver as a module, choose M here: themodule will be called mini2440_1wire_host.
在linux-2.6.32.24/drivers/input/touchscreen/Makefile中编写链接代码:
3.3 移植S3C2410触摸功能程序
3.3.1 编写驱动代码
在linux-2.6.32.24/drivers/input/touchscreen/s3c2410_ts.c文件中编写如下代码:
#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/gpio.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <plat/regs-adc.h>
#include <mach/regs-gpio.h>
/* For ts.dev.id.version */
#define S3C2410TSVERSION 0x0101
#define WAIT4INT(x) (((x)<<8) | \S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \S3C2410_ADCTSC_XY_PST(3))
#define AUTOPST (S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \S3C2410_ADCTSC_AUTO_PST | S3C2410_ADCTSC_XY_PST(0))
static char *s3c2410ts_name = "s3c2410 TouchScreen";
static struct input_dev *dev;
static long xp;
static long yp;
static int count;
extern struct semaphore ADC_LOCK;
static int OwnADC = 0;
static void __iomem *base_addr;
static inline void s3c2410_ts_connect(void)
{s3c2410_gpio_cfgpin(S3C2410_GPG(12), S3C2410_GPG12_XMON);s3c2410_gpio_cfgpin(S3C2410_GPG(13), S3C2410_GPG13_nXPON);s3c2410_gpio_cfgpin(S3C2410_GPG(14), S3C2410_GPG14_YMON);s3c2410_gpio_cfgpin(S3C2410_GPG(15), S3C2410_GPG15_nYPON);
}
static void touch_timer_fire(unsigned long data)
{unsigned long data0;unsigned long data1;int updown;
data0 = ioread32(base_addr+S3C2410_ADCDAT0);data1 = ioread32(base_addr+S3C2410_ADCDAT1);
updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));
if (updown) {if (count != 0) {long tmp;tmp = xp;xp = yp;yp = tmp;xp >>= 2;yp >>= 2;
input_report_abs(dev, ABS_X, xp);input_report_abs(dev, ABS_Y, yp);
input_report_key(dev, BTN_TOUCH, 1);input_report_abs(dev, ABS_PRESSURE, 1);input_sync(dev);}
xp = 0;yp = 0;count = 0;
iowrite32(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);iowrite32(ioread32(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);} else {count = 0;
input_report_key(dev, BTN_TOUCH, 0);input_report_abs(dev, ABS_PRESSURE, 0);input_sync(dev);
iowrite32(WAIT4INT(0), base_addr+S3C2410_ADCTSC);if (OwnADC) {OwnADC = 0;up(&ADC_LOCK);}}
}
static struct timer_list touch_timer = TIMER_INITIALIZER(touch_timer_fire, 0, 0);
static irqreturn_t stylus_updown(int irq, void *dev_id)
{unsigned long data0;unsigned long data1;int updown;
if (down_trylock(&ADC_LOCK) == 0) {OwnADC = 1;data0 = ioread32(base_addr+S3C2410_ADCDAT0);data1 = ioread32(base_addr+S3C2410_ADCDAT1);
updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));
if (updown) {touch_timer_fire(0);} else {OwnADC = 0;up(&ADC_LOCK);}}
return IRQ_HANDLED;
}
static irqreturn_t stylus_action(int irq, void *dev_id)
{unsigned long data0;unsigned long data1;
if (OwnADC) {data0 = ioread32(base_addr+S3C2410_ADCDAT0);data1 = ioread32(base_addr+S3C2410_ADCDAT1);
xp += data0 & S3C2410_ADCDAT0_XPDATA_MASK;yp += data1 & S3C2410_ADCDAT1_YPDATA_MASK;count++;
if (count < (1<<2)) {iowrite32(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);iowrite32(ioread32(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);} else {mod_timer(&touch_timer, jiffies+1);iowrite32(WAIT4INT(1), base_addr+S3C2410_ADCTSC);}}
return IRQ_HANDLED;
}
static struct clk *adc_clock;
static int __init s3c2410ts_init(void)
{struct input_dev *input_dev;
adc_clock = clk_get(NULL, "adc");if (!adc_clock) {printk(KERN_ERR "failed to get adc clock source\n");return -ENOENT;}clk_enable(adc_clock);
base_addr=ioremap(S3C2410_PA_ADC,0x20);if (base_addr == NULL) {printk(KERN_ERR "Failed to remap register block\n");return -ENOMEM;}
/* Configure GPIOs */s3c2410_ts_connect();
iowrite32(S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(0xFF),\base_addr+S3C2410_ADCCON);iowrite32(0xffff, base_addr+S3C2410_ADCDLY);iowrite32(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
/* Initialise input stuff */input_dev = input_allocate_device();
if (!input_dev) {printk(KERN_ERR "Unable to allocate the input device !!\n");return -ENOMEM;}
dev = input_dev;dev->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);dev->keybit[BITS_TO_LONGS(BTN_TOUCH)] = BIT(BTN_TOUCH);input_set_abs_params(dev, ABS_X, 0, 0x3FF, 0, 0);input_set_abs_params(dev, ABS_Y, 0, 0x3FF, 0, 0);input_set_abs_params(dev, ABS_PRESSURE, 0, 1, 0, 0);
dev->name = s3c2410ts_name;dev->id.bustype = BUS_RS232;dev->id.vendor = 0xDEAD;dev->id.product = 0xBEEF;dev->id.version = S3C2410TSVERSION;
/* Get irqs */if (request_irq(IRQ_ADC, stylus_action, IRQF_SHARED|IRQF_SAMPLE_RANDOM,"s3c2410_action", dev)) {printk(KERN_ERR "s3c2410_ts.c: Could not allocate ts IRQ_ADC !\n");iounmap(base_addr);return -EIO;}if (request_irq(IRQ_TC, stylus_updown, IRQF_SAMPLE_RANDOM,"s3c2410_action", dev)) {printk(KERN_ERR "s3c2410_ts.c: Could not allocate ts IRQ_TC !\n");iounmap(base_addr);return -EIO;}
printk(KERN_INFO "%s successfully loaded\n", s3c2410ts_name);
/* All went ok, so register to the input system */input_register_device(dev);
return 0;
}
static void __exit s3c2410ts_exit(void)
{disable_irq(IRQ_ADC);disable_irq(IRQ_TC);free_irq(IRQ_TC,dev);free_irq(IRQ_ADC,dev);
if (adc_clock) {clk_disable(adc_clock);clk_put(adc_clock);adc_clock = NULL;}
input_unregister_device(dev);iounmap(base_addr);
}
module_init(s3c2410ts_init);
module_exit(s3c2410ts_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("redistributed by tangmingfei2013@126.com");
MODULE_DESCRIPTION("Mini2440 Touch Screen Driver");
3.3.2 编写配置链接文件
在linux-2.6.32.24/drivers/input/touchscreen/Kconfig中添加代码:
源代码:
config TOUCHSCREEN_S3C2410tristate "Samsung S3C2410 touchscreen input driver by mftang "depends on MACH_MINI2440 && INPUT && INPUT_TOUCHSCREEN && MINI2440_ADChelpSay Y here if you have the s3c2410 touchscreen.
If unsure, say N.
To compile this driver as a module, choose M here: themodule will be called s3c2410_ts.
在linux-2.6.32.24/drivers/input/touchscreen/Makefile中编写链接代码:
源代码:
obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o
3.4 在内核中查看触摸功能的配置
使用make menuconfig命令,运行命令后,进入:
在进入代下一级目录中:
Device Drivers --->Input device support ---><*> Touchscreens --->
选中如下两项配置
退出配置界面,然后编译内核
4 功能测试
编译完成内核,生成zImage文件
下载zImage到mini2440,重新启动开发板:
屏幕能正常显示UI,触摸功能也能正常工作。