【TINY4412】LINUX移植笔记:(27)设备树LCD驱动

环境

宿主机 : 虚拟机 Ubuntu 16.04 LTS / X64
目标板[底板]: Tiny4412SDK - 1506
目标板[核心板]: Tiny4412 - 1412
LINUX内核: 4.12.0
交叉编译器: arm-none-linux-gnueabi-gcc(gcc version 4.8.3 20140320)
日期: 2017-9-9 20:30:09
作者: SY

简介

开发板的液晶屏型号S702,电容触摸屏,分辨率:800X480

根据三星手册41 Display Controller章节,LCD接口支持三种接口, RGB-interface indirect-i80 interface and YUV interface for write-back

LCD

使用RGB888接口,包含24位并行数据总线。

设备树

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

移植

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
#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
#define LCD_LENTH 800
#define LCD_WIDTH 480
#define BITS_PER_PIXEL 32
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 color = 0;
uint32_t *p;
color = chan_to_field(red, &info->var.red);
color |= chan_to_field(green, &info->var.green);
color |= chan_to_field(blue, &info->var.blue);
p = info->pseudo_palette;
p[regno] = color;
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, "s702");
s3c_lcd->fix.smem_len = LCD_LENTH * LCD_WIDTH * BITS_PER_PIXEL / 8; //显存的长度
s3c_lcd->fix.type = FB_TYPE_PACKED_PIXELS; //类型
s3c_lcd->fix.visual = FB_VISUAL_TRUECOLOR; //TFT 真彩色
s3c_lcd->fix.line_length = LCD_LENTH * BITS_PER_PIXEL / 8; //一行的长度
/* 2.2 设置 var 可变的参数 */
s3c_lcd->var.xres = LCD_LENTH; //x方向分辨率
s3c_lcd->var.yres = LCD_WIDTH; //y方向分辨率
s3c_lcd->var.xres_virtual = LCD_LENTH; //x方向虚拟分辨率
s3c_lcd->var.yres_virtual = LCD_WIDTH; //y方向虚拟分辨率
s3c_lcd->var.bits_per_pixel = BITS_PER_PIXEL; //每个像素占的bit
/* RGB:888 */
s3c_lcd->var.red.length = 8;
s3c_lcd->var.red.offset = 16; //红
s3c_lcd->var.green.length = 8;
s3c_lcd->var.green.offset = 8; //绿
s3c_lcd->var.blue.length = 8;
s3c_lcd->var.blue.offset = 0; //蓝
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_size = LCD_LENTH * LCD_WIDTH * BITS_PER_PIXEL / 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);
if (lcd0_configuration == NULL)
{
printk("devm_ioremap_resource error\n");
return -EINVAL;
}
*(unsigned long *)lcd0_configuration = 7;
res4 = platform_get_resource(pdev, IORESOURCE_MEM, 3);
if (res3 == 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 ) == 33.3M
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 = ((LCD_WIDTH-1) << 11) | LCD_LENTH;
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 &= ~(0xf << 2);
temp |= (1 << 15) | (0xd << 2) | 1;
writel(temp, lcd_regs_base + WINCON0);
//Window Size For example, Height ? Width (number of word)
temp = (LCD_LENTH * LCD_WIDTH) >> 1;
writel(temp, lcd_regs_base + VIDOSD0C);
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(((LCD_LENTH-1) << 11) | (LCD_WIDTH-1), lcd_regs_base + VIDOSD0B);
//Enables video output and logic immediately
temp = readl(lcd_regs_base + VIDCON0);
writel(temp | 0x03, lcd_regs_base + VIDCON0);
/* 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);
/* 4. 注册 */
ret = register_framebuffer(s3c_lcd);
return ret;
}
static int lcd_remove(struct platform_device *pdev)
{
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);
return 0;
}
static const struct of_device_id lcd_dt_ids[] =
{
{ .compatible = "tiny4412, lcd_s702", },
{},
};
MODULE_DEVICE_TABLE(of, lcd_dt_ids);
static struct platform_driver lcd_driver =
{
.driver = {
.name = "lcd_s702",
.of_match_table = of_match_ptr(lcd_dt_ids),
},
.probe = lcd_probe,
.remove = lcd_remove,
};
static int lcd_init(void)
{
int ret;
ret = platform_driver_register(&lcd_driver);
if (ret)
{
printk(KERN_ERR "lcd: probe fail: %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");

makefile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Linux modules compile
# Author : SY
# Time : 2017-8-8 21:30:31
#############################################################################
TARGET := tiny4412_fb
obj-m := $(TARGET).o
CROSS := arm-none-linux-gnueabi-
KDIR := /opt/fs/rootfs/rootfs/lib/modules/4.12.0+/build
PWD := $(shell pwd)
INSTALL_DIR := /opt/fs/rootfs/rootfs
all:
$(MAKE) -C $(KDIR) M=$(PWD)
install:
$(MAKE) -C $(KDIR) M=$(PWD) modules_install INSTALL_MOD_PATH=$(INSTALL_DIR)
chmod 755 $(TARGET).ko
cp $(TARGET).ko /opt/fs/rootfs/rootfs/opt/app
clean:
rm -rf *.o *.ko *.mod.c *.temp_versions *.symvers *.order .built* .tmp* .$(TARGET)*

一开始烧录后测试,红色显示蓝色,绿色显示黑色,蓝色显示红色。最后发现原因是:

1
2
3
4
temp = readl(lcd_regs_base + WINCON0);
temp &= ~(0xf << 2);
temp |= (1 << 15) | (0xd << 2) | 1;
writel(temp, lcd_regs_base + WINCON0);

原来设置为 1 << 16 ,应该修改为 1 << 15 ,代表的含义:

1
2
15: Specifies Word swap control bit.
16: Specifies Half-Word swap control bit.

如果使用 16 位色,需要设置为 16

如果使用 32 位色,需要设置为 15

APP

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
/*
* lcd_fb driver for tiny4412
*
* Copyright (c) 2017
* Author: SY <1530454315@qq.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <stdbool.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#define FBIOGET_VSCREENINFO 0x4600
#define FBIOPUT_VSCREENINFO 0x4601
#define FBIOGET_FSCREENINFO 0x4602
#define FBIOGETCMAP 0x4604
#define FBIOPUTCMAP 0x4605
#define FBIOPAN_DISPLAY 0x4606
typedef uint32_t __u32;
typedef uint16_t __u16;
struct fb_bitfield {
__u32 offset; /* beginning of bitfield */
__u32 length; /* length of bitfield */
__u32 msb_right; /* != 0 : Most significant bit is */
/* right */
};
struct fb_fix_screeninfo {
char id[16]; /* identification string eg "TT Builtin" */
unsigned long smem_start; /* Start of frame buffer mem */
/* (physical address) */
__u32 smem_len; /* Length of frame buffer mem */
__u32 type; /* see FB_TYPE_* */
__u32 type_aux; /* Interleave for interleaved Planes */
__u32 visual; /* see FB_VISUAL_* */
__u16 xpanstep; /* zero if no hardware panning */
__u16 ypanstep; /* zero if no hardware panning */
__u16 ywrapstep; /* zero if no hardware ywrap */
__u32 line_length; /* length of a line in bytes */
unsigned long mmio_start; /* Start of Memory Mapped I/O */
/* (physical address) */
__u32 mmio_len; /* Length of Memory Mapped I/O */
__u32 accel; /* Indicate to driver which */
/* specific chip/card we have */
__u16 capabilities; /* see FB_CAP_* */
__u16 reserved[2]; /* Reserved for future compatibility */
};
struct fb_var_screeninfo {
__u32 xres; /* visible resolution */
__u32 yres;
__u32 xres_virtual; /* virtual resolution */
__u32 yres_virtual;
__u32 xoffset; /* offset from virtual to visible */
__u32 yoffset; /* resolution */
__u32 bits_per_pixel; /* guess what */
__u32 grayscale; /* 0 = color, 1 = grayscale, */
/* >1 = FOURCC */
struct fb_bitfield red; /* bitfield in fb mem if true color, */
struct fb_bitfield green; /* else only length is significant */
struct fb_bitfield blue;
struct fb_bitfield transp; /* transparency */
__u32 nonstd; /* != 0 Non standard pixel format */
__u32 activate; /* see FB_ACTIVATE_* */
__u32 height; /* height of picture in mm */
__u32 width; /* width of picture in mm */
__u32 accel_flags; /* (OBSOLETE) see fb_info.flags */
/* Timing: All values in pixclocks, except pixclock (of course) */
__u32 pixclock; /* pixel clock in ps (pico seconds) */
__u32 left_margin; /* time from sync to picture */
__u32 right_margin; /* time from picture to sync */
__u32 upper_margin; /* time from sync to picture */
__u32 lower_margin;
__u32 hsync_len; /* length of horizontal sync */
__u32 vsync_len; /* length of vertical sync */
__u32 sync; /* see FB_SYNC_* */
__u32 vmode; /* see FB_VMODE_* */
__u32 rotate; /* angle we rotate counter clockwise */
__u32 colorspace; /* colorspace for FOURCC-based modes */
__u32 reserved[4]; /* Reserved for future compatibility */
};
#if 0
static void help(void)
{
printf("Usage: ./key <id>\n");
}
#endif
/*
bool esc = false;
static void sigint_handler(int dunno)
{
switch (dunno) {
case SIGINT:
esc = true;
printf("< Ctrl+C > Press.\n");
break;
default:
break;
}
}
*/
#define RED_COLOR565 0xFF0000
#define GREEN_COLOR565 0x00FF00
#define BLUE_COLOR565 0x0000FF
int main(int argc, char **argv)
{
int fb = 0;
struct fb_var_screeninfo vinfo;
struct fb_fix_screeninfo finfo;
long int screen_size = 0;
uint32_t *fbp = NULL;
int x = 0, y = 0;
fb = open("/dev/fb0", O_RDWR);
if(!fb) {
printf("open /dev/fb0 return error\n");
return -1;
}
if(ioctl(fb, FBIOGET_FSCREENINFO, &finfo)) {
printf("get fb fixed infomation return error\n");
return -1;
}
if(ioctl(fb, FBIOGET_VSCREENINFO, &vinfo)) {
printf("get fb variable infomation return error\n");
return -1;
}
screen_size = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;
printf("%d-%d, %dbpp, screen_size = %ld\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel, screen_size);
fbp = (uint32_t *)mmap(0, screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fb, 0);
if(fbp < 0) {
printf("mmap return error\n");
return -1;
}
{
// Red Screen
printf("Red Screen\n");
for(y = 0; y < vinfo.yres/3; y++)
{
for(x = 0; x < vinfo.xres ; x++)
{
*(fbp + y * vinfo.xres + x) = RED_COLOR565;
}
}
// Green Screen
printf("Green Screen\n");
for(y = vinfo.yres/3; y < (vinfo.yres*2)/3; y++)
{
for(x = 0; x < vinfo.xres; x++)
{
*(fbp + y * vinfo.xres + x) =GREEN_COLOR565;
}
}
// Blue Screen
printf("Blue Screen\n");
for(y = (vinfo.yres*2)/3; y < vinfo.yres; y++)
{
for(x = 0; x < vinfo.xres; x++)
{
*(fbp + y * vinfo.xres + x) = BLUE_COLOR565;
}
}
}
munmap(fbp, screen_size);
close(fb);
return 0;
}

makefile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Linux modules compile
# Author : SY
# Time : 2017-8-10 22:29:24
#############################################################################
CC = arm-none-linux-gnueabi-gcc
CFLAGS = -Wall -std=gnu99 -pthread
TARGET = lcd_fb
OBJS = $(TARGET).o
INSTALL_PATH = /opt/fs/rootfs/rootfs/tmp/
$(TARGET) : $(OBJS)
$(CC) $(CFLAGS) -o $@ $<
install:
chmod 755 $(TARGET)
cp $(TARGET) $(INSTALL_PATH)
clean:
rm -rf *.o $(TARGET)

测试

lcd 驱动编译进内核,可以正常加载 4 只小企鹅,但是屏幕在几秒之后会自动熄灭,再向 dev/fb0 写入数据,屏幕不显示,以后待解决。

如果将驱动编译为模块,放在 etc/init.d/rcS

1
2
3
# load lcd
/opt/app/backlight 127
insmod /opt/app/tiny4412_fb.ko

上面表示先打开 lcd 背光,再加载 lcd 模块。

进入 etc/inittab ,输入:

1
2
3
4
::sysinit:/etc/init.d/rcS
::respawn:-/bin/sh
tty0::askfirst:-/bin/sh
::ctrlaltdel:/bin/umount -a -r

其中最重要的是第三段, tty0 设备表示 lcd 输出,askfirst 会询问用户激活命令行,/bin/sh 表示命令行脚本。 这样,重新上电后 lcd 默认加载命令行,插上鼠标后:

1

1

参考

Linux驱动:LCD驱动框架分析

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

SY wechat
扫一扫,用手机访问本站