本文是基于mini2440开发板Linux版本号是linux-2.6.32.2的学习笔记

一. LCD device硬件信息

1.LCD控制器的寄存器地址从 0X4D000000开始

2.lcd device的名称:s3c2410-lcd

struct platform_device s3c_device_lcd = {.name        = "s3c2410-lcd",.id          = -1,.num_resources    = ARRAY_SIZE(s3c_lcd_resource),.resource   = s3c_lcd_resource,.dev              = {.dma_mask       = &s3c_device_lcd_dmamask,.coherent_dma_mask   = 0xffffffffUL}
};
  1. lcd的平台信息的设置
s3c24xx_fb_set_platdata(&mini2440_fb_info);
  1. 现在的lcd的型号:TD240320
    lcd的类型
.type        = S3C2410_LCDCON1_TFT,
  1. lcd的长宽
#define LCD_WIDTH 240
#define LCD_HEIGHT 320

lcd 平台信息的设置

二. lcd device的注册

调用注册平台设备接口注册s3c2440的外设设备。

platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));

设备的名称是上面的“s3c2410-lcd”

三. lcd driver的注册

搜索“s3c2410-lcd”,找到文件s3c2410fb.c,发现在这个文件里面注册了匹配s3c2410-lcd设备的驱动。

static struct platform_driver s3c2410fb_driver = {.probe        = s3c2410fb_probe,.remove      = s3c2410fb_remove,.suspend    = s3c2410fb_suspend,.resume        = s3c2410fb_resume,.driver     = {.name   = "s3c2410-lcd",.owner   = THIS_MODULE,},
};

probe函数是s3c2410fb_probe。
调用platform_driver_register注册driver。

int __init s3c2410fb_init(void)
{int ret = platform_driver_register(&s3c2410fb_driver);if (ret == 0)ret = platform_driver_register(&s3c2412fb_driver);return ret;
}
四. lcd driver的probe函数
  • 获取设备的platform_data。这个platform_data就是在mach-mini2440.c文件中通过s3c24xx_fb_set_platdata函数注册,最终指向的是全局变量mini2440_fb_info。
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,
};
  • 获取display硬件信息,实际上指向的是mini2440_lcd_cfg全局变量
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,
};
  • 申请一个framebuffer
fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
  • s3c2410fb_info结构体和fb_info的相互关联
info = fbinfo->par;
  • 获取寄存器资源并进行映射
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
size = (res->end - res->start) + 1;
info->mem = request_mem_region(res->start, size, pdev->name);
info->io = ioremap(res->start, size);
  • 先禁止lcd 视频和信号输出, LCDCON1寄存器的第0位写0就是禁止。
lcdcon1 = readl(info->io + S3C2410_LCDCON1);
writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);

  • 先填充fbinfo的fix结构体
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结构体
fbinfo->var.nonstd        = 0;
fbinfo->var.activate     = FB_ACTIVATE_NOW;
fbinfo->var.accel_flags     = 0;
fbinfo->var.vmode        = FB_VMODE_NONINTERLACED;
  • 申请中断,注册中断函数
irq = platform_get_irq(pdev, 0);
ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);
  • 获取lcd的时钟,并使能时钟
info->clk = clk_get(NULL, "lcd");
clk_enable(info->clk);
  • 计算得到最大的显存,计算公式(长(每个像素所占字节))
unsigned long smem_len = mach_info->displays[i].xres;
smem_len *= mach_info->displays[i].yres;
smem_len *= mach_info->displays[i].bpp;
smem_len >>= 3;

mach_info->displays[i].xres = 240;
mach_info->displays[i].yres = 320;
mach_info->displays[i].bpp = 16;
那么smem_len = 240 * 320 * 16/8 = 153600byte

  • 申请video显存,大小为smem_len
info->screen_base = dma_alloc_writecombine(fbi->dev, map_size,&map_dma, GFP_KERNEL);

显存需要进行初始化为0

memset(info->screen_base, 0x00, map_size);
  • lcd 寄存器的初始化,调用的函数是s3c2410fb_init_registers
  • 将上面的framebuffer注册进内核。
ret = register_framebuffer(fbinfo);
五. lcd寄存器初始化
  • GPCCON的配置
.gpccon =       0xaa955699,

0xaa955699 = 10101010100101010101011010011001
具体的引脚配置为:
GPC15:VD[7]; GPC14:VD[6]; GPC13:VD[5]; GPC12:VD[4]; GPC11:VD[3]; GPC10:Output; GPC9:Output; GPC8:Output;
GPC7:Output; GPC6:Output; GPC5:Output; GPC4:VM; GPC3:VFRAME; GPC2:Output; GPC1:VCLK; GPC0:Output;

  • GPCUP的设置
