这篇驱动移植文章,目的是实现相应的功能,分享我在做移植过程中所碰到的一些问题以及解决的方法。
交叉编译器环境:arm-linux-gcc-4.5.4
开发板平台:FL2440
Linux内核版本: 3.0
贴上修改代码:
--- mach-smdk2440_o.c 2015-04-24 11:39:20.492017397 +0800 +++ mach-smdk2440.c 2015-05-17 16:27:25.250018401 +0800 @@ -46,6 +46,33 @@ #include <plat/cpu.h> #include <plat/common-smdk.h> +#include <sound/s3c24xx_uda134x.h> +#include <linux/dm9000.h> +#include <plat/ts.h> +#include <mach/regs-clock.h> //USB add by Handy 2015.5.17 +#include <linux/delay.h> //USB add by Handy 2015.5.17 +#define MACH_SMDK2440_DM9K_BASE (S3C2410_CS4 + 0x300) + +/* USB */ +int usb_s3c2440_init(void) +{ + unsigned long upllvalue= (0x38<<12)|(0x02<<4)|(0x02); + while (upllvalue != __raw_readl(S3C2410_UPLLCON)) + { + __raw_writel(upllvalue, S3C2410_UPLLCON); + mdelay(1); + } + return 0; +} + +/* Touch Screen info add by Handy,2015.5.14 */ +#if 1 +static struct s3c2410_ts_mach_info smdk2440_ts_cfg __initdata = { + .delay = 10000, +}; +#endif static struct map_desc smdk2440_iodesc[] __initdata = { /* ISA IO Space map (memory space selected by A24) */ .type = S3C2410_LCDCON1_TFT, //选择TFT模式 - .width = 240,//宽 - .height = 320,//高 + .width = 320, //width和height表示屏幕的分辨率,我的分辨率是320X240 + .height = 240, +#if 0 .pixclock = 166667, /* HCLK 60 MHz, divisor 10 */ .xres = 240, .yres = 320, @@ -127,6 +155,33 @@ .upper_margin = 8, .lower_margin = 7, .vsync_len = 4, + /* orignal data */ +#endif + +#if 0 /* For 3.5" TFT-LCD (WXCAT35-TG3#001) */ + .pixclock = 156250, //像素时钟 + .xres = 320, //水平可见的有效像素;xres和yres分别等于width和height + .yres = 240, //垂直可见的有效像素 + .bpp = 16, //bpp表示每个像素点位数,即色位模式;这里是16位. + .left_margin = 38, --> Thb 行切换,从同步到绘图之间的延迟 + .right_margin = 20, --> Thf 行切换,从绘图到同步之间的延迟 + .hsync_len = 30, --> Thp 水平同步的长度 + .upper_margin = 15, --> Tvb 帧切换,从同步到绘图之间的延迟 + .lower_margin = 12, --> Tvf 帧切换,从绘图到同步之间的延迟 + .vsync_len = 3,<span style="white-space:pre"> </span>//垂直同步的长度 +#else /* For 4.3" TFT-LCD (WXCAT43-TG3#001) */ + .pixclock = 111111, + .xres = 480, + .yres = 272, + .bpp = 16, + .left_margin = 38, /* for HFPD*/ + .right_margin = 20, /* for HBPD*/ + .hsync_len = 30, /* for HSPW*/ + .upper_margin = 15, /* for VBPD*/ + .lower_margin = 12, /* for VFPD*/ + .vsync_len = 3, /* for VSPW*/ +#endif }; static struct s3c2410fb_mach_info smdk2440_fb_info __initdata = { @@ -146,15 +201,80 @@ .gpdup_mask = 0xffffffff, #endif - .lpcsel = ((0xCE6) & ~7) | 1<<4, + .lpcsel = ((0xCE6) & ~7) | 1<<1, }; +/* DM9000AEP 10/100 ethernet controller */ + +static struct resource smdk2440_dm9k_resource[] = { + [0] = { + .start = MACH_SMDK2440_DM9K_BASE, + .end = MACH_SMDK2440_DM9K_BASE + 3, + .flags = IORESOURCE_MEM + }, + [1] = { + .start = MACH_SMDK2440_DM9K_BASE + 4, + .end = MACH_SMDK2440_DM9K_BASE + 7, + .flags = IORESOURCE_MEM + }, + [2] = { + .start = IRQ_EINT7, + .end = IRQ_EINT7, + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE, + } +}; + +/* + * The DM9000 has no eeprom, and it's MAC address is set by + * the bootloader before starting the kernel. + */ +static struct dm9000_plat_data smdk2440_dm9k_pdata = { + .flags = (DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM), +}; + +static struct platform_device smdk2440_device_eth = { + .name = "dm9000", + .id = -1, + .num_resources = ARRAY_SIZE(smdk2440_dm9k_resource), + .resource = smdk2440_dm9k_resource, + .dev = { + .platform_data = &smdk2440_dm9k_pdata, + }, +}; + +/* AUDIO */ + +static struct s3c24xx_uda134x_platform_data smdk2440_audio_pins = { + .l3_clk = S3C2410_GPB(4), + .l3_mode = S3C2410_GPB(2), + .l3_data = S3C2410_GPB(3), + .model = UDA134X_UDA1341 +}; + +static struct platform_device smdk2440_audio = { + .name = "s3c24xx_uda134x", + .id = 0, + .dev = { + .platform_data = &smdk2440_audio_pins, + }, +}; +static struct platform_device uda1340_codec = { + .name = "uda134x-codec", + .id = -1, +}; static struct platform_device *smdk2440_devices[] __initdata = { &s3c_device_ohci, &s3c_device_lcd, tspatch &s3c_device_wdt, &s3c_device_i2c0, &s3c_device_iis, + &s3c_device_rtc, + &uda1340_codec,/* add by Handy */ + &smdk2440_audio,/* add by Handy */ + &samsung_asoc_dma,/* add by Handy */ + &smdk2440_device_eth,/* add by Handy */ + &s3c_device_adc,/* add Touch Screen driver by Handy,2015.5.14 */ + &s3c_device_ts, /* add Touch Screen driver by Handy,2015.5.14 */ }; static void __init smdk2440_map_io(void) @@ -162,11 +282,13 @@ s3c24xx_init_io(smdk2440_iodesc, ARRAY_SIZE(smdk2440_iodesc)); s3c24xx_init_clocks(12000000); s3c24xx_init_uarts(smdk2440_uartcfgs, ARRAY_SIZE(smdk2440_uartcfgs)); + usb_s3c2440_init();//USB add by Handy 2015.5.17 } static void __init smdk2440_machine_init(void) { s3c24xx_fb_set_platdata(&smdk2440_fb_info); + s3c24xx_ts_set_platdata(&smdk2440_ts_cfg);/* add Touch screen info by Handy,2015.5.14 */ s3c_i2c0_set_platdata(NULL); platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));
这里有个要注意的地方,就是同样的代码,有的人能移植成功,有的人同样的代码却出现定义了未使用以及未定义的错误。我也出现了这个错误,这个问题很低级,就是所有需要使用到的函数都必须先将它声明,也就是说。我们移植的时候添加结构体和函数时要注意添加的顺序。编译器是按顺序编译,编译器是按顺序编译,编译器是按顺序编译( 重要的事情说三遍)。
--- ../../../../linux-3.0/drivers/input/touchscreen/s3c2410_ts.c 2015-03-29 11:46:22.159022388 +0800 +++ s3c2410_ts.c 2015-05-17 15:16:36.971018373 +0800 @@ -126,7 +126,7 @@ input_report_abs(ts.input, ABS_Y, ts.yp); input_report_key(ts.input, BTN_TOUCH, 1); + input_report_abs(ts.input, ABS_PRESSURE, 1);//pressure args add by Handy 2015.5.17 input_sync(ts.input); ts.xp = 0; @@ -141,7 +141,7 @@ ts.count = 0; input_report_key(ts.input, BTN_TOUCH, 0); + input_report_abs(ts.input, ABS_PRESSURE, 0);//pressure args add by Handy 2015.5.17 input_sync(ts.input); writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC); @@ -316,12 +316,12 @@ } ts.input = input_dev; ts.input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); ts.input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); input_set_abs_params(ts.input, ABS_X, 0, 0x3FF, 0, 0); input_set_abs_params(ts.input, ABS_Y, 0, 0x3FF, 0, 0); + input_set_abs_params(ts.input, ABS_PRESSURE, 0, 1, 0, 0);//pressure args add by Handy 2015.5.17 ts.input->name = "S3C24XX TouchScreen"; ts.input->id.bustype = BUS_HOST; ts.input->id.vendor = 0xDEAD;
/*内核自带的触摸屏驱动有BUG,需要修改才能正常使用,具体修改内容请参考linux源码包中的s3c2410_ts.c文件*/
LCD:
对照4.3寸的detasheet,参考了网上的文档,我发现各自的参数不一,而且都没有清楚的解释。所以我现在看手册知道了对应的参数,但是具体怎么计算,根据参数来取多大的值我还不是很清楚。如有知道的,麻烦评论告知。
VBPD(vertical back porch):表示在一帧图像开始时,垂直同步信号以后的无效的行数,对应驱动中的upper_margin;
VFBD(vertical front porch):表示在一帧图像结束后,垂直同步信号以前的无效的行数,对应驱动中的lower_margin;
VSPW(vertical sync pulse width):表示垂直同步脉冲的宽度,用行数计算,对应驱动中的vsync_len;
HBPD(horizontal back porch):表示从水平同步信号开始到一行的有效数据开始之间的VCLK的个数,对应驱动中的left_margin;
HFPD(horizontal front porth):表示一行的有效数据结束到下一个水平同步信号开始之间的VCLK的个数,对应驱动中的right_margin;
HSPW(horizontal sync pulse width):表示水平同步信号的宽度,用VCLK计算,对应驱动中的hsync_len;
2. 内核的配置
LCD:
USB:
● 添加USB控制器支持:
[*] USB support --->
{*} Support for Host-side USB
[*] USB device filesystem (DEPRECATED)
[*] USB device class-devices (DEPRECATED)
<*> OHCI HCD support
[*] HID Devices --->
-*- Generic HID support
<*> USB Human Interface Device (full HID) support
● 添加U盘支持:
SCSI device support ---><*> SCSI device support
[*] legacy /proc/scsi/ support
<*> SCSI disk support
<*> SCSI generic support
<*> SCSI media changer support
PS:HID是Human Interface Devices的缩写.翻译成中文即为人机交互设备.这里的人机交互设备是一个宏观上面的概念,任何设备,只要符合HID spec,都可以称之为HID设备
● 添加文件系统及语言配置:
File systems --->
DOS/FAT/NT Filesystems --->
<*> MSDOS fs support
<*> VFAT (Windows-95) fs support
(437) Default codepage for FAT
(iso8859-1) Default iocharset for FAT
<*> NTFS file system support
[*] NTFS write support
Partition Types --->
[*] PC BIOS (MSDOS partition tables) support
[*] Windows Logical Disk Manager (Dynamic Disk) support
-*- Native language support --->
<*> Codepage 437 (United States, Canada)
<*> Simplified Chinese charset (CP936, GB2312)
<*> ASCII (United States)
<*> NLS ISO 8859-1 (Latin 1; Western European Languages)
<*> NLS UTF-8
小插曲:要先添加对U盘的支持才能按出<*> USB Mass Storage support;不然只能一直按出空格和M,因为该选项依赖于SCSI。(如上图)
这是内核配置,至于在文件系统中我们为了方便应为利用mdev来实现USB的自动挂载,我以前的根文件系统那篇文章里面有实现这部分功能的shell代码。而LCD我们的内核原本已经支持了,所以我们只需要添加代码即可实现简单的屏幕显示企鹅功能。
触摸屏:
关于fl2440的触摸屏的移植,首先要了解关于输入设备是怎样与用户进行交互的。
在linux的输入子系统中,低层驱动(例如按键被按下,触摸屏被触摸,鼠标点击等)都会发生相应的事件,而驱动就会把事件提交给上层。因为触摸屏的接口与ADC的接口是集成在一起的,触摸屏要正常的工作就离不开ADC的转换。 当然,在这里我们的触摸屏是电阻屏TS。
上图是功能方框图,假如我们在触摸屏上按下,它会产生x,y方向上的两个模拟信号(xp,yp)进入到AD转换器,然后再触发相应的中断(分为ADC和TC两种)来进行处理,然后在中断处理程序中来上报数据。
移植成功后会在内核里的打印信息中有加载信息。
cat dev/event0 中可以显示触摸信息,开始是一些乱码,如果要精确显示坐标信息,还要移植tslib这个程序来进行校准。(每个人的touchscreen设备节点位置和名字都不一样,如有的在/dev/input/event1;所以要对照自己的查找确认)
移植触摸屏校正Tslib1.4
1.解压下载的tslib1.4
# tar xzvf tslib-1.4.tar.gz
2.进入生成的目录/home/pikaqiu/tslib,执行脚本文件
# ./autogen.sh
3./tslib下执行configure
# sudo ./configure --host=arm-linux ac_cv_func_malloc_0_nonnull=yes --prefix=/usr/local/arm/tslib CC=/opt/buildroot-2012.08/arm920t/usr/bin/arm-linux-gcc
经过make编译,如果无错误,执行make install,安装后,tslib已经安装到/usr/local/arm/tslib目录下
4.修改/usr/local/arm/tslib/etc/ts.conf
去掉第二行的#,并同时删除空格。如果不去掉,测试时会出现段错误(Segmentation fault),另外若在开发板上出现line 1: syntax error: unexpected "(" 则是没有指定交叉编译环境。CC=
5.移植至开发板
将安装路径下的整个tslib文件夹,下载至开发板的上,我存放的路径为/usr/local/arm/tslib
6.设置开发板环境变量
# vi /etc/profile
添加后为如下内容:
# Touch Scree tslib Setting
export TSLIB_ROOT=/apps/tslib
export TSLIB_CONFFILE=$TSLIB_ROOT/etc/ts.conf // 指定 TSLIB 配置文件的位置
export TSLIB_CALIBFILE=$TSLIB_ROOT/etc/pointercal // 指定触摸屏校准文件 pintercal 的存放位置
export TSLIB_TSDEVICE=/dev/event0 // 指定触屏设备
export TSLIB_CONSOLEDEVICE=none //设定控制台设备为 none,否则默认为/dev/tty,这样可以避免出现“open consoledevice: No such file or directory KDSETMODE: Bad file descriptor”的错误
export TSLIB_FBDEVICE=/dev/fb0 // 指定帧缓冲设备
export TSLIB_PLUGINDIR=$TSLIB_ROOT/lib/ts // 指定触摸屏插件所在路径
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$TSLIB_ROOT/lib //另外指定好库文件查找路径
对于各自的event0和fb0位置可能不同,具体的对照自己touchscreen的设备节点的来改。
7.校验程序生成校验文件。
./ts_calibrate
如上图即成功。
以上是我参考并综合网上前辈的文档后,能一次性移植成功。
我在移植摸索过程中所遇到的错误这个文档里面基本都有比较好的回答,如果有错误可以直接进去参考。
http://blog.csdn.net/xdw1985829/article/details/6670523
另外:我第一次到板子上执行后出现 过:selected device is not a touchscreen I understand
通过打印出来的selected device is not a touchscreen I understand
我们可以用grep工具追踪发现错误发生在tslib中的plugins目录下input-raw.c文件中的check_fd函数
static intcheck_fd(struct tslib_input *i) { structtsdev *ts = i->module.dev; intversion; u_int32_tbit; u_int64_tabsbit; if (!((ioctl(ts->fd, EVIOCGVERSION, &version) >= 0) && (version == EV_VERSION) && (ioctl(ts->fd, EVIOCGBIT(0, sizeof(bit) *8), &bit) >= 0) && (bit & (1 << EV_ABS)) && (ioctl(ts->fd, EVIOCGBIT(EV_ABS,sizeof(absbit) * 8), &absbit) >= 0) && (absbit & (1 << ABS_X)) && (absbit & (1 << ABS_Y)) &&(absbit & (1 << ABS_PRESSURE)))) { fprintf(stderr,"selected device is not a touchscreen I understand "); return-1; } if(bit & (1 << EV_SYN)) i->using_syn= 1; return0; }
所以有多种原因会导致出现这个错误,简单来说:
1. TS设备节点不对也就是说你选择的event0并不是TouchScreen设备节点。这个可以通过cat event0并点击触摸屏,看有没有乱码打印在输出上面即可确定。
2.交叉编译器的input.h里面的版本号EV_VERSION和你内核的input.h里面的EV_VERSION不一致。
3.内核驱动里面没有ABS_PRESSURE压力函数,需要自己在linux内核的drivers里面添加。
在drivers/input/touchscreen/s3c2410_ts.c中添加函数ABS_PRESSURE(),更具体的可以参照这篇文档。http://blog.csdn.net/zhaocj/article/details/37522115
http://blog.csdn.net/liuzijiang1123/article/details/45601981 //USB
http://www.embedu.org/Column/Column687.htm //USB