2009年2月24日 星期二

引-Linux-2.6.22的LCD驱动

Hardware and Software:
CPU: S3C2410
LCD: LTV350QV-F04
OS : linux-2.6.22

1. 添加s3c2410处理器的LCD控制寄存器的初始值,具体做法为在文件arch/arm/mach-s3c2410/mach-smdk2410.c中添加struct s3c2410fb_mach_info类型的寄存器描述讯息,如下所示:

static struct s3c2410fb_mach_info smdk2410_lcd_info __initdata = {
.fixed_syncs = 0,
.type = S3C2410_LCDCON1_TFT,

.width = 320,
.height = 240,

.xres = {
.min = 320,
.max = 320,
.defval = 320,
},
.yres = {
.max = 240,
.min = 240,
.defval = 240,
},
.bpp = {
.min = 16,
.max = 16,
.defval = 16,
},

.regs = {
.lcdcon1 = S3C2410_LCDCON1_TFT16BPP | //(1)
S3C2410_LCDCON1_TFT |
S3C2410_LCDCON1_CLKVAL(2),
.lcdcon2 = S3C2410_LCDCON2_VBPD(3) |
S3C2410_LCDCON2_LINEVAL(239) |
S3C2410_LCDCON2_VFPD(5) |
S3C2410_LCDCON2_VSPW(15),
.lcdcon3 = S3C2410_LCDCON3_HBPD(5) |
S3C2410_LCDCON3_HOZVAL(319) |
S3C2410_LCDCON3_HFPD(15),
.lcdcon4 = //S3C2410_LCDCON4_MVAL(13) |
S3C2410_LCDCON4_HSPW(8),
.lcdcon5 = S3C2410_LCDCON5_FRM565 |
S3C2410_LCDCON5_INVVLINE |
S3C2410_LCDCON5_INVVFRAME |
S3C2410_LCDCON5_PWREN |
S3C2410_LCDCON5_HWSWP,
},

.gpccon = 0xaaaaaaaa,
.gpccon_mask = 0xffffffff,
.gpcup = 0xffffffff,
.gpcup_mask = 0xffffffff, //(2)

.gpdcon = 0xaaaaaaaa,
.gpdcon_mask = 0xffffffff,
.gpdup = 0xffffffff,
.gpdup_mask = 0xffffffff,

.lpcsel = 0x00, //(3)
};
注:
(1) LCD的数据线,也就是RGB(Red Green Blue)线,此处的硬件连接决定了LCD的BPP,16BPP或24BPP,我的开发板上采用的是5:6:5的16BPP的连接。关于BPP和连线的关 系可以参考2410手册的387~390页。
(2) 将GPC配置为VD功能,并关闭GPC的pull-up(上拉电阻)功能
(3) 这款LCD的特点是驱动IC内置在LCD模块上,所以不用外接lpc3600等驱动IC。
(4) S3C2410的LCD控制器说明 以及 HSPW,HBPD,HFPD等参数的算法s3c24xx_fb_set_platdata()函数向内核注册上面的信息(smdk2410_lcd_info),此函数在arch/arm/plat-s3c24xx/devs.c中定义。

然后在arch/arm/mach-s3c2410/mach-smdk2410.c的smdk2410_init()函数中调用s3c24xx_fb_set_platdata(),具体为:
static void __init smdk2410_init(void)
{
s3c24xx_fb_set_platdata(&smdk2410_lcd_info);
platform_add_devices(smdk2410_devices, ARRAY_SIZE(smdk2410_devices));
smdk_machine_init();
}

3. 在make menuconfig的时候配置Linux的logo选项,然后的时候在console选项中选上framebuffer console surpport,要不然看不到小企鹅。
Device Drivers --->
Graphics support --->
Console display driver support --->
<*> Framebuffer Console support
[*] Bootup logo --->


4. 测试LCD驱动
类别:驱动开发api | 添加到搜藏 | 浏览(421) | 评论 (2)
上一篇:LCD参数 下一篇:驱动的入口点

网友评论:
1

