Linux内核升级LCD驱动的更换(开发板)

Linux内核升级LCD驱动的更换(开发板)

关键字

 

 

内核升级 ,更换驱动 ,LCD

 

 

 

 

本文给出了将一个已有的LCD驱动添加进一个新的linux内核中的方法

 


一. 概述

本文搜集整理了Linux系统编译时的主要配置选项(make config)的详细说明,供Linux裁剪特别是设备驱动和模块功能增删时使用参考。需要注意的是,每个版本linux版本的config各选项意义,命名等都可能有所差异。

1、 修改内核根目录config文件

文本方式打开config文件,将LCD的几个编译开关添加进Frame buffer hardware drivers这一项目下,之后在make menuconfig中就可以看到。

#

# Frame buffer hardware drivers

#

CONFIG_FB_SIDSA=y

CONFIG_FB_TCM=y

# CONFIG_FB_SDRAM is not set

CONFIG_TFT_AT91=y

CONFIG_FB_SIDSA_DEFAULT_BPP=16

# CONFIG_FB_SIDSA_DEBUG is not set

在控制台显示驱动项目中将linux logo显示的编译开关打开,logo显示16色或其他均可。

#

# Console display driver support

#

CONFIG_LOGO=y

# CONFIG_LOGO_LINUX_MONO is not set

CONFIG_LOGO_LINUX_VGA16=y

添加好上述config选项后,使用make menuconfig打开该config文件,即可看到这些选项,一一进行确认:

LCD的配置项位于drivers->graphices support下 分别将frame buffer和logo的选项选中,才能使用逻辑屏和logo图片。Bootup logo中可选黑白图,16色图或256色图其中一种均可。

Frame buffer的设备驱动选中SIDSA控制器,目前逻辑屏比较小为QVGA,数据源选为TCM等选项。开发板具有内置大约1M多的SRAM,当逻辑屏比较大时,应选SDRAM.

2、 添加屏驱动源码到工程目录

Linux的屏驱动一般位于drivers/video 目录下,目前AT91SAM926开发板使用的LCD驱动是sidsafb.c和sidsafb.h,at91sam9261_lcdc.h三个文件,在标准的linux2.6.25上是没有的,从开发板源码包中复制过来放到该目录下(还要进行一些代码修改,后面的步骤会讲到)。

3、修改逻辑屏驱动模块的Makefile文件

打开drivers/video目录下的Makefile文件,在其中添加sidsafb.o文件的编译选项,依赖于编译开关CONFIG_FB_SIDSA,也可使用文件比较的方式和开发板同名makefile比较,将下图蓝色方框部分复制到你的makefile

4、修改模块的Kconfig文件

打开drivers/video目录下的Kconfig文件,添加FB_SIDSA, FB_TCM等几个编译开关的配置,也可使用文件比较器将开发板源码包下的同名Kconfig文件比较,将下图蓝色方框部分的几个编译开关选项复制添加到你的drivers/video/Kconfig中,如下图:

5、源代码的修改

移植驱动时有两种工作方式,一种是所有驱动同时一并移植,这样做的好处是各驱动模块公共文件如寄存器表,相关宏的头文件放置的比较合理,移植完后清晰,缺点是交叉错误多,调试工作量大,一两个人无法完成。另一种是单个模块串行一一移植,好处是调试比较好调,缺点是移植完代码可能比较乱。综合一下,个人认为驱动整合移植时先移植所有公共的头文件,公共的功能函数和宏,不添加具体模块,编译出一个基本版本,在此版本基础上在分头移植各个模块比较合理。

单驱动模块移植时,总结下来源代码的修改主要包括几个部分:

a) 驱动的依赖关系整理清楚,若本驱动还依赖其他驱动,则还需要先移植添加其他驱动模块

b) 理清所添加驱动对外部变量,函数,宏的引用关系,去除编译错误。

c) 修改虚拟地址->物理地址的映射函数为当前平台的映射函数,使寄存器map表映射正确。

d) 修改驱动的注册和加载部分。

具体改动如下:(此部分改动已有改好的版本在FTP,列举只是为了说明问题和给出方法)

模块依赖关系和编译错误的整理

