您的位置:首页 > 运维架构 > Linux

设备树学习之(十二)LCD驱动

2017-06-10 07:28 204 查看
转载自:http://blog.csdn.net/lizuobin2/article/details/54743381

开发板:tiny4412SDK + S702 + 4GB Flash

要移植的内核版本:Linux-4.4.0 (支持device tree)

u-boot版本:友善之臂自带的 U-Boot 2010.12

busybox版本:busybox 1.25

参考:tiny4412LCD驱动加字符显示

1、背光

  友善之臂的该款LCD采用了一线触控技术,包括背光控制也集成在一线触控之中,关于背光的控制,在前一篇文章中已经提及,本文重点在于LCD驱动。

2、LCD接口

1)Tiny 4412 使用的lcd接口为LCD1

2)图片数据信号接口

B[0:7] : 蓝色数据信号线

G[0:7] : 绿色数据信号线

R[0:7] : 红色数据信号线

3)时序信号接口

DEN 数据允许信号

VSYNC 垂直同步信号

HSYNC 水平同步信号

VLCK LCD时钟信号

4)一线触控

XEINT10_OUT



3、图像的构成

帧:一幅图像被称为一帧,每帧有多行组成,每行有多个像素点组成

像素:

1)显示的最小单位

2)由若干位的颜色数据来构成,像素越高,则一个像素点所需要的颜色数据越多,能够显示的颜色更广

3)一个像素点构成的颜色位数称为像素深度,单位为1BPP 常见的有16BPP/24BPP

4、颜色的量化(颜色<—–>数字)

颜色一般采用RGB标准,通过对红(R)、绿(GREEN),蓝(B)三个颜色以及相互叠加获取各种不同的颜色

1)通过对颜色的编码来对颜色进行量化(即转换成数字量,RGB是一种编码方式)

2)每种颜色根据RGB格式不同,每种颜色的量化位不相同

3) 常见的RGB格式有RGB565/RGB888

RGB565: red :5 green : 6 blue:5

RGB888: red :8 green : 8 blue:8

5、显示图像与LCD时序

1)使用HSYNC信号来控制一行的显示

2)使用VSYNC信号来控制一帧的显示

3)使用VCLK信号来控制一个像素的显示

4)使用VDEN信号来控制数据的输出

6、Exyons 4412 display 控制器

1)alpha,alpha操作用于实现图形渐变效果,以及半透明效果

0xfff == 全透明

0x0 == 不透明

2)colorkey,colorkey操作在融合两个窗口时过虑掉其中一个窗口的某一种特定颜色

3)HOZVAL与LINEVAL

HOZVAL = (Horizontal display size) - 1

LINEVAL = (Vertical display size) - 1

4)LCD时序图

notes:

.Using the display controller data, you can select one of the above data paths by setting LCDBLK_CFG Register(0x1001_0210). For more information, refer to the “System Others” manual











7、Exyons 4412 display 控制器配置

1)gpio配置,查看原理图 ,获取LCD接口的对应的gpio

LCD_HSYNC:GPF0_0

LCD_VSYNC:GPF0_1

LCD_VDEV: GPF0_2

LCD_VCLK: GPF0_3

VD[23:0]:GPF1_0 - GPF1_5 / GPF2_0 - GPF2_7 / GPF3_0 - GPF3_3

2)时钟配置

(1)查看Exyons 4412 手册 获取LCD时钟源

LCD 时钟源为SCLKmpll_user_t:800Mhz

(2)配置相关的寄存器得到LCD所需要的时钟 (见07lcd_clock)

3)系统配置

LCDBLK_CFG : 配置成FIMD接口



一、设备树

&pinctrl_0 {
lcd_demo: lcd{
samsung,pins = "gpf0-0", "gpf0-1", "gpf0-2", "gpf0-3", "gpf0-4", "gpf0-5", "gpf0-6","gpf0-7", "gpf1-0", "gpf1-1", "gpf1-2", "gpf1-3", "gpf1-4", "gpf1-5", "gpf1-6","gpf1-7", "gpf2-0", "gpf2-1", "gpf2-2", "gpf2-3", "gpf2-4", "gpf2-5", "gpf2-6","gpf2-7", "gpf3-0", "gpf3-1", "gpf3-2", "gpf3-3";
samsung,pin-function = <2>;
samsung,pin-pud = <0>;
samsung,pin-drv = <0>;
};
};