zengzhaonong
2008年08月30日 星期六 21:51 | 回复
S3C2410 HOZVAL = (Horizontal display size) -1 LINEVAL = (Vertical display size) -1 VCLK(Hz) = HCLK/[(CLKVAL+1)x2]
3

zengzhaonong
2008年08月30日 星期六 22:53 | 回复
VCLK(Hz) = HCLK/[(CLKVAL+1)x2] CLKVAL:决定VCLK的分频比。LCD控制器输出的VCLK是直接由系统总线(AHB)的工作频率HCLK直接分频得到的。做为240*320的TFT屏,应保证得出的VCLK在5~10MHz之间。 S3C2410的总线工作频率HCLK为60MHz.为了使VCLK等于10MHz,所以设置CLKVAL为2。 注: 如果CLKVAL设置不合适(过大),会导致屏幕抖动。

引-Linux-2.6.22的LCD驱动

〔http://hi.baidu.com/zengzhaonong/blog/item/2d78054ca53ea7ffd72afcdd.html」

Hardware and Software:
CPU: S3C2410
LCD: LTV350QV-F04
OS : linux-2.6.22

1. 添加s3c2410处理器的LCD控制寄存器的初始值,具体做法为在文件arch/arm/mach-s3c2410/mach-smdk2410.c中添加struct s3c2410fb_mach_info类型的寄存器描述讯息,如下所示:

static struct s3c2410fb_mach_info smdk2410_lcd_info __initdata = {
.fixed_syncs = 0,
.type = S3C2410_LCDCON1_TFT,

.width = 320,
.height = 240,

.xres = {
.min = 320,
.max = 320,
.defval = 320,
},
.yres = {
.max = 240,
.min = 240,
.defval = 240,
},
.bpp = {
.min = 16,
.max = 16,
.defval = 16,
},

.regs = {
.lcdcon1 = S3C2410_LCDCON1_TFT16BPP | //(1)
S3C2410_LCDCON1_TFT |
S3C2410_LCDCON1_CLKVAL(2),
.lcdcon2 = S3C2410_LCDCON2_VBPD(3) |
S3C2410_LCDCON2_LINEVAL(239) |
S3C2410_LCDCON2_VFPD(5) |
S3C2410_LCDCON2_VSPW(15),
.lcdcon3 = S3C2410_LCDCON3_HBPD(5) |
S3C2410_LCDCON3_HOZVAL(319) |
S3C2410_LCDCON3_HFPD(15),
.lcdcon4 = //S3C2410_LCDCON4_MVAL(13) |
S3C2410_LCDCON4_HSPW(8),
.lcdcon5 = S3C2410_LCDCON5_FRM565 |
S3C2410_LCDCON5_INVVLINE |
S3C2410_LCDCON5_INVVFRAME |
S3C2410_LCDCON5_PWREN |
S3C2410_LCDCON5_HWSWP,
},

.gpccon = 0xaaaaaaaa,
.gpccon_mask = 0xffffffff,
.gpcup = 0xffffffff,
.gpcup_mask = 0xffffffff, //(2)

.gpdcon = 0xaaaaaaaa,
.gpdcon_mask = 0xffffffff,
.gpdup = 0xffffffff,
.gpdup_mask = 0xffffffff,

.lpcsel = 0x00, //(3)
};
注:
(1) LCD的数据线,也就是RGB(Red Green Blue)线,此处的硬件连接决定了LCD的BPP,16BPP或24BPP,我的开发板上采用的是5:6:5的16BPP的连接。关于BPP和连线的关 系可以参考2410手册的387~390页。
(2) 将GPC配置为VD功能,并关闭GPC的pull-up(上拉电阻)功能
(3) 这款LCD的特点是驱动IC内置在LCD模块上,所以不用外接lpc3600等驱动IC。
(4)S3C2410的LCD控制器说明 以及 HSPW,HBPD,HFPD等参数的算法


2. 通过s3c24xx_fb_set_platdata()函数向内核注册上面的信息(smdk2410_lcd_info),此函数在arch/arm/plat-s3c24xx/devs.c中定义。