由于本开发板的屏是RGB屏,所以实际驱动是由AT91所带的LCDC控制器来完成,因此屏的驱动主要工作是配置LCDC和管理Frame buffer两部分。LCDC依赖于另外两个模块:IO和CLOCK(即代码中的PIO和PMC模块),IO用来控制LCDC的电源,CLOCK用来给出LCDC时序的时基。所以先要添加PIO和PMC部分的代码.为了不至于要挂载整个PIO和PMC模块,开发板的BSP将一些简单的功能独立出来作为函数供其他模块调用。

从开发板源码中找到PIO的寄存器定义相关宏,拷贝出来添加到sidsafb.c头部或sidsafb.h,不嫌麻烦的话也可另建头文件,PIO的寄存器主要是这些宏:

#define PIO_PER (0x0000) /**< PIO Enable Register */

#define PIO_PDR (0x0004) /**< PIO Disable Register */

#define PIO_PSR (0x0008) /**< PIO Status Register */

#define PIO_OER (0x0010) /**< Output Enable Register */

#define PIO_ODR (0x0014) /**< Output Disable Registerr */

#define PIO_OSR (0x0018) /**< Output Status Register */

#define PIO_IFER (0x0020) /**< Input Filter Enable Register */

………

同样添加PMC模块的寄存器宏,主要是这些:

#define PMC_SCER (0x0000) /**< System Clock Enable Register */

#define PMC_SCDR (0x0004) /**< System Clock Disable Register */

#define PMC_SCSR (0x0008) /**< System Clock Status Register */

#define PMC_PCER (0x0010) /**< Peripheral Clock Enable Register */

#define PMC_PCDR (0x0014) /**< Peripheral Clock Disable Register */

#define PMC_PCSR (0x0018) /**< Peripheral Clock Status Register */

#define PMC_MOR (0x0020) /**< Main Oscillator Register */

……..

添加各模块寄存器读写的宏

#define readreg_pmc(offset) readl(AT91C_VA_BASE_PMC + offset)

#define writereg_pmc(value, offset) writel(value, (AT91C_VA_BASE_PMC + offset))

有了这些宏,对IO和CLOCK的操作函数基本就可以加进来编译,屏驱动中引用到的操作函数主要是:

at91_gpio_set_level

at91_gpio_periph_enable

at91_gpio_configure

at91_device_pio_setup

at91_enable_periph_clock

at91_disable_system_clock

at91_enable_system_clock

at91_lcdc_clock_enable

at91_lcdc_power_up

虚拟地址-物理地址转换函数的修改

去除上述产生的所有编译错误,将其他一些零碎的未定义的对象添加进来,基本就可以编译通过了。但还不能运行,一般地讲,两个linux版本的虚拟地址到物理地址的转换关系是不一致的,而驱动对寄存器的访问都是通过虚拟地址进行,因此要使驱动能在新的linux内核上运行,必须使用该linux的虚拟地址-物理地址转换公式,获取到正确的寄存器虚拟地址。

Linux系统的设备内存映射都映射到系统空间(0xc00000000-0xFFFFFFFF)的固定映射区之内,映射方式是线性、连续的,因此设备的物理地址和虚拟地址只相差一个常数偏移量,当物理内存小于896M时,高端映射区未用,固定映射区的首地址一般就是紧接在vmalloc区的后面,不同的版本不同的芯片体系中vmalloc区的尾地址定义有所不同,在vmalloc.h中定义:

Linux 2.6.15-AT91定义为

#define VMALLOC_END (0xFF000000 - 0x00200000)

Linux 2.6.25-AT91定义为

#define VMALLOC_END (AT91_VIRT_BASE & PGDIR_MASK)

在Linux2.6.15-AT91体系中,物理地址到虚拟地址的转换相差的常数依赖于VMALLOC_END(/include/asm-arm/arch-at91sam9261/hardware.h):

#define AT91C_IO_PHYS_BASE 0xFFFFF000

#define AT91C_IO_VIRT_BASE VMALLOC_END

/* Convert a physical IO address to virtual IO address */

#define AT91_IO_P2V(x) ((x) - AT91C_IO_PHYS_BASE + AT91C_IO_VIRT_BASE)

即物理地址转换为虚拟地址时,虚拟地址=物理地址-(0xFFFFF000- VMALLOC_END)

在Linux2.6.25-AT91体系中,IO物理地址到物理地址转换公式为:

#define AT91_IO_P2V(x) ((x) - AT91_IO_PHYS_BASE + AT91_IO_VIRT_BASE)