.gpcup =        0x0000ffff,

gpc IO口禁止上拉

  • GPDCON的配置
.gpdcon =       0xaa95aaa1,

0xaa95aaa1 = 10101010100101011010101010100001
具体的引脚配置为:
GPD15:VD[23]; GPD14:VD[22]; GPD13:VD[21]; GPD12:VD[20]; GPD11:VD[19]; GPD10:Output; GPD9:Output; GPD8:Output;
GPD7:VD[15]; GPD6:VD[14]; GPD5:VD[13]; GPD4:VD[12]; GPD3:VD[11]; GPD2:VD[10]; GPD1:Input; GPD0:Output;

  • GPDUP的配置
.gpdup =        0x0000faff,

0x0000faff = 1111101011111111
除了GPD8和GPD10两个引脚外,其余的引脚全部禁止上拉。

  • 可以看出lcd的data引脚配置如下:

  • TCONSEL寄存器的设置

.lpcsel      = 0xf82,

即:选择输出分辨率为240 * 320
选择 Sync mode

Sync mode需要用H-SYNC和V-SYNC同步RGB data;DE mode (Data Enable)则只需要DE信号同步RGB data。

  • 禁止临时调色板
tpal = regs + S3C2410_TPAL;
/* ensure temporary palette disabled */
writel(0x00, tpal);
六. lcd设置fbinfo的var的参数
  • 设置的长宽
var->xres_virtual = display->xres;
var->yres_virtual = display->yres;
var->height = display->height;
var->width = display->width;

var->xres_virtual = 240;
var->yres_virtual = 320;
var->height = 0;
var->width = 0;

  • 设置时钟参数
    var->pixclock = display->pixclock = 170000
    var->left_margin = display->left_margin = 0 + 1
    var->right_margin = display->right_margin = 100 + 1
    var->upper_margin = display->upper_margin = 0 + 1
    var->lower_margin = display->lower_margin = 1 + 1
    var->vsync_len = display->vsync_len = 9 + 1
    var->hsync_len = display->hsync_len = 4 + 1

  • 设置颜色,颜色模式为RGB565
    var->red.offset = 11;
    var->green.offset = 5;
    var->blue.offset = 0;
    var->red.length = 5;
    var->green.length = 6;
    var->blue.length = 5;
    没有透明度的设置
    var->transp.offset = 0;
    var->transp.length = 0;

七. lcd几个控制寄存器的设置
  • LCDCON1寄存器设置16位的颜色模式

    选择16bpp,LCDCON1 |= 12 << 1;

  • 设置lcd的y分辨率

    var->yres << 14;

  • 设置VBPD

    var->upper_margin << 24 = 0 << 24
    VBPD:帧同步后,帧数据开始前,无效行信号的数量。

  • 设置VFPD

    var->lower_margin << 6 = 1 << 6
    VFPD:帧数据结束后,帧同步前,无效行信号的数量

  • 设置VSPW

    var->vsync_len << 0 = 9 << 0
    VSPW:通过计算无效行的数量,决定帧同步信号脉冲高电平的宽度。

  • VSPW,VBPD,VFPD之间的关系

  • 设置lcd的x分辨率

    var->xres << 8 = 320 << 8;

  • 设置HBPD

    var->upper_margin << 19 = 100 << 19
    HBPD:行同步下降沿后,行数据开始前,无效的VCLK的数量

  • 设置HFPD

    var->left_margin << 0 = 0 << 0
    HFPD:行数据结束后,行同步上升沿前,无效的VCLK的数量

  • 设置HSPW


    var->hsync_len << 0 = 4 << 0
    HSPW:决定行同步信号脉冲高电平的宽度

  • HSPW,HBPD,HFPD之间的关系

  • 设置字节在显存中的排放

#define S3C2410_LCDCON5_BSWP     (1<<1)
#define S3C2410_LCDCON5_HWSWP       (1<<0)regs->lcdcon5 &= ~S3C2410_LCDCON5_BSWP;
regs->lcdcon5 |= S3C2410_LCDCON5_HWSWP;


BSWP = 0, HWSWP = 1

八. lcd显存的分配

显存的分配不能调用kmalloc函数,因为分配的物理地址不是连续的,而lcd控制器需要从连续的显存上取数据。

  • 申请显存
info->screen_base = dma_alloc_writecombine(fbi->dev, map_size,  &map_dma, GFP_KERNEL);

dma_alloc_writecombine返回的是申请的显存的虚拟地址,赋值给info->screen_base。
map_dma是申请的显存的物理地址,赋值给smem_start。