lcd_demo@11C00000{
compatible         = "tiny4412,lcd_demo";
reg = <0x11C00000  0x20c0 0x10010210 0x08 0x10023c80 0x04 0x1003c000 0x1000>;
pinctrl-names = "default";
pinctrl-0 = <&lcd_demo>;
clocks = <&clock CLK_FIMD0 &clock CLK_ACLK160>;
clock-names = "fimd0","aclk160";
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[/code]

二、驱动代码

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/wait.h>
#include <linux/platform_device.h>
#include <linux/clk.h>

#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/div64.h>

#include <asm/mach/map.h>
#include <linux/fb.h>
#include <asm/types.h>

#define     VIDCON0         0x00
#define     VIDCON1         0x04
#define     VIDTCON0        0x10
#define     VIDTCON1        0x14
#define     VIDTCON2        0x18
#define     WINCON0         0x20
#define     VIDOSD0C        0x48
#define     SHADOWCON       0x34
#define     WINCHMAP2       0x3c
#define     VIDOSD0A        0x40
#define     VIDOSD0B        0x44
#define     VIDW00ADD0B0    0xA0
#define     VIDW00ADD1B0    0xD0

#define     CLK_SRC_LCD0        0x234
#define     CLK_SRC_MASK_LCD    0x334
#define     CLK_DIV_LCD         0x534
#define     CLK_GATE_IP_LCD     0x934

#define     LCDBLK_CFG      0x00
#define     LCDBLK_CFG2     0x04

static int s3c_lcdfb_setcolreg(unsigned int regno, unsigned int red,
unsigned int green, unsigned int blue,
unsigned int transp, struct fb_info *info);

static struct fb_ops s3c_lcdfb_ops =
{
.owner      = THIS_MODULE,
.fb_setcolreg   = s3c_lcdfb_setcolreg,
.fb_fillrect    = cfb_fillrect,
.fb_copyarea    = cfb_copyarea,
.fb_imageblit   = cfb_imageblit,
};

static struct fb_info *s3c_lcd;
static volatile void __iomem *lcd_regs_base;
static volatile void __iomem *clk_regs_base;
static volatile void __iomem *lcdblk_regs_base;
static volatile void __iomem *lcd0_configuration;
static u32 pseudo_palette[16];
static struct resource *res1, *res2, *res3, *res4;

/* from pxafb.c */
static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
{
chan &= 0xffff;
chan >>= 16 - bf->length;
return chan << bf->offset;
}

static int s3c_lcdfb_setcolreg(unsigned int regno, unsigned int red,
unsigned int green, unsigned int blue,
unsigned int transp, struct fb_info *info)
{
unsigned int val;

if (regno > 16)
{ return 1; }

/* 用red,green,blue三原色构造出val */
val  = chan_to_field(red,   &info->var.red);
val |= chan_to_field(green, &info->var.green);
val |= chan_to_field(blue,  &info->var.blue);
//((u32 *)(info->pseudo_palette))[regno] = val;
pseudo_palette[regno] = val;
return 0;
}

static int lcd_probe(struct platform_device *pdev)
{
int ret;
unsigned int temp;
/* 1. 分配一个fb_info */
s3c_lcd = framebuffer_alloc(0, NULL);
/* 2. 设置 */
/* 2.1 设置 fix 固定的参数 */
strcpy(s3c_lcd->fix.id, "mylcd");
s3c_lcd->fix.smem_len = 480 * 800 * 16 / 8;     //显存的长度
s3c_lcd->fix.type     = FB_TYPE_PACKED_PIXELS;  //类型
s3c_lcd->fix.visual   = FB_VISUAL_TRUECOLOR;    //TFT 真彩色
s3c_lcd->fix.line_length = 800 * 2;             //一行的长度
/* 2.2 设置 var 可变的参数 */
s3c_lcd->var.xres           = 800;  //x方向分辨率
s3c_lcd->var.yres           = 480;  //y方向分辨率
s3c_lcd->var.xres_virtual   = 800;  //x方向虚拟分辨率
s3c_lcd->var.yres_virtual   = 480;  //y方向虚拟分辨率
s3c_lcd->var.bits_per_pixel = 16;   //每个像素占的bit
/* RGB:565 */
s3c_lcd->var.red.offset     = 11;   //红
s3c_lcd->var.red.length     = 5;
s3c_lcd->var.green.offset   = 5;    //绿
s3c_lcd->var.green.length   = 6;
s3c_lcd->var.blue.offset    = 0;    //蓝
s3c_lcd->var.blue.length    = 5;
s3c_lcd->var.activate       = FB_ACTIVATE_NOW;
/* 2.3 设置操作函数 */
s3c_lcd->fbops              = &s3c_lcdfb_ops;
/* 2.4 其他的设置 */
s3c_lcd->pseudo_palette = pseudo_palette;       //调色板
//s3c_lcd->screen_base  = ;                     //显存的虚拟地址,分配显存时填充
s3c_lcd->screen_size    = 480 * 800 * 16 / 8;   //显存大小
/* 3. 硬件相关的操作 */
/* 3.1 配置GPIO用于LCD */
//设备树中使用"default"
/* 3.2 根据LCD手册设置LCD控制器, 比如VCLK的频率等 */
//寄存器映射
res1 = platform_get_resource(pdev, IORESOURCE_MEM, 0);

if (res1 == NULL)
{
printk("platform_get_resource error\n");
return -EINVAL;
}

lcd_regs_base = devm_ioremap_resource(&pdev->dev, res1);

if (lcd_regs_base == NULL)
{
printk("devm_ioremap_resource error\n");
return -EINVAL;
}

res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);

if (res2 == NULL)
{
printk("platform_get_resource error\n");
return -EINVAL;
}

lcdblk_regs_base = devm_ioremap_resource(&pdev->dev, res2);

if (lcdblk_regs_base == NULL)
{
printk("devm_ioremap_resource error\n");
return -EINVAL;
}

res3 = platform_get_resource(pdev, IORESOURCE_MEM, 2);

if (res3 == NULL)
{
printk("platform_get_resource error\n");
return -EINVAL;
}

lcd0_configuration = ioremap(res3->start, 0x04);
*lcd0_configuration = 0x07;

res4 = platform_get_resource(pdev, IORESOURCE_MEM, 3);

if (res4 == NULL)
{
printk("platform_get_resource error\n");
return -EINVAL;
}

clk_regs_base = ioremap(res4->start, 0x1000);

if (clk_regs_base == NULL)
{
printk("devm_ioremap_resource error\n");
return -EINVAL;
}

//使能时钟
//时钟源选择 0110  SCLKMPLL_USER_T 800M
temp = readl(clk_regs_base + CLK_SRC_LCD0);
temp &= ~0x0f;
temp |= 0x06;
writel(temp, clk_regs_base + CLK_SRC_LCD0);

//FIMD0_MASK
temp = readl(clk_regs_base + CLK_SRC_MASK_LCD);
temp |= 0x01;
writel(temp, clk_regs_base + CLK_SRC_MASK_LCD);

//SCLK_FIMD0 = MOUTFIMD0/(FIMD0_RATIO + 1),分频 1/1
temp = readl(clk_regs_base + CLK_DIV_LCD);
temp &= ~0x0f;
writel(temp, clk_regs_base + CLK_DIV_LCD);

//CLK_FIMD0 Pass
temp = readl(clk_regs_base + CLK_GATE_IP_LCD);
temp |= 0x01;
writel(temp, clk_regs_base + CLK_GATE_IP_LCD);

//FIMDBYPASS_LBLK0 FIMD Bypass
temp = readl(lcdblk_regs_base + LCDBLK_CFG);
temp |= 1 << 1;
writel(temp, lcdblk_regs_base + LCDBLK_CFG);
temp = readl(lcdblk_regs_base + LCDBLK_CFG2);
temp |= 1 << 0;
writel(temp, lcdblk_regs_base + LCDBLK_CFG2);
mdelay(1000);

//分频    800/(23 +1 ) == 33M
temp = readl(lcd_regs_base + VIDCON0);
temp |= (23 << 6);
writel(temp, lcd_regs_base + VIDCON0);

/*
* VIDTCON1:
* [5]:IVSYNC  ===> 1 : Inverted(反转)
* [6]:IHSYNC  ===> 1 : Inverted(反转)
* [7]:IVCLK   ===> 1 : Fetches video data at VCLK rising edge (下降沿触发)
* [10:9]:FIXVCLK  ====> 01 : VCLK running
*/
temp = readl(lcd_regs_base + VIDCON1);
temp |= (1 << 9) | (1 << 7) | (1 << 5) | (1 << 6);
writel(temp, lcd_regs_base + VIDCON1);

/*
* VIDTCON0:
* [23:16]:  VBPD + 1  <------> tvpw (1 - 20)  13
* [15:8] :  VFPD + 1  <------> tvfp 22
* [7:0]  :  VSPW  + 1 <------> tvb - tvpw = 23 - 13 = 10
*/
temp = readl(lcd_regs_base + VIDTCON0);
temp |= (12 << 16) | (21 << 8) | (9);
writel(temp, lcd_regs_base + VIDTCON0);

/*
* VIDTCON1:
* [23:16]:  HBPD + 1  <------> thpw (1 - 40)  36
* [15:8] :  HFPD + 1  <------> thfp 210
* [7:0]  :  HSPW  + 1 <------> thb - thpw = 46 - 36 = 10
*/
temp = readl(lcd_regs_base + VIDTCON1);
temp |= (35 << 16) | (209 << 8)  | (9);
writel(temp, lcd_regs_base + VIDTCON1);

/*
* HOZVAL = (Horizontal display size) - 1 and LINEVAL = (Vertical display size) - 1.
* Horizontal(水平) display size : 800
* Vertical(垂直) display size : 480
*/
temp = (479 << 11) | 799;
writel(temp, lcd_regs_base + VIDTCON2);

/*
* WINCON0:
* [16]:Specifies Half-Word swap control bit.  1 = Enables swap P1779 低位像素存放在低字节
* [5:2]: Selects Bits Per Pixel (BPP) mode for Window image : 0101 ===> 16BPP RGB565
* [1]:Enables/disables video output   1 = Enables
*/
temp = readl(lcd_regs_base + WINCON0);
temp |= (1 << 16) | (5 << 2) | 1;
writel(temp, lcd_regs_base + WINCON0);

temp = readl(lcd_regs_base + SHADOWCON);
writel(temp | 0x01, lcd_regs_base + SHADOWCON);

//p1769
temp = readl(lcd_regs_base + WINCHMAP2);
temp &= ~(7 << 16);
temp |= 1 << 16;
temp &= ~7;
temp |= 1;
writel(temp, lcd_regs_base + WINCHMAP2);

/*
* bit0-10 : 指定OSD图像左上像素的垂直屏幕坐标
* bit11-21: 指定OSD图像左上像素的水平屏幕坐标
*/
writel(0, lcd_regs_base + VIDOSD0A);

/*
* bit0-10 : 指定OSD图像右下像素的垂直屏幕坐标
* bit11-21: 指定OSD图像右下像素的水平屏幕坐标
*/
writel((799 << 11) | 479, lcd_regs_base + VIDOSD0B);

//Window Size For example, Height ? Width (number of word)
temp = 480 * 800 >> 1;
writel(temp, lcd_regs_base + VIDOSD0C);

/* 3.3 分配显存(framebuffer), 并把地址告诉LCD控制器 */
// s3c_lcd->screen_base     显存虚拟地址
// s3c_lcd->fix.smem_len    显存大小,前面计算的
// s3c_lcd->fix.smem_start  显存物理地址
s3c_lcd->screen_base = dma_alloc_writecombine(NULL, s3c_lcd->fix.smem_len, (dma_addr_t *)&s3c_lcd->fix.smem_start, GFP_KERNEL);

//显存起始地址
writel(s3c_lcd->fix.smem_start, lcd_regs_base + VIDW00ADD0B0);

//显存结束地址
writel(s3c_lcd->fix.smem_start + s3c_lcd->fix.smem_len, lcd_regs_base + VIDW00ADD1B0);
//Enables video output and logic immediately
temp = readl(lcd_regs_base + VIDCON0);
writel(temp | 0x03, lcd_regs_base + VIDCON0);

/* 4. 注册 */
ret = register_framebuffer(s3c_lcd);
return ret;
}