然后在arch/arm/mach-s3c2410/mach-smdk2410.c的smdk2410_init()函数中调用s3c24xx_fb_set_platdata(),具体为:
static void __init smdk2410_init(void)
{
s3c24xx_fb_set_platdata(&smdk2410_lcd_info);
platform_add_devices(smdk2410_devices, ARRAY_SIZE(smdk2410_devices));
smdk_machine_init();
}

3. 在make menuconfig的时候配置Linux的logo选项,然后的时候在console选项中选上framebuffer console surpport,要不然看不到小企鹅。
Device Drivers --->
Graphics support --->
Console display driver support --->
<*> Framebuffer Console support
[*] Bootup logo --->


4. 测试LCD驱动

类别:驱动开发api | 添加到搜藏 | 浏览(421) | 评论 (2)
上一篇:LCD参数 下一篇:驱动的入口点

网友评论:
1

zengzhaonong
2008年08月30日 星期六 21:51 | 回复
S3C2410 HOZVAL = (Horizontal display size) -1 LINEVAL = (Vertical display size) -1 VCLK(Hz) = HCLK/[(CLKVAL+1)x2]
3

zengzhaonong
2008年08月30日 星期六 22:53 | 回复
VCLK(Hz) = HCLK/[(CLKVAL+1)x2] CLKVAL:决定VCLK的分频比。LCD控制器输出的VCLK是直接由系统总线(AHB)的工作频率HCLK直接分频得到的。做为240*320的TFT屏,应保证得出的VCLK在5~10MHz之间。 S3C2410的总线工作频率HCLK为60MHz.为了使VCLK等于10MHz,所以设置CLKVAL为2。 注: 如果CLKVAL设置不合适(过大),会导致屏幕抖动。

引-s3c2410 LCD驱动分析

(http://hi.baidu.com/zengzhaonong/blog/item/00dc0f385a0785f4b211c7af.html)

Linux-2.6.22

module_init() -->
s3c2410fb_init() -->
platform_driver_register() -->
s3c2410fb_probe()



(10) 为该设备创建一个在sysfs中的属性


static int __init s3c2410fb_probe(struct platform_device *pdev)
{
struct s3c2410fb_info *info;
struct fb_info *fbinfo;
struct s3c2410fb_hw *mregs; //s3c2410fb_hw为描述LCD的硬件控制寄存器内容的结构体
int ret;
int irq;
int i;
u32 lcdcon1;

mach_info = pdev->dev.platform_data;
if (mach_info == NULL) {
dev_err(&pdev->dev,"no platform data for lcd, cannot attach\n");
return -EINVAL;
}

mregs = &mach_info->regs;

irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "no irq for device\n");
return -ENOENT;
}

fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
if (!fbinfo) {
return -ENOMEM;
}


info = fbinfo->par;
info->fb = fbinfo;
info->dev = &pdev->dev;

platform_set_drvdata(pdev, fbinfo);

dprintk("devinit\n");

strcpy(fbinfo->fix.id, driver_name);

memcpy(&info->regs, &mach_info->regs, sizeof(info->regs));

/* Stop the video and unset ENVID if set */
info->regs.lcdcon1 &= ~S3C2410_LCDCON1_ENVID;
lcdcon1 = readl(S3C2410_LCDCON1);
writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, S3C2410_LCDCON1);

info->mach_info = pdev->dev.platform_data;

fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
fbinfo->fix.type_aux = 0;
fbinfo->fix.xpanstep = 0;
fbinfo->fix.ypanstep = 0;
fbinfo->fix.ywrapstep = 0;
fbinfo->fix.accel = FB_ACCEL_NONE;

fbinfo->var.nonstd = 0;
fbinfo->var.activate = FB_ACTIVATE_NOW;
fbinfo->var.height = mach_info->height;
fbinfo->var.width = mach_info->width;
fbinfo->var.accel_flags = 0;
fbinfo->var.vmode = FB_VMODE_NONINTERLACED;

fbinfo->fbops = &s3c2410fb_ops;
fbinfo->flags = FBINFO_FLAG_DEFAULT;
fbinfo->pseudo_palette = &info->pseudo_pal;