info->fix.smem_start = map_dma;
九. 设置lcd时钟

在mach-mini2440.c文件中,lcd时钟的频率是170000,单位是什么暂时不知道。

#define LCD_PIXCLOCK 170000

从后面给的内核注释看,单位是ps, 及该lcd的时钟频率是170ns。


HCLK在开机时有打印,为100M,我们通过设置CLKVAL的值,可以控制lcd的输出时钟VCLK.
现在已经lcd需要的时钟频率为170000ps, 反推CLKVAL的值。

  • VCLK转为s: VCLK * 10^(-12)
  • VCLK转为HZ: 1 / (VCLK * 10^(-12))
  • 1 / (VCLK * 10^(-12)) = HCLK / ((CLKVAL + 1) * 2)
  • (CLKVAL + 1) * 2 = HCLK * (VCLK * 10^(-12))
  • (CLKVAL + 1) = HCLK * VCLK / ( 2 * (10^12) )
  • CLKVAL = HCLK * VCLK / ( 2 * (10^12) ) - 1

看一下源代码的计算

clkdiv = DIV_ROUND_UP(s3c2410fb_calc_pixclk(fbi, var->pixclock), 2);

clkdiv = s3c2410fb_calc_pixclk(fbi, var->pixclock) / 2

if (type == S3C2410_LCDCON1_TFT)
{--clkdiv;if (clkdiv < 0)clkdiv = 0;
}

clkdiv = clkdiv -1;

看一下s3c2410fb_calc_pixclk函数

static unsigned int s3c2410fb_calc_pixclk(struct s3c2410fb_info *fbi, unsigned long pixclk)
{unsigned long clk = fbi->clk_rate;unsigned long long div;/* pixclk is in picoseconds, our clock is in Hz** Hz -> picoseconds is / 10^-12*/div = (unsigned long long)clk * pixclk;div >>= 12;            /* div / 2^12 */do_div(div, 625 * 625UL * 625); /* div / 5^12 */dprintk("pixclk %ld, divisor is %ld\n", pixclk, (long)div);return div;
}

div = clk * pixclk;
div = div / 2^12;
div = div / 5^12;
即div = div / (10^12)
div = clk * pixclk / (10^12)
clkdiv = clk * pixclk / ( (10^12) * 2 ) - 1,跟上面推导的公式是一致的。

十. lcd显存寄存器设置

分配了显存之后,要把显存的地址告诉lcd控制器。lcd控制器会自动的从显存中取出每一个值通过VD0 ~ VD23发送出像素数据到lcd屏上去。

  • 存放显存的起始地址
saddr1  = info->fix.smem_start >> 1;


因为我的lcd设置的是单扫描模式,saddr1保存的是显存地址的A30~A1位,最高位和最低位不要,因此
saddr1 = info->fix.smem_start >> 1;

  • 存放显存的结束地址
saddr2  = info->fix.smem_start;
saddr2 += info->fix.line_length * info->var.yres;
saddr2 >>= 1;

info->fix.line_length = 240 * 2;
info->var.yres = 320;
我们的结束地址是:s3c_lcd->fix.smem_start + s3c_lcd->fix.smem_len

saddr2保存显存的A21 ~ A1位,需要右移一位赋值给saddr2

  • 存放虚拟屏幕信息
    虚拟屏幕偏移
    由于这里我们的虚拟屏幕和物理屏幕是一样大的,因此偏移是0

虚拟屏幕页宽度
还是info->fix.line_length = 240 * 2,因为虚拟屏幕和物理屏幕一样大,但是这里表示的是半字,所以要除以2

saddr3 = S3C2410_OFFSIZE(0) | S3C2410_PAGEWIDTH((info->fix.line_length / 2) & 0x3ff);

十一. 设置调色板

真彩色时不需要设置调色板,假彩色时才需要调色板
真彩色宏:FB_VISUAL_TRUECOLOR
假彩色宏:FB_VISUAL_PSEUDOCOLOR
调色板采用8位索引,用来索引256种颜色。lcd 有256个寄存器来保存这256中颜色值。
颜色模式有2种: 5:6:5 (R:G:B) 和 5:5:5:1(R:G:B:I)
寄存器地址:0X4D000400 ~ 0X4D0007FC

调色板颜色设置:

val  = (red   >>  0) & 0xf800;
val |= (green >>  5) & 0x07e0;
val |= (blue  >> 11) & 0x001f;
writel(val, regs + S3C2410_TFTPAL(regno));

真彩色不需要调色板,因此使用了一个假调色板,颜色保存在假调色板数组里面。