static int lcd_remove(struct platform_device *pdev)
{
printk("%s enter.\n", __func__);
unregister_framebuffer(s3c_lcd);
dma_free_writecombine(NULL, s3c_lcd->fix.smem_len, s3c_lcd->screen_base, s3c_lcd->fix.smem_start);
framebuffer_release(s3c_lcd);
iounmap(lcd0_configuration);
iounmap(clk_regs_base);
return 0;
}

static const struct of_device_id lcd_dt_ids[] =
{
{ .compatible = "tiny4412,lcd_demo", },
{},
};

MODULE_DEVICE_TABLE(of, lcd_dt_ids);

static struct platform_driver lcd_driver =
{
.driver        = {
.name      = "lcd_demo",
.of_match_table    = of_match_ptr(lcd_dt_ids),
},
.probe         = lcd_probe,
.remove        = lcd_remove,
};

static int lcd_init(void)
{
int ret;
printk("enter %s\n", __func__);
ret = platform_driver_register(&lcd_driver);

if (ret)
{
printk(KERN_ERR "pwm demo: probe faipwm: %d\n", ret);
}

return ret;
}

static void lcd_exit(void)
{
printk("enter %s\n", __func__);
platform_driver_unregister(&lcd_driver);
}

module_init(lcd_init);
module_exit(lcd_exit);

MODULE_LICENSE("GPL");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
[/code]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  tiny4412 linux驱动