fbinfo->var.xres = mach_info->xres.defval;
fbinfo->var.xres_virtual = mach_info->xres.defval;
fbinfo->var.yres = mach_info->yres.defval;
fbinfo->var.yres_virtual = mach_info->yres.defval;
fbinfo->var.bits_per_pixel = mach_info->bpp.defval;

fbinfo->var.upper_margin = S3C2410_LCDCON2_GET_VBPD(mregs->lcdcon2) + 1;
fbinfo->var.lower_margin = S3C2410_LCDCON2_GET_VFPD(mregs->lcdcon2) + 1;
fbinfo->var.vsync_len = S3C2410_LCDCON2_GET_VSPW(mregs->lcdcon2) + 1;

fbinfo->var.left_margin = S3C2410_LCDCON3_GET_HFPD(mregs->lcdcon3) + 1;
fbinfo->var.right_margin = S3C2410_LCDCON3_GET_HBPD(mregs->lcdcon3) + 1;
fbinfo->var.hsync_len = S3C2410_LCDCON4_GET_HSPW(mregs->lcdcon4) + 1;

fbinfo->var.red.offset = 11;
fbinfo->var.green.offset = 5;
fbinfo->var.blue.offset = 0;
fbinfo->var.transp.offset = 0;
fbinfo->var.red.length = 5;
fbinfo->var.green.length = 6;
fbinfo->var.blue.length = 5;
fbinfo->var.transp.length = 0;
fbinfo->fix.smem_len = mach_info->xres.max *
mach_info->yres.max *
mach_info->bpp.max / 8;

for (i = 0; i < 256; i++) //初始化调色板缓冲区
info->palette_buffer[i] = PALETTE_BUFF_CLEAR;

if (!request_mem_region((unsigned long)S3C24XX_VA_LCD, SZ_1M, "s3c2410-lcd")) {
ret = -EBUSY;
goto dealloc_fb;
}


dprintk("got LCD region\n");

ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);
if (ret) {
dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);
ret = -EBUSY;
goto release_mem;
}

info->clk = clk_get(NULL, "lcd");
if (!info->clk || IS_ERR(info->clk)) {
printk(KERN_ERR "failed to get lcd clock source\n");
ret = -ENOENT;
goto release_irq;
}

clk_enable(info->clk);
dprintk("got and enabled clock\n");

msleep(1);

/* Initialize video memory */
ret = s3c2410fb_map_video_memory(info);
if (ret) {
printk( KERN_ERR "Failed to allocate video RAM: %d\n", ret);
ret = -ENOMEM;
goto release_clock;
}
dprintk("got video memory\n");

ret = s3c2410fb_init_registers(info);

ret = s3c2410fb_check_var(&fbinfo->var, fbinfo);

ret = register_framebuffer(fbinfo);
if (ret < 0) {
printk(KERN_ERR "Failed to register framebuffer device: %d\n", ret);
goto free_video_memory;
}

/* create device files */
device_create_file(&pdev->dev, &dev_attr_debug);

printk(KERN_INFO "fb%d: %s frame buffer device\n",
fbinfo->node, fbinfo->fix.id);

return 0;

free_video_memory:
s3c2410fb_unmap_video_memory(info);
release_clock:
clk_disable(info->clk);
clk_put(info->clk);
release_irq:
free_irq(irq,info);
release_mem:
release_mem_region((unsigned long)S3C24XX_VA_LCD, S3C24XX_SZ_LCD);
dealloc_fb:
framebuffer_release(fbinfo);
return ret;
}

「引」測試LCD驅動