u32 *pal = info->pseudo_palette;
val  = chan_to_field(red,   &info->var.red);
val |= chan_to_field(green, &info->var.green);
val |= chan_to_field(blue,  &info->var.blue);
pal[regno] = val;

Linux LCD设备驱动详解相关推荐

  1. Linux字符设备驱动详解七(“插件“设备树实现RGB灯驱动)

    文章目录 系列文章目录 前言 正文 Device Tree Overlays:"插件"设备树 传统设备树 "插件"设备树 使用前提 案例说明 设备树:foo.d ...

  2. Linux字符设备驱动详解四(使用自属的xbus驱动总线)

    文章目录 系列文章目录 前言 驱动目录 正文 驱动总线 总线管理 总线注册 设备注册 驱动注册 代码示例 总结 系列文章目录 Linux字符设备驱动详解 Linux字符设备驱动详解二(使用设备驱动模型 ...

  3. Linux usb设备驱动详解

    1.Linux usb设备驱动框架 USB是通用串行总线的总称,Linux内核几乎支持所有的usb设备,包括键盘,鼠标,打印机,modem,扫描仪.Linux的usb驱动分为主机驱动与gadget驱动 ...

  4. itop4412 LCD设备驱动详解(四)之PROBE再深入

    LCD的工作,在kernel中有device和driver两个描述,这也是必然 在第二节中我们详解介绍了 s3cfb_main.c ---probe函数的框架. 回顾一下probe函数的作用: 1. ...

  5. itop4412 LCD设备驱动详解(三)之PROBE

    lcd的工作,在kernel中有device和driver两个描述,这也是必然 在上一节中我们详解介绍了 s3cfb_main.c ---probe函数的框架. 回顾一下probe函数的作用: 1. ...

  6. 【转】草根老师的 linux字符设备驱动详解

    Linux 驱动 之 模块化编程 Linux 驱动之模块参数和符号导出 Linux 设备驱动之字符设备(一) Linux 设备驱动之字符设备(二) Linux 设备驱动之字符设备(三) 转载于:htt ...

  7. linux usb gadget驱动详解(一)

    由于PC的推广,USB(通用串行总线)是我们最熟知的通信总线规范之一,其他的还有诸如以太网.PCIE总线和RS232串口等.这里我们主要讨论USB. USB是一个主从通信架构,但只能一主多从.其中us ...

  8. linux usb gadget驱动详解(二)

    在上篇<linux usb gadget驱动详解(一)>中,我们了解到gadget的测试方法,但在最后,我们留下一个问题,就是怎样使用新的方法进行usb gadget驱动测试. 我们发现l ...

  9. Linux设备驱动之usb设备驱动详解

    原文地址:http://blog.csdn.net/chenjin_zhong/article/details/6329316 1.Linux usb设备驱动框架 USB是通用串行总线的总称,Linu ...

最新文章

  1. mysql 5.7 编译_Mysql5.7版本编译安装及配置
  2. 为什么Kubernetes的存储如此艰难?
  3. 一文读懂全球半导体市场
  4. javascript判断对象、字符串、数组是否为空(兼容绝大部分浏览器)
  5. 人生不能过分认真(较真),但必须认真!
  6. 安卓系列转载,有时间可以参考学习
  7. sql server数据库课程设计分析
  8. 也许你需要点实用的-Web前端笔试题
  9. cuda Device Management
  10. 安装包制作工具 SetupFactory使用 详解
  11. 智能网联汽车——传感器与驾驶辅助
  12. Thinkpad E430c 16GB内存安装成功
  13. 怎么把外部参照合并到图纸_为什么CAD图纸作为外部参照插入时位置变了?
  14. 【已解决】找不到某服务器 IP 地址
  15. 华为交换机RRPP环协议
  16. 分享10个高质量的插画网站
  17. Arithmetic Slices 算术序列
  18. cad捕捉不到标注线上的点_CAD捕捉不到正在绘制的多段线上的点怎么办
  19. 背景图片和颜色混合叠加多种混合模式
  20. 计算机组成原理~计算机的基本组成②

热门文章

  1. 泰课在线 秘密行动课程
  2. 免费WebCamps-北美,亚洲和欧洲-*立即注册*
  3. 看起来为空的字符串,长度却是1?
  4. linux to go把linux系统装进U盘里随插随用
  5. 97年黑客设计大赛中冠军作品
  6. Tkinter 组件详解(七):Entry
  7. k8s集群coredns无法解析外部域名
  8. mac修改默认python版本_Mac系统修改Python版本软链接
  9. 2019.12.18
  10. 概览嵌入式系统的各种周边设备