但其物理基地址AT91_IO_PHYS_BASE和虚拟基地址AT91_IO_VIRT_BASE和Linux2.6.15版本均不同,(由于对其的长度不同,因此IO物理基地址可能不同版本都不同,但具体到任意一个寄存器的物理地址,最终换算出来基地址+OFFSET都是一样的),因此寄存器的访问,必须采用当前体系的基地址,当前体系的OFFSET,当前体系的映射方式AT91_IO_P2V,才能得到当前体系下对应的正确的虚拟地址。

代码中将旧的映射函数AT91_IO_P2V屏蔽不用,采用新版本同名的AT91_IO_P2V映射函数,并将所有使用到的寄存器基地址,偏移量修改为当前体系的值,此处只用到LCDC,PIO,PMC三片寄存器,将其基地址改为当前版本定义值:

#define AT91C_VA_BASE_PIOA AT91_IO_P2V(AT91C_BASE_PIOA)

#define AT91C_VA_BASE_PMC AT91_IO_P2V(AT91C_BASE_PMC)

改为

#define AT91C_VA_BASE_PIOA AT91_IO_P2V(AT91_PIOA)

#define AT91C_VA_BASE_PMC AT91_IO_P2V(AT91_PMC)

至此,在当前体系下寄存器的虚拟地址就可以正确地得到了,使用虚拟地址去配置寄存器,才不会产生分页错,data abort之类地内存错误,
设备注册和初始化的修改

设备的注册和初始化一般位于arch/arm/对应体系目录下,使用AT91芯片时,主要是at91sam9261.c,at91sam9261_devices.c和board-sam9261ek.c三个代码文件和一些.h文件,第一个at91sam9261.c负责总的调用和定义,第二个at91sam9261_devices.c负责主要片内控制器的定义,初始化,其中LCDC就包含在这里面。第三个board-sam9261ek.c负责片外板上设备的定义和初始化,同一款芯片不同的板子不同的硬件在这里区别。

在at91sam9261_devices.c中找到LCD的定义和初始化,标准Linux2.6.25使用的是CONFIG_FB_ATMEL,将其改为当前开发板对应的代码,主要是RGB屏时序的定义,FB的位置区域,和普通设备device不一样,LCDC属于平台标配设备,其类型为platform_device,需要定义对DMA的使用等,将其设备名改为sidsafb.c文件中定义的"sidsa-lcdc",这样启动时才能找到"sidsa-lcdc"并将其挂载。这样,LCD驱动就移植完毕,编译通过,烧写后启动即可亮屏,如果想修改logo图片,到drivers/video/logo目录下修改图片,或直接修改其对应的数组。

LCDC的设备是:

static struct platform_device at91_lcdc_device = {

.name = "sidsa-lcdc",

.id = 0,

.num_resources = ARRAY_SIZE(lcdc_resources),

.resource = lcdc_resources,

.dev = {

.dma_mask = &lcdc_dmamask,

.coherent_dma_mask = DMA_BIT_MASK(32),

.platform_data = &lcdc_data,

},

};

调试过程中可在关键的函数中使用printk添加一些打印语句,如:

init/main.c中的start_kernel函数是内核启动的第一个函数,在这里进行各项初始化,可在其中加一些打印观察情况;

at91sam9260.c和at91sam9261_devices.c中进行芯片设备的加载,可加一些打印,观察哪些设备加载成功,哪些加载失败。

观察所加的设备"sidsa-lcdc”是否被正常挂载,可在设备的初始化函数sidsafb_init中添加打印,观察是否进入其中初始化,且注册设备driver_register时返回的值是否正确等。

编译时,要使用AT91-SAM926的config文件,由于每个板子的内存基地址和长度不一样,因此要在config中定义,也可以在代码中定义:(include/asm-arm/arch-at91/hardware.h)

#define AT91_SDRAM_BASE 0x20000000

屏基本上可以亮了,但还有个bug,就是显示完logo后之后一会儿就灭掉了,这是因为Linux2.6.25版本上的默认BSP时钟使用情况和现有开发板不一致,LCD定义的CLOCK有冲突,将如下CLK定义宏暂时注销即可正常显示:

//#define AT91_PMC_HCK1(1 << 17) /* AHB Clock (LCD) [AT91SAM9261 only] */

//lingzj remove

#define AT91_PMC_HCK1 (1 << 1) /* AHB Clock (LCD) [AT91SAM9261 only] */

—— 完 ——

你可能感兴趣的:(Linux,Driver)