(http://hi.baidu.com/zengzhaonong/blog/item/ff92ac1b0120861f8618bf9d.html)
#include
#include
#include
#include
#include
#define FBDEV "/dev/fb0"
static char * default_framebuffer = FBDEV;

struct fb_dev
{
int fb;
void * fb_mem;
int fb_width, fb_height, fb_line_len, fb_size;
int fb_bpp;
};
static struct fb_dev fbdev;

static void draw(int color)
{
int i, j;
unsigned short int *p = (unsigned short int *)fbdev.fb_mem;
for (i = 0; i < fbdev.fb_height; i++, p += fbdev.fb_line_len/2) {
for (j = 0; j < fbdev.fb_width; j++)
p[j] = color;
}
}

int framebuffer_open(void)
{
int fb;
struct fb_var_screeninfo fb_vinfo;
struct fb_fix_screeninfo fb_finfo;
char * fb_dev_name = NULL;

if (!(fb_dev_name = getenv("FRAMEBUFFER")))
fb_dev_name = default_framebuffer;
fb = open(fb_dev_name, O_RDWR);
if (fb < 0) {
perror("oepn fb_dev error");
return -1;
}

if (ioctl(fb, FBIOGET_VSCREENINFO, &fb_vinfo)) {
perror("ioctl FBIOGET_VSCREENINFO error");
close(fb);
return -1;
}

if (ioctl(fb, FBIOGET_FSCREENINFO, &fb_finfo)) {
perror("ioctl FBIOGET_FSCREENINFO error");
return 1;
}

fbdev.fb_bpp = fb_vinfo.red.length + fb_vinfo.green.length +
fb_vinfo.blue.length + fb_vinfo.transp.length;
fbdev.fb_width = fb_vinfo.xres;
fbdev.fb_height = fb_vinfo.yres;
fbdev.fb_line_len = fb_finfo.line_length;
fbdev.fb_size = fb_finfo.smem_len;

printf("frame buffer: %d(%d)x%d, %dbpp, 0x%xbytes\n",
fbdev.fb_width, fbdev.fb_line_len, fbdev.fb_height,
fbdev.fb_bpp, fbdev.fb_size);

if (fbdev.fb_bpp != 16) {
printf("frame buffer must be 16bpp mode");
exit(0);
}

fbdev.fb_mem = mmap(NULL, fbdev.fb_size,
PROT_READ|PROT_WRITE, MAP_SHARED, fb, 0);
if (fbdev.fb_mem == NULL || (int)fbdev.fb_mem == -1) {
fbdev.fb_mem = NULL;
printf("mmap failed\n");
close(fb);
return -1;
}

fbdev.fb = fb;
memset(fbdev.fb_mem, 0x0, fbdev.fb_size);

return 0;
}

void framebuffer_close()
{
if (fbdev.fb_mem) {
munmap(fbdev.fb_mem, fbdev.fb_size);
fbdev.fb_mem = NULL;
}

if (fbdev.fb) {
close(fbdev.fb);
fbdev.fb = 0;
}
}

int main(void)
{
int i;
framebuffer_open();

for (i = 0; i < 16; i++) {
printf("%d: color = 0x%x", i, 1 << i);
draw(1 << i);
getchar();
}

framebuffer_close();
return 0;
}

「引」使用SPI驱动的接口(SPI API)

spi.c
--------------------------------------
#include
#include

#define TEST_REG 0x01

static char test_read_reg(struct spi_device *spi, char reg)
{
int ret;
char buf[2];
buf[0] = reg; // TX
buf[1] = 0; // RX
//spi_write_then_read(spi, &buf[0], 1, &buf[1], 1);
ret = spi_write(spi, &buf[0], 1);
if (ret != 0)
printk("spi write err\n");
ret = spi_read(spi, &buf[1], 1);
if (ret != 0)
printk("spi read err\n");

return buf[1];
}

static int spi_test_probe(struct spi_device *spi)
{
printk("<1> TEST_REG: 0x%x\n", test_read_reg(spi, TEST_REG));
return 0;
}

static int spi_test_remove(struct spi_device *spi)
{
return 0;
}

static struct spi_driver spi_test_driver = {
.probe = spi_test_probe,
.remove = spi_test_remove,
.driver = {
.name = "innofidei_cmmb",
.owner = THIS_MODULE,
},
};

static int __init spi_test_init(void)
{
return spi_register_driver(&spi_test_driver);
}

static void __exit spi_test_exit(void)
{
spi_unregister_driver(&spi_test_driver);
}

module_init(spi_test_init);
module_exit(spi_test_exit);

MODULE_DESCRIPTION("spi device test");
MODULE_LICENSE("GPL");



Makefile
--------------------------------------
KDIR=/home/ssl/linux-2.6.24-ssl

obj-m += spi.o

all:
make -C $(KDIR) M=`pwd` modules
clean:
make -C $(KDIR) M=`pwd` clean





生成spi_device
linux-2.6.24/arch/arm/mach-magus/a2818p.c
--------------------------------------
static struct spi_board_info _spi[] __initdata =
{
{
.modalias = "WM8987",
.bus_num = 0,
.chip_select = 3,
.max_speed_hz = 100000,
.mode = SPI_MODE_3,
},
{
.modalias = "ads7846",
.bus_num = 0,
.chip_select = 1,
.max_speed_hz = 2500000,
.irq = gpio_to_irq(MAGUS_GPIO_ADS7846),
.platform_data = &ads7846,
},
{
.modalias = "innofidei_cmmb",
.bus_num = 1, // SPI2
.chip_select = 0, // CS0
.max_speed_hz = 4000000, // 4M
.mode = SPI_MODE_0,
},

};

spi_register_board_info(_spi, ARRAY_SIZE(_spi)); //register SPI devices for a given board


spi控制器初始化的时候,会使用spi_board_info生成spi_device
sslspi_probe()
spi_register_master()
scan_boardinfo() // 使用spi_board_info信息生成新的spi_device
spi_new_device()





vi arch/arm/mach-magus/a2818t.c
------------------------------------------
static struct platform_device *devices[] =
{
// &ehci,
// &usbd,
// &otg,
&sdhc,
// &sdhc2,
&spi,
&spi2,
&i2c,
&rtc,
&wdog,
&kpp,
&lcdc,
&lcdcw1,
&lcdcw2,
&tvout,
&vpp,
&vip,
&d2d,
&nfc,
};


vi arch/arm/mach-magus/magus.h
------------------------------------------
//#if defined CONFIG_ACCIO_PF101 || defined CONFIG_ACCIO_P1
RCN_AIDP(spi2, "spi", 1, MAGUS_IO_SPI2, MAGUS_IRQ_SPI2, MAGUS_DMA_SPI2_TX, FREE_PIN, FREE_PIN, FREE_PIN, FREE_PIN, FREE_PIN );
//#endif

(http://hi.baidu.com/zengzhaonong/blog/item/0d9356543b084952564e00d6.html)

「引」zImgage,uImage区别(ZZ)

(http://hi.baidu.com/zengzhaonong/blog/item/1c3455edb15c1dd2b31cb1d3.html)
zImgage,uImage区别(ZZ)

2008年09月10日 星期三 21:58
对于Linux内核,编译可以生成不同格式的映像文件,例如:
# make zImage
# make uImage

zImage是ARM Linux常用的一种压缩映像文件,uImage是U-boot专用的映像文件,它是在zImage之前加上一个长度为0x40的“头”,说明这个映像文 件的类型、加载位置、生成时间、大小等信息。换句话说,如果直接从uImage的0x40位置开始执行,zImage和uImage没有任何区别。另 外,Linux2.4内核不支持uImage,Linux2.6内核加入了很多对嵌入式系统的支持,但是uImage的生成也需要设置。

「引」register_chrdev

(http://hi.baidu.com/zengzhaonong/blog/item/9f02adb70779b2f230add107.html)

register_chrdev

2008年09月29日 星期一 23:42
int register_chrdev(unsigned int major, const char *name,
struct file_operations *fops);

其中,major是为设备驱动程序向系统申请的主设备号,如果为0则系统为此驱动程序动态地分配一个主设备号。name是设备名。fops就是前面所说的 对各个调用的入口点的说明。此函数返回0表示成功。返回-EINVAL表示申请的主设备号非法,一般来说是主设备号大于系统所允许的最大设备号。返回 -EBUSY表示所申请的主设备号正在被其它设备驱动程序使用。如果是动态分配主设备号成功,此函数将返回所分配的主设备号。如果 register_chrdev操作成功,设备名就会出现在/proc/devices文件里。

在成功的向系统注册了设备驱动程序后(调用register_chrdev()成功后),就可以用mknod命令来把设备映射为一个特别文件,其它程序使用这个设备的时候,只要对此特别文件进行操作就行了。