add: 03_LCD/14,15,18 for stm32mp157
@@ -19,16 +19,118 @@
|
||||
|
||||
### 1. 单Buffer的缺点
|
||||
|
||||
* 如果APP速度很慢,可以看到它在LCD上缓慢绘制图案
|
||||
|
||||
* 即使APP速度很高,LCD控制器不断从Framebuffer中读取数据来显示,而APP不断把数据写入Framebuffer
|
||||
|
||||
* 假设APP想把LCD显示为整屏幕的蓝色、红色
|
||||
|
||||
* 很大几率出现这种情况:
|
||||
|
||||
* LCD控制器读取Framebuffer数据,读到一半时,在LCD上显示了半屏幕的蓝色
|
||||
* 这是APP非常高效地把整个Framebuffer的数据都改为了红色
|
||||
* LCD控制器继续读取数据,于是LCD上就会显示半屏幕蓝色、半屏幕红色
|
||||
* 人眼就会感觉到屏幕闪烁、撕裂
|
||||
|
||||

|
||||
|
||||
### 2. 使用多Buffer来改进
|
||||
|
||||
上述两个缺点的根源是一致的:Framebuffer中的数据还没准备好整帧数据,就被LCD控制器使用了。
|
||||
使用双buffer甚至多buffer可以解决这个问题:
|
||||
|
||||
|
||||
### 3. 内核驱动程序分析
|
||||
|
||||
|
||||
### 4. APP编写方法
|
||||
* 假设有2个Framebuffer:FB0、FB1
|
||||
* LCD控制器正在读取FB0
|
||||
* APP写FB1
|
||||
* 写好FB1后,让LCD控制器切换到FB1
|
||||
* APP写FB0
|
||||
* 写好FB0后,让LCD控制器切换到FB0
|
||||
|
||||
|
||||
|
||||
### 3. 内核驱动程序、APP互相配合使用多buffer
|
||||
|
||||
流程如下:
|
||||
|
||||

|
||||
|
||||
* 驱动:分配多个buffer
|
||||
|
||||
```c
|
||||
fb_info->fix.smem_len = SZ_32M;
|
||||
fbi->screen_base = dma_alloc_writecombine(fbi->device,
|
||||
fbi->fix.smem_len,
|
||||
(dma_addr_t *)&fbi->fix.smem_start,
|
||||
GFP_DMA | GFP_KERNEL);
|
||||
```
|
||||
|
||||
|
||||
|
||||
* 驱动:保存buffer信息
|
||||
|
||||
```c
|
||||
fb_info->fix.smem_len // 含有总buffer大小
|
||||
fb_info->var // 含有单个buffer信息
|
||||
```
|
||||
|
||||
* APP:读取buffer信息
|
||||
|
||||
```c
|
||||
ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix);
|
||||
ioctl(fd_fb, FBIOGET_VSCREENINFO, &var);
|
||||
|
||||
// 计算是否支持多buffer,有多少个buffer
|
||||
screen_size = var.xres * var.yres * var.bits_per_pixel / 8;
|
||||
nBuffers = fix.smem_len / screen_size;
|
||||
```
|
||||
|
||||
* APP:使能多buffer
|
||||
|
||||
```c
|
||||
var.yres_virtual = nBuffers * var.yres;
|
||||
ioctl(fd_fb, FBIOPUT_VSCREENINFO, &var);
|
||||
```
|
||||
|
||||
* APP:写buffer
|
||||
|
||||
```c
|
||||
fb_base = (unsigned char *)mmap(NULL , fix.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
|
||||
|
||||
/* get buffer */
|
||||
pNextBuffer = fb_base + nNextBuffer * screen_size;
|
||||
|
||||
/* set buffer */
|
||||
lcd_draw_screen(pNextBuffer, colors[i]);
|
||||
```
|
||||
|
||||
* APP:开始切换buffer
|
||||
|
||||
```c
|
||||
/* switch buffer */
|
||||
var.yoffset = nNextBuffer * var.yres;
|
||||
ioctl(fd_fb, FBIOPAN_DISPLAY, &var);
|
||||
```
|
||||
|
||||
* 驱动:切换buffer
|
||||
|
||||
```c
|
||||
// fbmem.c
|
||||
fb_ioctl
|
||||
do_fb_ioctl
|
||||
fb_pan_display(info, &var);
|
||||
err = info->fbops->fb_pan_display(var, info) // 调用硬件相关的函数
|
||||
```
|
||||
|
||||
示例:
|
||||

|
||||
|
||||
|
||||
|
||||
* APP:等待切换完成(在驱动程序中已经等待切换完成了,所以这个调用并无必要)
|
||||
|
||||
```c
|
||||
ret = 0;
|
||||
ioctl(fd_fb, FBIO_WAITFORVSYNC, &ret);
|
||||
```
|
||||
|
||||
|
||||
BIN
IMX6ULL/doc_pic/03_LCD/pic/02_LCD驱动/048_singble_buffer.png
Normal file
|
After Width: | Height: | Size: 260 KiB |
|
After Width: | Height: | Size: 23 KiB |
BIN
IMX6ULL/doc_pic/03_LCD/pic/02_LCD驱动/050_mxsfb_pan_display.png
Normal file
|
After Width: | Height: | Size: 71 KiB |
@@ -71,6 +71,10 @@ git clone https://e.coding.net/weidongshan/doc_and_source_for_drivers.git
|
||||
* 2021.01.28 发布"LCD驱动":16\_上机实验\_基于IMX6ULL
|
||||
* 2021.01.29 发布"LCD驱动":17\_单Buffer的缺点与改进方法
|
||||
* 2021.02.01 发布"LCD驱动":18\_编写使用多buffer的应用程序
|
||||
* 2021.02.03 发布"LCD驱动":
|
||||
* 15\_编程_配置LCD控制器之寄存器操作\_基于STM32MP157
|
||||
* 16\_上机实验\_基于STM32MP157
|
||||
* 18\_STM32MP157内核自带的LCD驱动不支持多buffer
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -222,8 +222,8 @@ CFBADD:设置LAYER1(图层1)的显存地址。
|
||||
|
||||

|
||||
|
||||
CFBP:Framebuffer中以行像素所占据的字节数
|
||||
CFBLL:Framebuffer中以行像素所占据的字节数+7
|
||||
CFBP:Framebuffer中一行像素所占据的字节数
|
||||
CFBLL:Framebuffer中一行像素所占据的字节数+7
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -108,7 +108,8 @@ LCD驱动程序的核心就是:
|
||||
参考内核文件:
|
||||
|
||||
* `drivers\video\of_videomode.c`
|
||||
* `drivers\video\fbdev\mxsfb.c`
|
||||
* GIT仓库里IMX6ULL的驱动程序
|
||||
* `STM32MP157\source\A7\03_LCD\12_lcd_drv_imx6ull_from_kernel_4.9.88\mxsfb.c`
|
||||
|
||||
#### 3.2 使用参数配置LCD控制器
|
||||
|
||||
125
STM32MP157/doc_pic/03_LCD/15_编程_配置LCD控制器之寄存器操作_基于STM32MP157.md
Normal file
@@ -0,0 +1,125 @@
|
||||
## 编程\_配置LCD控制器之寄存器操作\_基于STM32MP157
|
||||
|
||||
参考资料,GIT仓库里:
|
||||
|
||||
* 芯片资料
|
||||
|
||||
* `STM32MP157\开发板配套资料\datasheeet\02_Core_board(核心板)\CPU\CPU开发参考手册\DM00327659.pdf`
|
||||
* `《35 LCD-TFT display controller (LTDC)》`
|
||||
|
||||
* STM32MP157的LCD裸机程序
|
||||
|
||||
* `STM32MP157\source\A7\03_LCD\05_参考的裸机源码\03_font_test`
|
||||
|
||||
* 内核自带的STM32MP157 LCD驱动程序
|
||||
* 驱动源码:
|
||||
|
||||
* LCD相关:`Linux-5.4\drivers\gpu\drm\panel\panel-myir070tft.c`
|
||||
* LCD控制器相关:`Linux-5.4\drivers\gpu\drm\stm\ltdc.c`
|
||||
* GPU相关:`Linux-5.4\drivers\gpu\drm\stm\drv.c`
|
||||
* 设备树:
|
||||
* `Linux-5.4/arch/arm/boot/dts/stm32mp157c-100ask-512d-lcd-v1.dts`
|
||||
* `Linux-5.4/arch/arm/boot/dts/stm32mp151.dtsi`
|
||||
* `Linux-5.4/arch/arm/boot/dts/stm32mp15-pinctrl.dtsi`
|
||||
|
||||
* 本节视频编写好的代码
|
||||
|
||||
* `STM32MP157\source\A7\03_LCD\10_lcd_drv_lcdcontroller_reg_config_use_devicetre`
|
||||
|
||||
* 引脚配置工具/设备树生成工具
|
||||
|
||||
* 打开:http://download.100ask.net/
|
||||
* 找到开发板:"100ASK_STM32MP157_PRO开发板"
|
||||
* 下载开发板配套资料
|
||||
* 下载完后,工具在如下目录里:
|
||||
|
||||

|
||||
|
||||
### 1. 硬件相关的操作
|
||||
|
||||
LCD驱动程序的核心就是:
|
||||
|
||||
* 分配fb_info
|
||||
* 设置fb_info
|
||||
* 注册fb_info
|
||||
* 硬件相关的设置
|
||||
|
||||
|
||||
|
||||
硬件相关的设置又可以分为3部分:
|
||||
* 引脚设置
|
||||
* 时钟设置
|
||||
* LCD控制器设置
|
||||
|
||||
|
||||
|
||||
### 2. 在设备树里指定LCD参数
|
||||
|
||||
```shell
|
||||
framebuffer-mylcd {
|
||||
compatible = "100ask,lcd_drv";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&mylcd_pinctrl>;
|
||||
backlight-gpios = <&gpio1 8 GPIO_ACTIVE_HIGH>;
|
||||
|
||||
clocks = <&clks IMX6UL_CLK_LCDIF_PIX>,
|
||||
<&clks IMX6UL_CLK_LCDIF_APB>;
|
||||
clock-names = "pix", "axi";
|
||||
|
||||
display = <&display0>;
|
||||
|
||||
display0: display {
|
||||
bits-per-pixel = <24>;
|
||||
bus-width = <24>;
|
||||
|
||||
display-timings {
|
||||
native-mode = <&timing0>;
|
||||
|
||||
timing0: timing0_1024x768 {
|
||||
clock-frequency = <50000000>;
|
||||
hactive = <1024>;
|
||||
vactive = <600>;
|
||||
hfront-porch = <160>;
|
||||
hback-porch = <140>;
|
||||
hsync-len = <20>;
|
||||
vback-porch = <20>;
|
||||
vfront-porch = <12>;
|
||||
vsync-len = <3>;
|
||||
|
||||
hsync-active = <0>;
|
||||
vsync-active = <0>;
|
||||
de-active = <1>;
|
||||
pixelclk-active = <0>;
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 3. 编程
|
||||
|
||||
#### 3.1 从设备树获得参数
|
||||
|
||||
时序参数、引脚极性等信息,都被保存在一个display_timing结构体里:
|
||||
|
||||

|
||||
|
||||
参考内核文件:
|
||||
|
||||
* `drivers\video\of_display_timing.c`
|
||||
|
||||
* `drivers\video\fbdev\mxsfb.c`
|
||||
|
||||
|
||||
|
||||
#### 3.2 使用参数配置LCD控制器
|
||||
|
||||
根据芯片手册,一个一个设置寄存器:
|
||||
|
||||
* Framebuffer地址设置
|
||||
* Framebuffer中数据格式设置
|
||||
* LCD时序参数设置
|
||||
* LCD引脚极性设置
|
||||
229
STM32MP157/doc_pic/03_LCD/16_上机实验_基于STM32MP157.md
Normal file
@@ -0,0 +1,229 @@
|
||||
## 上机实验\_基于STM32MP157
|
||||
|
||||
参考资料,GIT仓库里:
|
||||
|
||||
* 芯片资料
|
||||
|
||||
* `STM32MP157\开发板配套资料\datasheeet\02_Core_board(核心板)\CPU\CPU开发参考手册\DM00327659.pdf`
|
||||
* `《35 LCD-TFT display controller (LTDC)》`
|
||||
|
||||
* STM32MP157的LCD裸机程序
|
||||
|
||||
* `STM32MP157\source\A7\03_LCD\05_参考的裸机源码\03_font_test`
|
||||
|
||||
* 内核自带的STM32MP157 LCD驱动程序
|
||||
* 驱动源码:
|
||||
|
||||
* LCD相关:`Linux-5.4\drivers\gpu\drm\panel\panel-myir070tft.c`
|
||||
* LCD控制器相关:`Linux-5.4\drivers\gpu\drm\stm\ltdc.c`
|
||||
* GPU相关:`Linux-5.4\drivers\gpu\drm\stm\drv.c`
|
||||
* 设备树:
|
||||
* `Linux-5.4/arch/arm/boot/dts/stm32mp157c-100ask-512d-lcd-v1.dts`
|
||||
* `Linux-5.4/arch/arm/boot/dts/stm32mp151.dtsi`
|
||||
* `Linux-5.4/arch/arm/boot/dts/stm32mp15-pinctrl.dtsi`
|
||||
|
||||
* 本节视频测试通过的代码
|
||||
|
||||
* `STM32MP157\source\A7\03_LCD\11_lcd_drv_stm32mp157_ok`
|
||||
|
||||
* 搭建开发环境
|
||||
* 视频:https://www.100ask.net/
|
||||
* 《Linux系列教程之快速入门》之《【第2篇】环境搭建、Linux基本操作、工具使用》
|
||||
* 文档:` git clone https://e.coding.net/weidongshan/01_all_series_quickstart.git`
|
||||
* 《嵌入式Linux应用开发完全手册\_韦东山全系列视频文档全集V2.8.pdf》
|
||||
|
||||
### 1. 要做的事情
|
||||
|
||||
* 去除内核自带的驱动程序
|
||||
|
||||
* 加入我们编写的驱动程序、设备树文件
|
||||
|
||||
* 重新编译内核、设备树
|
||||
|
||||
* 上机测试:使用编译出来的内核、设备树启动板子
|
||||
|
||||
|
||||
|
||||
### 2. 去除内核自带的驱动程序
|
||||
* 设置工具链
|
||||
|
||||
```shell
|
||||
source /home/book/100ask_stm32mp157_pro-sdk/ToolChain/openstlinux_eglfs-linux-gnueabi/environment-setup-cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi
|
||||
export ARCH=arm
|
||||
export CROSS_COMPILE=arm-ostl-linux-gnueabi-
|
||||
```
|
||||
|
||||
* 配置内核使用STM32MP157默认配置
|
||||
|
||||
```shell
|
||||
~/100ask_stm32mp157_pro-sdk/$ cd Linux-5.4
|
||||
~/100ask_stm32mp157_pro-sdk/Linux-5.4$ make 100ask_stm32mp157_pro_defconfig
|
||||
```
|
||||
* 去掉自带的驱动程序:执行`make menuconfig`,如下配置内核
|
||||
```shell
|
||||
Device Drivers --->
|
||||
Graphics support --->
|
||||
< > DRM Support for STMicroelectronics SoC Series // 输入N
|
||||
```
|
||||
|
||||
|
||||
|
||||
* 解决可能出现的问题
|
||||
|
||||
* 如果执行`make menuconfnig`出现如下问题:
|
||||
|
||||

|
||||
|
||||
* 就先执行以下命令:
|
||||
|
||||
```shell
|
||||
sudo apt install lib32ncursesw5 lib32ncursesw5-dev
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 3. 加入新驱动程序、设备树
|
||||
* 复制驱动程序:
|
||||
* 把`11_lcd_drv_stm32mp157_ok\lcd_drv.c`放到内核源码目录`drivers/video/fbdev`
|
||||
* 备份内核自带设备树文件:`arch/arm/boot/dts/stm32mp157c-100ask-512d-lcd-v1.dts`
|
||||
* 把`11_lcd_drv_stm32mp157_ok\stm32mp157c-100ask-512d-lcd-v1.dts`放到内核源码目录`arch/arm/boot/dts/`
|
||||
|
||||
* 修改内核文件:
|
||||
* 修改:`drivers/video/fbdev/Makefile`,使用我们提供的lcd_drv.c,如下:
|
||||
|
||||
```shell
|
||||
obj-y += lcd_drv.o
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 4. 重新编译内核、设备树
|
||||
|
||||
**以下命令在Ubuntu中执行。**
|
||||
|
||||
* 设置工具链
|
||||
|
||||
```shell
|
||||
source /home/book/100ask_stm32mp157_pro-sdk/ToolChain/openstlinux_eglfs-linux-gnueabi/environment-setup-cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi
|
||||
export ARCH=arm
|
||||
export CROSS_COMPILE=arm-ostl-linux-gnueabi-
|
||||
```
|
||||
|
||||
* 配置、编译
|
||||
|
||||
```shell
|
||||
~/100ask_stm32mp157_pro-sdk/$ cd Linux-5.4
|
||||
~/100ask_stm32mp157_pro-sdk/Linux-5.4$
|
||||
~/100ask_stm32mp157_pro-sdk/Linux-5.4$ make uImage LOADADDR=0xC2000040
|
||||
~/100ask_stm32mp157_pro-sdk/Linux-5.4$ make dtbs
|
||||
```
|
||||
|
||||
* 得到
|
||||
* 内核:`arch/arm/boot/uImage`
|
||||
* 设备树文件:`arch/arm/boot/dts/stm32mp157c-100ask-512d-lcd-v1.dtb`
|
||||
|
||||
* 复制到NFS目录:
|
||||
|
||||
```shell
|
||||
$ cp arch/arm/boot/uImage ~/nfs_rootfs/
|
||||
$ cp arch/arm/boot/dts/stm32mp157c-100ask-512d-lcd-v1.dtb ~/nfs_rootfs/
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 5. 上机测试
|
||||
|
||||
**以下命令在开发板中执行。**
|
||||
|
||||
#### 5.1 更换内核、设备树
|
||||
|
||||
* 挂载NFS
|
||||
|
||||
* vmware使用NAT(假设windowsIP为192.168.1.100)
|
||||
|
||||
```shell
|
||||
[root@100ask:~]# mount -t nfs -o nolock,vers=3,port=2049,mountport=9999
|
||||
192.168.1.100:/home/book/nfs_rootfs /mnt
|
||||
```
|
||||
|
||||
* vmware使用桥接,或者不使用vmware而是直接使用服务器:假设Ubuntu IP为192.168.1.137
|
||||
|
||||
```shell
|
||||
[root@100ask:~]# mount -t nfs -o nolock,vers=3 192.168.1.137:/home/book/nfs_rootfs /mnt
|
||||
```
|
||||
|
||||
* 确定单板上内核、设备树保存在哪里
|
||||
|
||||
由于版本变化,STM32MP157单板上烧录的系统可能有细微差别,在开发板上执行`cat /proc/mounts`后,
|
||||
可以得到两种结果(见下图):
|
||||
|
||||
* 保存内核、设备树的分区,挂载在/boot目录下:无需特殊操作
|
||||
* 保存内核、设备树的分区,挂载在/mnt目录下
|
||||
* 在视频里、后面文档里,都是更新/boot目录下的文件,所以要先执行以下命令重新挂载:
|
||||
* `mount /dev/mmcblk2p2 /boot`
|
||||
|
||||
|
||||

|
||||
|
||||
* 更新单板文件
|
||||
|
||||
```shell
|
||||
[root@100ask:~]# cp /mnt/uImage /boot
|
||||
[root@100ask:~]# cp /mnt/stm32mp157c-100ask-512d-lcd-v1.dtb /boot
|
||||
[root@100ask:~]# sync
|
||||
```
|
||||
|
||||
* 重启开发板观察现象
|
||||
|
||||
* 如果可以看到企鹅LOGO,就表示正常
|
||||
|
||||
* 如果在终端中可以查看到存在`/dev/fb0`节点,也表示正常
|
||||
|
||||
|
||||
|
||||
|
||||
#### 5.2 板子无法启动,使用uboot下载内核、设备树
|
||||
|
||||
使用新内核、设备树启动单板时,打印`Starting kernel`后就再无输出,串口信息如下:
|
||||

|
||||
|
||||
这表示内核或设备树有问题,只能使用uboot来下载其他内核、设备树来调试。
|
||||
|
||||
重启开发板,按住空格进入uboot,在uboot使用nfs命令下载uImage、设备树来启动开发板。
|
||||
|
||||
* 以下命令适用于:vmware使用桥接
|
||||
|
||||
```shell
|
||||
=> setenv ipaddr 192.168.1.112 //设置开发板的IP地址。
|
||||
=> nfs c4000000 192.168.1.137:/home/book/nfs_rootfs/stm32mp157c-100ask-512d-lcd-v1.dtb
|
||||
=> nfs c4100000 192.168.1.137:/home/book/nfs_rootfs/uImage
|
||||
=> setenv bootargs root=/dev/mmcblk2p3 rw
|
||||
=> bootm c4100000 - 0xc4000000
|
||||
```
|
||||
|
||||
|
||||
|
||||
* 以下命令使用于:vmware使用NAT,并且先启动tftpd64,在tftpd64所设置目录下放入uImage和设备树文件
|
||||
|
||||
```shell
|
||||
=> setenv serverip 192.168.1.100 //设置服务器的IP地址,这里指的是Ubuntu主机IP
|
||||
=> setenv ipaddr 192.168.1.112 //设置开发板的IP地址。
|
||||
=> tftpboot 0xc4000000 stm32mp157c-100ask-512d-lcd-v1.dtb
|
||||
=> tftpboot c4100000 uImage
|
||||
=> setenv bootargs root=/dev/mmcblk2p3 rw
|
||||
=> bootm c4100000 - 0xc4000000
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 5. 问题所在
|
||||
|
||||
* 设备树中引脚冲突,如下修改
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
* 驱动程序函数使用方法有变化
|
||||
|
||||

|
||||
@@ -19,16 +19,118 @@
|
||||
|
||||
### 1. 单Buffer的缺点
|
||||
|
||||
* 如果APP速度很慢,可以看到它在LCD上缓慢绘制图案
|
||||
|
||||
* 即使APP速度很高,LCD控制器不断从Framebuffer中读取数据来显示,而APP不断把数据写入Framebuffer
|
||||
|
||||
* 假设APP想把LCD显示为整屏幕的蓝色、红色
|
||||
|
||||
* 很大几率出现这种情况:
|
||||
|
||||
* LCD控制器读取Framebuffer数据,读到一半时,在LCD上显示了半屏幕的蓝色
|
||||
* 这是APP非常高效地把整个Framebuffer的数据都改为了红色
|
||||
* LCD控制器继续读取数据,于是LCD上就会显示半屏幕蓝色、半屏幕红色
|
||||
* 人眼就会感觉到屏幕闪烁、撕裂
|
||||
|
||||

|
||||
|
||||
### 2. 使用多Buffer来改进
|
||||
|
||||
上述两个缺点的根源是一致的:Framebuffer中的数据还没准备好整帧数据,就被LCD控制器使用了。
|
||||
使用双buffer甚至多buffer可以解决这个问题:
|
||||
|
||||
|
||||
### 3. 内核驱动程序分析
|
||||
|
||||
|
||||
### 4. APP编写方法
|
||||
* 假设有2个Framebuffer:FB0、FB1
|
||||
* LCD控制器正在读取FB0
|
||||
* APP写FB1
|
||||
* 写好FB1后,让LCD控制器切换到FB1
|
||||
* APP写FB0
|
||||
* 写好FB0后,让LCD控制器切换到FB0
|
||||
|
||||
|
||||
|
||||
### 3. 内核驱动程序、APP互相配合使用多buffer
|
||||
|
||||
流程如下:
|
||||
|
||||

|
||||
|
||||
* 驱动:分配多个buffer
|
||||
|
||||
```c
|
||||
fb_info->fix.smem_len = SZ_32M;
|
||||
fbi->screen_base = dma_alloc_writecombine(fbi->device,
|
||||
fbi->fix.smem_len,
|
||||
(dma_addr_t *)&fbi->fix.smem_start,
|
||||
GFP_DMA | GFP_KERNEL);
|
||||
```
|
||||
|
||||
|
||||
|
||||
* 驱动:保存buffer信息
|
||||
|
||||
```c
|
||||
fb_info->fix.smem_len // 含有总buffer大小
|
||||
fb_info->var // 含有单个buffer信息
|
||||
```
|
||||
|
||||
* APP:读取buffer信息
|
||||
|
||||
```c
|
||||
ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix);
|
||||
ioctl(fd_fb, FBIOGET_VSCREENINFO, &var);
|
||||
|
||||
// 计算是否支持多buffer,有多少个buffer
|
||||
screen_size = var.xres * var.yres * var.bits_per_pixel / 8;
|
||||
nBuffers = fix.smem_len / screen_size;
|
||||
```
|
||||
|
||||
* APP:使能多buffer
|
||||
|
||||
```c
|
||||
var.yres_virtual = nBuffers * var.yres;
|
||||
ioctl(fd_fb, FBIOPUT_VSCREENINFO, &var);
|
||||
```
|
||||
|
||||
* APP:写buffer
|
||||
|
||||
```c
|
||||
fb_base = (unsigned char *)mmap(NULL , fix.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
|
||||
|
||||
/* get buffer */
|
||||
pNextBuffer = fb_base + nNextBuffer * screen_size;
|
||||
|
||||
/* set buffer */
|
||||
lcd_draw_screen(pNextBuffer, colors[i]);
|
||||
```
|
||||
|
||||
* APP:开始切换buffer
|
||||
|
||||
```c
|
||||
/* switch buffer */
|
||||
var.yoffset = nNextBuffer * var.yres;
|
||||
ioctl(fd_fb, FBIOPAN_DISPLAY, &var);
|
||||
```
|
||||
|
||||
* 驱动:切换buffer
|
||||
|
||||
```c
|
||||
// fbmem.c
|
||||
fb_ioctl
|
||||
do_fb_ioctl
|
||||
fb_pan_display(info, &var);
|
||||
err = info->fbops->fb_pan_display(var, info) // 调用硬件相关的函数
|
||||
```
|
||||
|
||||
示例:
|
||||

|
||||
|
||||
|
||||
|
||||
* APP:等待切换完成(在驱动程序中已经等待切换完成了,所以这个调用并无必要)
|
||||
|
||||
```c
|
||||
ret = 0;
|
||||
ioctl(fd_fb, FBIO_WAITFORVSYNC, &ret);
|
||||
```
|
||||
|
||||
|
||||
179
STM32MP157/doc_pic/03_LCD/18_STM32MP157内核自带的LCD驱动不支持多buffer.md
Normal file
@@ -0,0 +1,179 @@
|
||||
## STM32MP157内核自带的LCD驱动不支持多buffer
|
||||
|
||||
* 本节视频编写好的程序,在GIT仓库里
|
||||
* `IMX6ULL\source\03_LCD\14_use_multi_framebuffer`
|
||||
* `STM32MP157\source\A7\03_LCD\14_use_multi_framebuffer`
|
||||
|
||||
* 参考程序:应用基础课程里使用Framebuffer的精简程序
|
||||
* `IMX6ULL\source\03_LCD\14_use_multi_framebuffer\reference\07_framebuffer`
|
||||
* `STM32MP157\source\A7\03_LCD\14_use_multi_framebuffer\reference\07_framebuffer`
|
||||
|
||||
* 参考程序:使用多buffer的APP,在GIT仓库里
|
||||
* `IMX6ULL\source\03_LCD\13_multi_framebuffer_example\testcamera`
|
||||
* `STM32MP157\source\A7\03_LCD\13_multi_framebuffer_example\testcamera`
|
||||
|
||||
|
||||
|
||||
### 1. STM32MP157内核自带的LCD驱动不支持都buffer
|
||||
|
||||
所以无法在STM32MP157观察到多buffer的效果。
|
||||
如果想学习多buffer的APP如何编写,请学习IMX6ULL的视频:`18_编写使用多buffer的应用程序`
|
||||
|
||||
|
||||
|
||||
### 2. 恢复自带的驱动(不想那么复杂的话,直接重烧系统)
|
||||
|
||||
**注意**:
|
||||
|
||||
* 一旦使用重新编译的内核,板子自带的GUI无法运行
|
||||
|
||||
* 原因在于内核重新编译后,也需要重新编译、安装各类模块,还有第3方模块。
|
||||
|
||||
* 编译第3方模块涉及buildroot的使用
|
||||
|
||||
* 这些不是LCD的内容,所以建议还是直接通过USB恢复系统
|
||||
|
||||
|
||||
|
||||
#### 2.1 在Ubuntu上编译内核、设备树
|
||||
|
||||
* 设置工具链,执行如下命令:
|
||||
|
||||
```shell
|
||||
source /home/book/100ask_stm32mp157_pro-sdk/ToolChain/openstlinux_eglfs-linux-gnueabi/environment-setup-cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi
|
||||
export ARCH=arm
|
||||
export CROSS_COMPILE=arm-ostl-linux-gnueabi-
|
||||
```
|
||||
|
||||
|
||||
|
||||
* 去掉自己编写的驱动程序
|
||||
* 修改内核文件:`drivers/video/fbdev/Makefile`,如下注释掉lcd_drv.o那行:
|
||||
|
||||
```shell
|
||||
#obj-y += lcd_drv.o
|
||||
```
|
||||
|
||||
* 重新配置内核选择自带驱动程序,执行`make menuconfig`,如下配置2项:
|
||||
|
||||
```shell
|
||||
Device Drivers --->
|
||||
Graphics support --->
|
||||
<*> DRM Support for STMicroelectronics SoC Series // 输入Y选择
|
||||
<*> STMicroelectronics specific extensions for Synopsys MIPI DSI // 输入Y选择
|
||||
```
|
||||
|
||||
* 恢复设备树
|
||||
|
||||
* 把GIT仓库中`STM32MP157\source\A7\03_LCD\11_lcd_drv_stm32mp157_ok\origin\stm32mp157c-100ask-512d-lcd-v1.dts`
|
||||
* 复制到内核目录:`arch/arm/boot/dts`目录下
|
||||
|
||||
* 编译内核、设备树
|
||||
|
||||
```shell
|
||||
~/100ask_stm32mp157_pro-sdk/$ cd Linux-5.4
|
||||
~/100ask_stm32mp157_pro-sdk/Linux-5.4$ make uImage LOADADDR=0xC2000040
|
||||
~/100ask_stm32mp157_pro-sdk/Linux-5.4$ make dtbs
|
||||
```
|
||||
|
||||
* 得到
|
||||
* 内核:`arch/arm/boot/uImage`
|
||||
* 设备树文件:`arch/arm/boot/dts/stm32mp157c-100ask-512d-lcd-v1.dtb`
|
||||
|
||||
* 复制到NFS目录:
|
||||
|
||||
```shell
|
||||
$ cp arch/arm/boot/uImage ~/nfs_rootfs/
|
||||
$ cp arch/arm/boot/dts/stm32mp157c-100ask-512d-lcd-v1.dtb ~/nfs_rootfs/
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 2.2 在开发板上通过NFS更新内核、设备树
|
||||
|
||||
* 挂载NFS
|
||||
|
||||
* vmware使用NAT(假设windowsIP为192.168.1.100)
|
||||
|
||||
```shell
|
||||
[root@100ask:~]# mount -t nfs -o nolock,vers=3,port=2049,mountport=9999
|
||||
192.168.1.100:/home/book/nfs_rootfs /mnt
|
||||
```
|
||||
|
||||
* vmware使用桥接,或者不使用vmware而是直接使用服务器:假设Ubuntu IP为192.168.1.137
|
||||
|
||||
```shell
|
||||
[root@100ask:~]# mount -t nfs -o nolock,vers=3 192.168.1.137:/home/book/nfs_rootfs /mnt
|
||||
```
|
||||
|
||||
* 确定单板上内核、设备树保存在哪里
|
||||
|
||||
由于版本变化,STM32MP157单板上烧录的系统可能有细微差别,在开发板上执行`cat /proc/mounts`后,
|
||||
可以得到两种结果(见下图):
|
||||
|
||||
* 保存内核、设备树的分区,挂载在/boot目录下:无需特殊操作
|
||||
* 保存内核、设备树的分区,挂载在/mnt目录下
|
||||
* 在视频里、后面文档里,都是更新/boot目录下的文件,所以要先执行以下命令重新挂载:
|
||||
* `mount /dev/mmcblk2p2 /boot`
|
||||
|
||||
|
||||

|
||||
|
||||
* 更新单板文件
|
||||
|
||||
```shell
|
||||
[root@100ask:~]# cp /mnt/uImage /boot
|
||||
[root@100ask:~]# cp /mnt/stm32mp157c-100ask-512d-lcd-v1.dtb /boot
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### 3. 编译、运行APP
|
||||
|
||||
* 设置工具链,执行如下命令:
|
||||
|
||||
```shell
|
||||
export ARCH=arm
|
||||
export CROSS_COMPILE=arm-buildroot-linux-gnueabihf-
|
||||
export PATH=$PATH:/home/book/100ask_stm32mp157_pro-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin
|
||||
```
|
||||
|
||||
* 上传代码、编译
|
||||
* 代码位置:GIT仓库`STM32MP157\source\A7\03_LCD\14_use_multi_framebuffer`
|
||||
* 上传到Ubuntu
|
||||
* 在`14_use_multi_framebuffer`目录下执行make,可以得到`multi_framebuffer_test`
|
||||
|
||||
* 通过NFS放到开发板/bin目录
|
||||
|
||||
```shell
|
||||
mount -t nfs -o nolock,vers=3 192.168.1.137:/home/book/nfs_rootfs /mnt
|
||||
cp /mnt/multi_framebuffer_test /bin
|
||||
```
|
||||
|
||||
|
||||
|
||||
* 在开发板执行
|
||||
|
||||
```shell
|
||||
systemctl stop myir //关闭自带的GUI程序
|
||||
multi_framebuffer_test double 或 multi_framebuffer_test single
|
||||
```
|
||||
|
||||
* 由于STM32MP157自带的LCD驱动不支持多buffer,上述命令效果一样
|
||||
|
||||
|
||||
|
||||
### 4. LCD自动黑屏
|
||||
|
||||
为了省电,LCD在10分钟左右会自动黑屏。
|
||||
|
||||
我们可以禁止LCD自动黑屏,执行以下命令即可:
|
||||
|
||||
```shell
|
||||
#close lcd sleep
|
||||
echo -e "\033[9;0]" > /dev/tty1
|
||||
echo -e "\033[?25l" > /dev/tty1
|
||||
```
|
||||
|
||||
|
After Width: | Height: | Size: 56 KiB |
BIN
STM32MP157/doc_pic/03_LCD/pic/02_LCD驱动/044_cannot_boot.png
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
STM32MP157/doc_pic/03_LCD/pic/02_LCD驱动/045_pins_conflict.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
STM32MP157/doc_pic/03_LCD/pic/02_LCD驱动/046_dma_alloc_wc.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
STM32MP157/doc_pic/03_LCD/pic/02_LCD驱动/047_boot_mount.png
Normal file
|
After Width: | Height: | Size: 348 KiB |
BIN
STM32MP157/doc_pic/03_LCD/pic/02_LCD驱动/048_singble_buffer.png
Normal file
|
After Width: | Height: | Size: 260 KiB |
|
After Width: | Height: | Size: 23 KiB |
BIN
STM32MP157/doc_pic/03_LCD/pic/02_LCD驱动/050_mxsfb_pan_display.png
Normal file
|
After Width: | Height: | Size: 71 KiB |
@@ -0,0 +1,420 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/err.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/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <asm/div64.h>
|
||||
|
||||
#include <asm/mach/map.h>
|
||||
|
||||
struct stm32mp157_lcdif {
|
||||
volatile unsigned int LTDC_IDR;
|
||||
volatile unsigned int LTDC_LCR;
|
||||
volatile unsigned int LTDC_SSCR;
|
||||
volatile unsigned int LTDC_BPCR;
|
||||
volatile unsigned int LTDC_AWCR;
|
||||
volatile unsigned int LTDC_TWCR;
|
||||
volatile unsigned int LTDC_GCR;
|
||||
volatile unsigned int LTDC_GC1R;
|
||||
volatile unsigned int LTDC_GC2R;
|
||||
volatile unsigned int LTDC_SRCR;
|
||||
unsigned char RESERVED_0[4];
|
||||
volatile unsigned int LTDC_BCCR;
|
||||
unsigned char RESERVED_1[4];
|
||||
volatile unsigned int LTDC_IER;
|
||||
volatile unsigned int LTDC_ISR;
|
||||
volatile unsigned int LTDC_ICR;
|
||||
volatile unsigned int LTDC_LIPCR;
|
||||
volatile unsigned int LTDC_CPSR;
|
||||
volatile unsigned int LTDC_CDSR;
|
||||
unsigned char RESERVED_2[56];
|
||||
volatile unsigned int LTDC_L1CR;
|
||||
volatile unsigned int LTDC_L1WHPCR;
|
||||
volatile unsigned int LTDC_L1WVPCR;
|
||||
volatile unsigned int LTDC_L1CKCR;
|
||||
volatile unsigned int LTDC_L1PFCR;
|
||||
volatile unsigned int LTDC_L1CACR;
|
||||
volatile unsigned int LTDC_L1DCCR;
|
||||
volatile unsigned int LTDC_L1BFCR;
|
||||
unsigned char RESERVED_3[8];
|
||||
volatile unsigned int LTDC_L1CFBAR;
|
||||
volatile unsigned int LTDC_L1CFBLR;
|
||||
volatile unsigned int LTDC_L1CFBLNR;
|
||||
unsigned char RESERVED_4[12];
|
||||
volatile unsigned int LTDC_L1CLUTWR;
|
||||
unsigned char RESERVED_5[60];
|
||||
volatile unsigned int LTDC_L2CR;
|
||||
volatile unsigned int LTDC_L2WHPCR;
|
||||
volatile unsigned int LTDC_L2WVPCR;
|
||||
volatile unsigned int LTDC_L2CKCR;
|
||||
volatile unsigned int LTDC_L2PFCR;
|
||||
volatile unsigned int LTDC_L2CACR;
|
||||
volatile unsigned int LTDC_L2DCCR;
|
||||
volatile unsigned int LTDC_L2BFCR;
|
||||
unsigned char RESERVED_6[8];
|
||||
volatile unsigned int LTDC_L2CFBAR;
|
||||
volatile unsigned int LTDC_L2CFBLR;
|
||||
volatile unsigned int LTDC_L2CFBLNR;
|
||||
unsigned char RESERVED_7[12];
|
||||
volatile unsigned int LTDC_L2CLUTWR;
|
||||
};
|
||||
|
||||
|
||||
struct lcd_regs {
|
||||
volatile unsigned int fb_base_phys;
|
||||
volatile unsigned int fb_xres;
|
||||
volatile unsigned int fb_yres;
|
||||
volatile unsigned int fb_bpp;
|
||||
};
|
||||
|
||||
static struct lcd_regs *mylcd_regs;
|
||||
|
||||
static struct fb_info *myfb_info;
|
||||
|
||||
static unsigned int pseudo_palette[16];
|
||||
|
||||
static struct gpio_desc *bl_gpio;
|
||||
|
||||
static struct clk *pixel_clk;
|
||||
|
||||
struct stm32mp157_lcdif *lcdif;
|
||||
|
||||
static void Stm32mp157_lcd_controller_enable(struct stm32mp157_lcdif *lcdif)
|
||||
{
|
||||
lcdif->LTDC_SRCR |= 1; /*加载LAYER的参数*/
|
||||
lcdif->LTDC_GCR |= 1<<0; /* 使能STM32MP157的LCD控制器 */
|
||||
}
|
||||
|
||||
static int lcd_controller_init(struct stm32mp157_lcdif *lcdif, struct display_timing *dt, int lcd_bpp, int fb_bpp, unsigned int fb_phy)
|
||||
{
|
||||
int bpp_mode;
|
||||
int pol_vclk = 0;
|
||||
int pol_vsync = 0;
|
||||
int pol_hsync = 0;
|
||||
int pol_de = 0;
|
||||
|
||||
lcd_bpp = lcd_bpp;
|
||||
|
||||
/*[11:0]垂直同步信号宽度tvp,[27:16]水平同步信号宽度thp*/
|
||||
lcdif->LTDC_SSCR = (dt->vsync_len.typ << 0) | (dt->hsync_len.typ << 16);
|
||||
|
||||
/*清空LTDC_BPCR寄存器*/
|
||||
lcdif->LTDC_BPCR = 0 ;
|
||||
/*[11:0] VSYNC宽度tvp + 上黑框tvb - 1*/
|
||||
lcdif->LTDC_BPCR |= (dt->vsync_len.typ + dt->vback_porch.typ - 1) << 0 ;
|
||||
/*[27:16]HSYNC宽度thp + 左黑框thb - 1*/
|
||||
lcdif->LTDC_BPCR |= (dt->hsync_len.typ + dt->hback_porch.typ - 1) << 16;
|
||||
|
||||
/*清空LTDC_AWCR寄存器*/
|
||||
lcdif->LTDC_AWCR = 0 ;
|
||||
/*[11:0] VSYNC宽度tvp + 上黑框tvb + 垂直有效高度yres - 1*/
|
||||
lcdif->LTDC_AWCR |= (dt->vsync_len.typ + dt->vback_porch.typ + dt->vactive.typ - 1) << 0;
|
||||
/*[27:16] HSYNC宽度thp + 左黑框thb + 水平有效高度xres - 1*/
|
||||
lcdif->LTDC_AWCR |= (dt->hsync_len.typ + dt->hback_porch.typ + dt->hactive.typ - 1) << 16;
|
||||
|
||||
/*清空LTDC_TWCR寄存器*/
|
||||
lcdif->LTDC_TWCR = 0;
|
||||
/*[11:0] VSYNC宽度tvp + 上黑框tvb + 垂直有效高度yres + 下黑框tvf - 1 , 即垂直方向上的总周期*/
|
||||
lcdif->LTDC_TWCR |= (dt->vsync_len.typ + dt->vback_porch.typ + dt->vactive.typ + dt->vfront_porch.typ - 1) << 0;
|
||||
/*[27:16] HSYNC宽度thp + 左黑框thb + 垂直有效高度xres + 右黑框thf - 1 , 即水平方向上的总周期*/
|
||||
lcdif->LTDC_TWCR |= (dt->hsync_len.typ + dt->hback_porch.typ + dt->hactive.typ + dt->hfront_porch.typ - 1) << 16;
|
||||
|
||||
if (dt->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE)
|
||||
pol_vclk = 1;
|
||||
if (dt->flags & DISPLAY_FLAGS_DE_HIGH)
|
||||
pol_de = 1;
|
||||
if (dt->flags & DISPLAY_FLAGS_VSYNC_HIGH)
|
||||
pol_vsync = 1;
|
||||
if (dt->flags & DISPLAY_FLAGS_HSYNC_HIGH)
|
||||
pol_hsync = 1;
|
||||
|
||||
/*清空LTDC_GCR寄存器*/
|
||||
lcdif->LTDC_GCR &= ~(0xF << 28);
|
||||
/* 1 : DOTCLK下降沿有效 ,根据屏幕配置文件将其设置为1 */
|
||||
lcdif->LTDC_GCR |= pol_vclk << 28;
|
||||
/* 1 : ENABLE信号高电平有效,根据屏幕配置文件将其设置为1 */
|
||||
lcdif->LTDC_GCR |= pol_de << 29;
|
||||
/* 0 : VSYNC低电平有效 ,根据屏幕配置文件将其设置为0 */
|
||||
lcdif->LTDC_GCR |= pol_vsync << 30 ;
|
||||
/* 0 : HSYNC低电平有效 , 根据屏幕配置文件将其设置为0 */
|
||||
lcdif->LTDC_GCR |= pol_hsync << 31 ;
|
||||
|
||||
/*layer 1的相关设置如下*/
|
||||
lcdif->LTDC_L1WHPCR |= (dt->hsync_len.typ + dt->hback_porch.typ + dt->hactive.typ - 1) << 16 | (dt->hsync_len.typ + dt->hback_porch.typ ) ;
|
||||
|
||||
lcdif->LTDC_L1WVPCR |= (dt->vsync_len.typ + dt->vback_porch.typ + dt->vactive.typ - 1) << 16 | (dt->vsync_len.typ + dt->vback_porch.typ ) ;
|
||||
|
||||
lcdif->LTDC_L1CFBLR = (dt->hactive.typ * (fb_bpp>>3) + 7) | (dt->hactive.typ * (fb_bpp>>3))<< 16;
|
||||
|
||||
lcdif->LTDC_L1CFBLNR = dt->vactive.typ;/*显存总共的行数*/
|
||||
|
||||
/*透明度填充值,当选的bpp格式是ARGB8888,ARGB1555等会使用到,如选的是RGB565,RBG888等者不设置也可以*/
|
||||
lcdif->LTDC_L1CACR = 0xff;
|
||||
|
||||
/*
|
||||
*BC = BF1 x C + BF2 x Cs
|
||||
*BF1为LTDC_L1BFCR设置的[10:8]值,设置为100:constant alpha即LTDC_L1CACR设置的值0xff,表示完全不透明
|
||||
*BF2为LTDC_L1BFCR设置的[2:0]值,设置为101:constant alpha即LTDC_L1CACR设置的值0xff,表示完全不透明
|
||||
*C为当前层的颜色,
|
||||
*Cs为背景色,不设置,默认值为0,即黑色
|
||||
*LTDC_L1BFCR寄存器也是针对有透明度的像素格式而设置,如用RGB565等也可不设置
|
||||
*/
|
||||
lcdif->LTDC_L1BFCR = (4<<8) | (5<<0);
|
||||
|
||||
/*当bpp为16时,数据格式为RGB565 , 当bpp为32时,数据格式为ARGB8888*/
|
||||
switch(fb_bpp)
|
||||
{
|
||||
case 16:{
|
||||
bpp_mode = 0x2;break;}
|
||||
case 32:{
|
||||
bpp_mode = 0x0;break;}
|
||||
default:{
|
||||
bpp_mode = 0x0;break;}
|
||||
}
|
||||
|
||||
lcdif->LTDC_L1PFCR = 0 ;
|
||||
lcdif->LTDC_L1PFCR |= bpp_mode; /*设置像素格式*/
|
||||
|
||||
lcdif->LTDC_L1CFBAR = fb_phy; /*设置显存地址*/
|
||||
|
||||
lcdif->LTDC_L1CR |= 0x1;/*1 layer 使能*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* 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 mylcd_setcolreg(unsigned regno,
|
||||
unsigned red, unsigned green, unsigned blue,
|
||||
unsigned transp, struct fb_info *info)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
/* dprintk("setcol: regno=%d, rgb=%d,%d,%d\n",
|
||||
regno, red, green, blue); */
|
||||
|
||||
switch (info->fix.visual) {
|
||||
case FB_VISUAL_TRUECOLOR:
|
||||
/* true-colour, use pseudo-palette */
|
||||
|
||||
if (regno < 16) {
|
||||
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;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return 1; /* unknown type */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct fb_ops myfb_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.fb_setcolreg = mylcd_setcolreg,
|
||||
.fb_fillrect = cfb_fillrect,
|
||||
.fb_copyarea = cfb_copyarea,
|
||||
.fb_imageblit = cfb_imageblit,
|
||||
};
|
||||
|
||||
static int mylcd_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *display_np;
|
||||
dma_addr_t phy_addr;
|
||||
int ret;
|
||||
int width;
|
||||
int bits_per_pixel;
|
||||
struct display_timings *timings = NULL;
|
||||
struct display_timing *timing;
|
||||
struct resource *res;
|
||||
|
||||
display_np = of_parse_phandle(pdev->dev.of_node, "display", 0);
|
||||
|
||||
/* get common info */
|
||||
ret = of_property_read_u32(display_np, "bus-width", &width);
|
||||
ret = of_property_read_u32(display_np, "bits-per-pixel",
|
||||
&bits_per_pixel);
|
||||
|
||||
/* get timming */
|
||||
timings = of_get_display_timings(display_np);
|
||||
timing = timings->timings[timings->native_mode];
|
||||
|
||||
/* get gpio from device tree */
|
||||
bl_gpio = gpiod_get(&pdev->dev, "backlight", 0);
|
||||
|
||||
/* config bl_gpio as output */
|
||||
gpiod_direction_output(bl_gpio, 1);
|
||||
|
||||
/* set val: gpiod_set_value(bl_gpio, status); */
|
||||
|
||||
/* get clk from device tree */
|
||||
pixel_clk = devm_clk_get(&pdev->dev, "lcd");
|
||||
|
||||
/* set rate */
|
||||
clk_set_rate(pixel_clk, timing->pixelclock.typ);
|
||||
|
||||
/* enable clk */
|
||||
clk_prepare_enable(pixel_clk);
|
||||
|
||||
/* 1.1 分配fb_info */
|
||||
myfb_info = framebuffer_alloc(0, NULL);
|
||||
|
||||
/* 1.2 设置fb_info */
|
||||
/* a. var : LCD分辨率、颜色格式 */
|
||||
myfb_info->var.xres_virtual = myfb_info->var.xres = timing->hactive.typ;
|
||||
myfb_info->var.yres_virtual = myfb_info->var.yres = timing->vactive.typ;
|
||||
|
||||
myfb_info->var.bits_per_pixel = bits_per_pixel; /* rgb565 */
|
||||
if (bits_per_pixel == 16)
|
||||
{
|
||||
myfb_info->var.red.offset = 11;
|
||||
myfb_info->var.red.length = 5;
|
||||
|
||||
myfb_info->var.green.offset = 5;
|
||||
myfb_info->var.green.length = 6;
|
||||
|
||||
myfb_info->var.blue.offset = 0;
|
||||
myfb_info->var.blue.length = 5;
|
||||
}
|
||||
else if (bits_per_pixel == 24 || bits_per_pixel == 32)
|
||||
{
|
||||
myfb_info->var.red.offset = 16;
|
||||
myfb_info->var.red.length = 8;
|
||||
|
||||
myfb_info->var.green.offset = 8;
|
||||
myfb_info->var.green.length = 8;
|
||||
|
||||
myfb_info->var.blue.offset = 0;
|
||||
myfb_info->var.blue.length = 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* b. fix */
|
||||
strcpy(myfb_info->fix.id, "100ask_lcd");
|
||||
myfb_info->fix.smem_len = myfb_info->var.xres * myfb_info->var.yres * myfb_info->var.bits_per_pixel / 8;
|
||||
if (myfb_info->var.bits_per_pixel == 24)
|
||||
myfb_info->fix.smem_len = myfb_info->var.xres * myfb_info->var.yres * 4;
|
||||
|
||||
|
||||
/* fb的虚拟地址 */
|
||||
myfb_info->screen_base = dma_alloc_wc(NULL, myfb_info->fix.smem_len, &phy_addr,
|
||||
GFP_KERNEL);
|
||||
myfb_info->fix.smem_start = phy_addr; /* fb的物理地址 */
|
||||
|
||||
myfb_info->fix.type = FB_TYPE_PACKED_PIXELS;
|
||||
myfb_info->fix.visual = FB_VISUAL_TRUECOLOR;
|
||||
|
||||
myfb_info->fix.line_length = myfb_info->var.xres * myfb_info->var.bits_per_pixel / 8;
|
||||
if (myfb_info->var.bits_per_pixel == 24)
|
||||
myfb_info->fix.line_length = myfb_info->var.xres * 4;
|
||||
|
||||
|
||||
/* c. fbops */
|
||||
myfb_info->fbops = &myfb_ops;
|
||||
myfb_info->pseudo_palette = pseudo_palette;
|
||||
|
||||
|
||||
/* 1.3 注册fb_info */
|
||||
register_framebuffer(myfb_info);
|
||||
|
||||
/* 1.4 硬件操作 */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
lcdif = devm_ioremap_resource(&pdev->dev, res);
|
||||
//lcdif = ioremap(0x5a001000, sizeof(struct stm32mp157_lcdif));
|
||||
lcd_controller_init(lcdif, timing, width, bits_per_pixel, phy_addr);
|
||||
|
||||
/* enable lcd controller */
|
||||
Stm32mp157_lcd_controller_enable(lcdif);
|
||||
|
||||
/* enable backlight */
|
||||
gpiod_set_value(bl_gpio, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int mylcd_remove(struct platform_device *pdev)
|
||||
{
|
||||
/* 反过来操作 */
|
||||
/* 2.1 反注册fb_info */
|
||||
unregister_framebuffer(myfb_info);
|
||||
|
||||
/* 2.2 释放fb_info */
|
||||
framebuffer_release(myfb_info);
|
||||
|
||||
//iounmap(lcdif);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const struct of_device_id mylcd_of_match[] = {
|
||||
{ .compatible = "100ask,lcd_drv", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, simplefb_of_match);
|
||||
|
||||
static struct platform_driver mylcd_driver = {
|
||||
.driver = {
|
||||
.name = "mylcd",
|
||||
.of_match_table = mylcd_of_match,
|
||||
},
|
||||
.probe = mylcd_probe,
|
||||
.remove = mylcd_remove,
|
||||
};
|
||||
|
||||
static int __init lcd_drv_init(void)
|
||||
{
|
||||
int ret;
|
||||
struct device_node *np;
|
||||
|
||||
ret = platform_driver_register(&mylcd_driver);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 2. 出口 */
|
||||
static void __exit lcd_drv_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&mylcd_driver);
|
||||
}
|
||||
|
||||
|
||||
module_init(lcd_drv_init);
|
||||
module_exit(lcd_drv_exit);
|
||||
|
||||
MODULE_AUTHOR("www.100ask.net");
|
||||
MODULE_DESCRIPTION("Framebuffer driver for the linux");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
|
||||
/*
|
||||
* Copyright (C) 100ASK 2020 - All Rights Reserved
|
||||
* Author: 100ask
|
||||
* support: weidongshan@qq.com
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
|
||||
#include "stm32mp157c-100ask-512d-v1.dts"
|
||||
|
||||
/ {
|
||||
model = "100ASK YA157C v2 www.100ask.com";
|
||||
compatible = "st,stm32mp157c-100ask-512d-v1", "st,stm32mp157";
|
||||
|
||||
|
||||
framebuffer-mylcd {
|
||||
compatible = "100ask,lcd_drv";
|
||||
reg = <0x5a001000 0x400>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <<dc_pins_a>;
|
||||
backlight-gpios = <&gpioe 11 GPIO_ACTIVE_HIGH>;
|
||||
clocks = <&rcc LTDC_PX>;
|
||||
clock-names = "lcd";
|
||||
|
||||
display = <&display0>;
|
||||
|
||||
display0: display {
|
||||
bits-per-pixel = <24>;
|
||||
bus-width = <24>;
|
||||
|
||||
display-timings {
|
||||
native-mode = <&timing0>;
|
||||
|
||||
timing0: timing0_1024x600 {
|
||||
clock-frequency = <50000000>;
|
||||
hactive = <1024>;
|
||||
vactive = <600>;
|
||||
hfront-porch = <160>;
|
||||
hback-porch = <140>;
|
||||
hsync-len = <20>;
|
||||
vback-porch = <20>;
|
||||
vfront-porch = <12>;
|
||||
vsync-len = <3>;
|
||||
|
||||
hsync-active = <0>;
|
||||
vsync-active = <0>;
|
||||
de-active = <1>;
|
||||
pixelclk-active = <0>;
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/*LCD Panel*/
|
||||
panel {
|
||||
compatible = "myir,070tft";
|
||||
interrupts = <11 IRQ_TYPE_EDGE_FALLING>;
|
||||
interrupt-parent = <&gpioe>;
|
||||
pinctrl-names = "default", "sleep";
|
||||
pinctrl-0 = <<dc_pins_a>;
|
||||
pinctrl-1 = <<dc_pins_sleep_a>;
|
||||
//reset-gpios = <&gpioe 12 GPIO_ACTIVE_LOW>;
|
||||
|
||||
backlight = <&panel_backlight>;
|
||||
status = "okay";
|
||||
|
||||
port {
|
||||
panel_in: endpoint {
|
||||
remote-endpoint = <<dc_ep0_out>;
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&spi5 {
|
||||
pinctrl-names = "default", "sleep";
|
||||
pinctrl-0 = <&spi5_pins_a>;
|
||||
pinctrl-1 = <&spi5_sleep_pins_a>;
|
||||
status = "okay";
|
||||
cs-gpios = <&gpioh 5 GPIO_ACTIVE_LOW>;
|
||||
spidev: icm20608@0{
|
||||
compatible = "invensense,icm20608";
|
||||
interrupts = <0 IRQ_TYPE_EDGE_FALLING>;
|
||||
interrupt-parent = <&gpioz>;
|
||||
spi-max-frequency = <8000000>;
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
/* test HDMI*/
|
||||
<dc {
|
||||
status = "okay";
|
||||
port {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
ltdc_ep1_out: endpoint@1 {
|
||||
reg = <1>;
|
||||
remote-endpoint = <&sii9022_in>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/*HDMI*/
|
||||
&i2c4 {
|
||||
clock-frequency = <100000>;
|
||||
hdmi-transmitter@40 { // use a dummy device
|
||||
compatible = "sil,sii9022";
|
||||
reg = <0x40>;
|
||||
reset-gpios = <&gpiob 10 GPIO_ACTIVE_LOW>;
|
||||
interrupts = <13 IRQ_TYPE_EDGE_FALLING>;
|
||||
interrupt-parent = <&gpiob>;
|
||||
//pinctrl-names = "default", "sleep";
|
||||
//pinctrl-0 = <<dc_pins_a>;
|
||||
//pinctrl-1 = <<dc_pins_sleep_a>;
|
||||
status = "disabled";
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
sii9022_in: endpoint {
|
||||
remote-endpoint = <<dc_ep1_out>;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
#if 1
|
||||
sii902x: sii902x@39 {
|
||||
compatible = "SiI,sii902x";
|
||||
reset-gpios = <&gpiob 10 GPIO_ACTIVE_LOW>;
|
||||
//pinctrl-names = "default";
|
||||
//pinctrl-0 = <&pinctrl_sii902x>;
|
||||
interrupts = <13 IRQ_TYPE_EDGE_FALLING>;
|
||||
interrupt-parent = <&gpiob>;
|
||||
mode_str ="1280x720M@60";
|
||||
bits-per-pixel = <16>;
|
||||
reg = <0x39>;
|
||||
status = "okay";
|
||||
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
&i2s2 {
|
||||
status = "disable";
|
||||
|
||||
};
|
||||
|
||||
/*test LCD*/
|
||||
<dc {
|
||||
status = "okay";
|
||||
|
||||
port {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
ltdc_ep0_out: endpoint@0 {
|
||||
reg = <0>;
|
||||
remote-endpoint = <&panel_in>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR
|
||||
# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量:
|
||||
# 2.1 ARCH, 比如: export ARCH=arm64
|
||||
# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu-
|
||||
# 2.3 PATH, 比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin
|
||||
# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同,
|
||||
# 请参考各开发板的高级用户使用手册
|
||||
|
||||
KERN_DIR = /home/book/100ask_stm32mp157_pro-sdk/Linux-5.4
|
||||
|
||||
all:
|
||||
make -C $(KERN_DIR) M=`pwd` modules
|
||||
|
||||
clean:
|
||||
make -C $(KERN_DIR) M=`pwd` modules clean
|
||||
rm -rf modules.order
|
||||
|
||||
obj-m += lcd_drv.o
|
||||
421
STM32MP157/source/A7/03_LCD/11_lcd_drv_stm32mp157_ok/lcd_drv.c
Normal file
@@ -0,0 +1,421 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/err.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/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/io.h>
|
||||
#include <video/display_timing.h>
|
||||
#include <video/of_display_timing.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
#include <asm/div64.h>
|
||||
|
||||
#include <asm/mach/map.h>
|
||||
|
||||
struct stm32mp157_lcdif {
|
||||
volatile unsigned int LTDC_IDR;
|
||||
volatile unsigned int LTDC_LCR;
|
||||
volatile unsigned int LTDC_SSCR;
|
||||
volatile unsigned int LTDC_BPCR;
|
||||
volatile unsigned int LTDC_AWCR;
|
||||
volatile unsigned int LTDC_TWCR;
|
||||
volatile unsigned int LTDC_GCR;
|
||||
volatile unsigned int LTDC_GC1R;
|
||||
volatile unsigned int LTDC_GC2R;
|
||||
volatile unsigned int LTDC_SRCR;
|
||||
unsigned char RESERVED_0[4];
|
||||
volatile unsigned int LTDC_BCCR;
|
||||
unsigned char RESERVED_1[4];
|
||||
volatile unsigned int LTDC_IER;
|
||||
volatile unsigned int LTDC_ISR;
|
||||
volatile unsigned int LTDC_ICR;
|
||||
volatile unsigned int LTDC_LIPCR;
|
||||
volatile unsigned int LTDC_CPSR;
|
||||
volatile unsigned int LTDC_CDSR;
|
||||
unsigned char RESERVED_2[56];
|
||||
volatile unsigned int LTDC_L1CR;
|
||||
volatile unsigned int LTDC_L1WHPCR;
|
||||
volatile unsigned int LTDC_L1WVPCR;
|
||||
volatile unsigned int LTDC_L1CKCR;
|
||||
volatile unsigned int LTDC_L1PFCR;
|
||||
volatile unsigned int LTDC_L1CACR;
|
||||
volatile unsigned int LTDC_L1DCCR;
|
||||
volatile unsigned int LTDC_L1BFCR;
|
||||
unsigned char RESERVED_3[8];
|
||||
volatile unsigned int LTDC_L1CFBAR;
|
||||
volatile unsigned int LTDC_L1CFBLR;
|
||||
volatile unsigned int LTDC_L1CFBLNR;
|
||||
unsigned char RESERVED_4[12];
|
||||
volatile unsigned int LTDC_L1CLUTWR;
|
||||
unsigned char RESERVED_5[60];
|
||||
volatile unsigned int LTDC_L2CR;
|
||||
volatile unsigned int LTDC_L2WHPCR;
|
||||
volatile unsigned int LTDC_L2WVPCR;
|
||||
volatile unsigned int LTDC_L2CKCR;
|
||||
volatile unsigned int LTDC_L2PFCR;
|
||||
volatile unsigned int LTDC_L2CACR;
|
||||
volatile unsigned int LTDC_L2DCCR;
|
||||
volatile unsigned int LTDC_L2BFCR;
|
||||
unsigned char RESERVED_6[8];
|
||||
volatile unsigned int LTDC_L2CFBAR;
|
||||
volatile unsigned int LTDC_L2CFBLR;
|
||||
volatile unsigned int LTDC_L2CFBLNR;
|
||||
unsigned char RESERVED_7[12];
|
||||
volatile unsigned int LTDC_L2CLUTWR;
|
||||
};
|
||||
|
||||
|
||||
struct lcd_regs {
|
||||
volatile unsigned int fb_base_phys;
|
||||
volatile unsigned int fb_xres;
|
||||
volatile unsigned int fb_yres;
|
||||
volatile unsigned int fb_bpp;
|
||||
};
|
||||
|
||||
static struct fb_info *myfb_info;
|
||||
|
||||
static unsigned int pseudo_palette[16];
|
||||
|
||||
static struct gpio_desc *bl_gpio;
|
||||
|
||||
static struct clk *pixel_clk;
|
||||
|
||||
struct stm32mp157_lcdif *lcdif;
|
||||
|
||||
static void Stm32mp157_lcd_controller_enable(struct stm32mp157_lcdif *lcdif)
|
||||
{
|
||||
lcdif->LTDC_SRCR |= 1; /*加载LAYER的参数*/
|
||||
lcdif->LTDC_GCR |= 1<<0; /* 使能STM32MP157的LCD控制器 */
|
||||
}
|
||||
|
||||
static int lcd_controller_init(struct stm32mp157_lcdif *lcdif, struct display_timing *dt, int lcd_bpp, int fb_bpp, unsigned int fb_phy)
|
||||
{
|
||||
int bpp_mode;
|
||||
int pol_vclk = 0;
|
||||
int pol_vsync = 0;
|
||||
int pol_hsync = 0;
|
||||
int pol_de = 0;
|
||||
|
||||
lcd_bpp = lcd_bpp;
|
||||
|
||||
/*[11:0]垂直同步信号宽度tvp,[27:16]水平同步信号宽度thp*/
|
||||
lcdif->LTDC_SSCR = (dt->vsync_len.typ << 0) | (dt->hsync_len.typ << 16);
|
||||
|
||||
/*清空LTDC_BPCR寄存器*/
|
||||
lcdif->LTDC_BPCR = 0 ;
|
||||
/*[11:0] VSYNC宽度tvp + 上黑框tvb - 1*/
|
||||
lcdif->LTDC_BPCR |= (dt->vsync_len.typ + dt->vback_porch.typ - 1) << 0 ;
|
||||
/*[27:16]HSYNC宽度thp + 左黑框thb - 1*/
|
||||
lcdif->LTDC_BPCR |= (dt->hsync_len.typ + dt->hback_porch.typ - 1) << 16;
|
||||
|
||||
/*清空LTDC_AWCR寄存器*/
|
||||
lcdif->LTDC_AWCR = 0 ;
|
||||
/*[11:0] VSYNC宽度tvp + 上黑框tvb + 垂直有效高度yres - 1*/
|
||||
lcdif->LTDC_AWCR |= (dt->vsync_len.typ + dt->vback_porch.typ + dt->vactive.typ - 1) << 0;
|
||||
/*[27:16] HSYNC宽度thp + 左黑框thb + 水平有效高度xres - 1*/
|
||||
lcdif->LTDC_AWCR |= (dt->hsync_len.typ + dt->hback_porch.typ + dt->hactive.typ - 1) << 16;
|
||||
|
||||
/*清空LTDC_TWCR寄存器*/
|
||||
lcdif->LTDC_TWCR = 0;
|
||||
/*[11:0] VSYNC宽度tvp + 上黑框tvb + 垂直有效高度yres + 下黑框tvf - 1 , 即垂直方向上的总周期*/
|
||||
lcdif->LTDC_TWCR |= (dt->vsync_len.typ + dt->vback_porch.typ + dt->vactive.typ + dt->vfront_porch.typ - 1) << 0;
|
||||
/*[27:16] HSYNC宽度thp + 左黑框thb + 垂直有效高度xres + 右黑框thf - 1 , 即水平方向上的总周期*/
|
||||
lcdif->LTDC_TWCR |= (dt->hsync_len.typ + dt->hback_porch.typ + dt->hactive.typ + dt->hfront_porch.typ - 1) << 16;
|
||||
|
||||
if (dt->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE)
|
||||
pol_vclk = 1;
|
||||
if (dt->flags & DISPLAY_FLAGS_DE_HIGH)
|
||||
pol_de = 1;
|
||||
if (dt->flags & DISPLAY_FLAGS_VSYNC_HIGH)
|
||||
pol_vsync = 1;
|
||||
if (dt->flags & DISPLAY_FLAGS_HSYNC_HIGH)
|
||||
pol_hsync = 1;
|
||||
|
||||
/*清空LTDC_GCR寄存器*/
|
||||
lcdif->LTDC_GCR &= ~(0xF << 28);
|
||||
/* 1 : DOTCLK下降沿有效 ,根据屏幕配置文件将其设置为1 */
|
||||
lcdif->LTDC_GCR |= pol_vclk << 28;
|
||||
/* 1 : ENABLE信号高电平有效,根据屏幕配置文件将其设置为1 */
|
||||
lcdif->LTDC_GCR |= pol_de << 29;
|
||||
/* 0 : VSYNC低电平有效 ,根据屏幕配置文件将其设置为0 */
|
||||
lcdif->LTDC_GCR |= pol_vsync << 30 ;
|
||||
/* 0 : HSYNC低电平有效 , 根据屏幕配置文件将其设置为0 */
|
||||
lcdif->LTDC_GCR |= pol_hsync << 31 ;
|
||||
|
||||
/*layer 1的相关设置如下*/
|
||||
lcdif->LTDC_L1WHPCR |= (dt->hsync_len.typ + dt->hback_porch.typ + dt->hactive.typ - 1) << 16 | (dt->hsync_len.typ + dt->hback_porch.typ ) ;
|
||||
|
||||
lcdif->LTDC_L1WVPCR |= (dt->vsync_len.typ + dt->vback_porch.typ + dt->vactive.typ - 1) << 16 | (dt->vsync_len.typ + dt->vback_porch.typ ) ;
|
||||
|
||||
lcdif->LTDC_L1CFBLR = (dt->hactive.typ * (fb_bpp>>3) + 7) | (dt->hactive.typ * (fb_bpp>>3))<< 16;
|
||||
|
||||
lcdif->LTDC_L1CFBLNR = dt->vactive.typ;/*显存总共的行数*/
|
||||
|
||||
/*透明度填充值,当选的bpp格式是ARGB8888,ARGB1555等会使用到,如选的是RGB565,RBG888等者不设置也可以*/
|
||||
lcdif->LTDC_L1CACR = 0xff;
|
||||
|
||||
/*
|
||||
*BC = BF1 x C + BF2 x Cs
|
||||
*BF1为LTDC_L1BFCR设置的[10:8]值,设置为100:constant alpha即LTDC_L1CACR设置的值0xff,表示完全不透明
|
||||
*BF2为LTDC_L1BFCR设置的[2:0]值,设置为101:constant alpha即LTDC_L1CACR设置的值0xff,表示完全不透明
|
||||
*C为当前层的颜色,
|
||||
*Cs为背景色,不设置,默认值为0,即黑色
|
||||
*LTDC_L1BFCR寄存器也是针对有透明度的像素格式而设置,如用RGB565等也可不设置
|
||||
*/
|
||||
lcdif->LTDC_L1BFCR = (4<<8) | (5<<0);
|
||||
|
||||
/*当bpp为16时,数据格式为RGB565 , 当bpp为32时,数据格式为ARGB8888*/
|
||||
switch(fb_bpp)
|
||||
{
|
||||
case 16:{
|
||||
bpp_mode = 0x2;break;}
|
||||
case 32:{
|
||||
bpp_mode = 0x0;break;}
|
||||
default:{
|
||||
bpp_mode = 0x0;break;}
|
||||
}
|
||||
|
||||
lcdif->LTDC_L1PFCR = 0 ;
|
||||
lcdif->LTDC_L1PFCR |= bpp_mode; /*设置像素格式*/
|
||||
|
||||
lcdif->LTDC_L1CFBAR = fb_phy; /*设置显存地址*/
|
||||
|
||||
lcdif->LTDC_L1CR |= 0x1;/*1 layer 使能*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* 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 mylcd_setcolreg(unsigned regno,
|
||||
unsigned red, unsigned green, unsigned blue,
|
||||
unsigned transp, struct fb_info *info)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
/* dprintk("setcol: regno=%d, rgb=%d,%d,%d\n",
|
||||
regno, red, green, blue); */
|
||||
|
||||
switch (info->fix.visual) {
|
||||
case FB_VISUAL_TRUECOLOR:
|
||||
/* true-colour, use pseudo-palette */
|
||||
|
||||
if (regno < 16) {
|
||||
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;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return 1; /* unknown type */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct fb_ops myfb_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.fb_setcolreg = mylcd_setcolreg,
|
||||
.fb_fillrect = cfb_fillrect,
|
||||
.fb_copyarea = cfb_copyarea,
|
||||
.fb_imageblit = cfb_imageblit,
|
||||
};
|
||||
|
||||
static int mylcd_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *display_np;
|
||||
dma_addr_t phy_addr;
|
||||
int ret;
|
||||
int width;
|
||||
int bits_per_pixel;
|
||||
struct display_timings *timings = NULL;
|
||||
struct display_timing *timing;
|
||||
struct resource *res;
|
||||
|
||||
display_np = of_parse_phandle(pdev->dev.of_node, "display", 0);
|
||||
|
||||
/* get common info */
|
||||
ret = of_property_read_u32(display_np, "bus-width", &width);
|
||||
ret = of_property_read_u32(display_np, "bits-per-pixel",
|
||||
&bits_per_pixel);
|
||||
|
||||
/* get timming */
|
||||
timings = of_get_display_timings(display_np);
|
||||
timing = timings->timings[timings->native_mode];
|
||||
|
||||
/* get gpio from device tree */
|
||||
bl_gpio = gpiod_get(&pdev->dev, "backlight", 0);
|
||||
|
||||
/* config bl_gpio as output */
|
||||
gpiod_direction_output(bl_gpio, 1);
|
||||
|
||||
/* set val: gpiod_set_value(bl_gpio, status); */
|
||||
|
||||
/* get clk from device tree */
|
||||
pixel_clk = devm_clk_get(&pdev->dev, "lcd");
|
||||
|
||||
/* set rate */
|
||||
clk_set_rate(pixel_clk, timing->pixelclock.typ);
|
||||
|
||||
/* enable clk */
|
||||
clk_prepare_enable(pixel_clk);
|
||||
|
||||
/* 1.1 分配fb_info */
|
||||
myfb_info = framebuffer_alloc(0, NULL);
|
||||
|
||||
/* 1.2 设置fb_info */
|
||||
/* a. var : LCD分辨率、颜色格式 */
|
||||
myfb_info->var.xres_virtual = myfb_info->var.xres = timing->hactive.typ;
|
||||
myfb_info->var.yres_virtual = myfb_info->var.yres = timing->vactive.typ;
|
||||
|
||||
myfb_info->var.bits_per_pixel = bits_per_pixel; /* rgb565 */
|
||||
if (bits_per_pixel == 16)
|
||||
{
|
||||
myfb_info->var.red.offset = 11;
|
||||
myfb_info->var.red.length = 5;
|
||||
|
||||
myfb_info->var.green.offset = 5;
|
||||
myfb_info->var.green.length = 6;
|
||||
|
||||
myfb_info->var.blue.offset = 0;
|
||||
myfb_info->var.blue.length = 5;
|
||||
}
|
||||
else if (bits_per_pixel == 24 || bits_per_pixel == 32)
|
||||
{
|
||||
myfb_info->var.red.offset = 16;
|
||||
myfb_info->var.red.length = 8;
|
||||
|
||||
myfb_info->var.green.offset = 8;
|
||||
myfb_info->var.green.length = 8;
|
||||
|
||||
myfb_info->var.blue.offset = 0;
|
||||
myfb_info->var.blue.length = 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* b. fix */
|
||||
strcpy(myfb_info->fix.id, "100ask_lcd");
|
||||
myfb_info->fix.smem_len = myfb_info->var.xres * myfb_info->var.yres * myfb_info->var.bits_per_pixel / 8;
|
||||
if (myfb_info->var.bits_per_pixel == 24)
|
||||
myfb_info->fix.smem_len = myfb_info->var.xres * myfb_info->var.yres * 4;
|
||||
|
||||
|
||||
/* fb的虚拟地址 */
|
||||
myfb_info->screen_base = dma_alloc_wc(&pdev->dev, myfb_info->fix.smem_len, &phy_addr,
|
||||
GFP_KERNEL);
|
||||
myfb_info->fix.smem_start = phy_addr; /* fb的物理地址 */
|
||||
|
||||
myfb_info->fix.type = FB_TYPE_PACKED_PIXELS;
|
||||
myfb_info->fix.visual = FB_VISUAL_TRUECOLOR;
|
||||
|
||||
myfb_info->fix.line_length = myfb_info->var.xres * myfb_info->var.bits_per_pixel / 8;
|
||||
if (myfb_info->var.bits_per_pixel == 24)
|
||||
myfb_info->fix.line_length = myfb_info->var.xres * 4;
|
||||
|
||||
|
||||
/* c. fbops */
|
||||
myfb_info->fbops = &myfb_ops;
|
||||
myfb_info->pseudo_palette = pseudo_palette;
|
||||
|
||||
|
||||
/* 1.3 注册fb_info */
|
||||
register_framebuffer(myfb_info);
|
||||
|
||||
/* 1.4 硬件操作 */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
lcdif = devm_ioremap_resource(&pdev->dev, res);
|
||||
//lcdif = ioremap(0x5a001000, sizeof(struct stm32mp157_lcdif));
|
||||
lcd_controller_init(lcdif, timing, width, bits_per_pixel, phy_addr);
|
||||
|
||||
/* enable lcd controller */
|
||||
Stm32mp157_lcd_controller_enable(lcdif);
|
||||
|
||||
/* enable backlight */
|
||||
gpiod_set_value(bl_gpio, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int mylcd_remove(struct platform_device *pdev)
|
||||
{
|
||||
/* 反过来操作 */
|
||||
/* 2.1 反注册fb_info */
|
||||
unregister_framebuffer(myfb_info);
|
||||
|
||||
/* 2.2 释放fb_info */
|
||||
framebuffer_release(myfb_info);
|
||||
|
||||
//iounmap(lcdif);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const struct of_device_id mylcd_of_match[] = {
|
||||
{ .compatible = "100ask,lcd_drv", },
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct platform_driver mylcd_driver = {
|
||||
.driver = {
|
||||
.name = "mylcd",
|
||||
.of_match_table = mylcd_of_match,
|
||||
},
|
||||
.probe = mylcd_probe,
|
||||
.remove = mylcd_remove,
|
||||
};
|
||||
|
||||
static int __init lcd_drv_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = platform_driver_register(&mylcd_driver);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 2. 出口 */
|
||||
static void __exit lcd_drv_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&mylcd_driver);
|
||||
}
|
||||
|
||||
|
||||
module_init(lcd_drv_init);
|
||||
module_exit(lcd_drv_exit);
|
||||
|
||||
MODULE_AUTHOR("www.100ask.net");
|
||||
MODULE_DESCRIPTION("Framebuffer driver for the linux");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
|
||||
/*
|
||||
* Copyright (C) 100ASK 2020 - All Rights Reserved
|
||||
* Author: 100ask
|
||||
* support: weidongshan@qq.com
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
|
||||
#include "stm32mp157c-100ask-512d-v1.dts"
|
||||
|
||||
/ {
|
||||
model = "100ASK YA157C v2 www.100ask.com";
|
||||
compatible = "st,stm32mp157c-100ask-512d-v1", "st,stm32mp157";
|
||||
/*LCD Panel*/
|
||||
panel {
|
||||
compatible = "myir,070tft";
|
||||
interrupts = <11 IRQ_TYPE_EDGE_FALLING>;
|
||||
interrupt-parent = <&gpioe>;
|
||||
pinctrl-names = "default", "sleep";
|
||||
pinctrl-0 = <<dc_pins_a>;
|
||||
pinctrl-1 = <<dc_pins_sleep_a>;
|
||||
//reset-gpios = <&gpioe 12 GPIO_ACTIVE_LOW>;
|
||||
|
||||
backlight = <&panel_backlight>;
|
||||
status = "okay";
|
||||
|
||||
port {
|
||||
panel_in: endpoint {
|
||||
remote-endpoint = <<dc_ep0_out>;
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&spi5 {
|
||||
pinctrl-names = "default", "sleep";
|
||||
pinctrl-0 = <&spi5_pins_a>;
|
||||
pinctrl-1 = <&spi5_sleep_pins_a>;
|
||||
status = "okay";
|
||||
cs-gpios = <&gpioh 5 GPIO_ACTIVE_LOW>;
|
||||
spidev: icm20608@0{
|
||||
compatible = "invensense,icm20608";
|
||||
interrupts = <0 IRQ_TYPE_EDGE_FALLING>;
|
||||
interrupt-parent = <&gpioz>;
|
||||
spi-max-frequency = <8000000>;
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
/* test HDMI*/
|
||||
<dc {
|
||||
status = "okay";
|
||||
port {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
ltdc_ep1_out: endpoint@1 {
|
||||
reg = <1>;
|
||||
remote-endpoint = <&sii9022_in>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/*HDMI*/
|
||||
&i2c4 {
|
||||
clock-frequency = <100000>;
|
||||
hdmi-transmitter@40 { // use a dummy device
|
||||
compatible = "sil,sii9022";
|
||||
reg = <0x40>;
|
||||
reset-gpios = <&gpiob 10 GPIO_ACTIVE_LOW>;
|
||||
interrupts = <13 IRQ_TYPE_EDGE_FALLING>;
|
||||
interrupt-parent = <&gpiob>;
|
||||
//pinctrl-names = "default", "sleep";
|
||||
//pinctrl-0 = <<dc_pins_a>;
|
||||
//pinctrl-1 = <<dc_pins_sleep_a>;
|
||||
status = "disabled";
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
sii9022_in: endpoint {
|
||||
remote-endpoint = <<dc_ep1_out>;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
#if 1
|
||||
sii902x: sii902x@39 {
|
||||
compatible = "SiI,sii902x";
|
||||
reset-gpios = <&gpiob 10 GPIO_ACTIVE_LOW>;
|
||||
//pinctrl-names = "default";
|
||||
//pinctrl-0 = <&pinctrl_sii902x>;
|
||||
interrupts = <13 IRQ_TYPE_EDGE_FALLING>;
|
||||
interrupt-parent = <&gpiob>;
|
||||
mode_str ="1280x720M@60";
|
||||
bits-per-pixel = <16>;
|
||||
reg = <0x39>;
|
||||
status = "okay";
|
||||
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
&i2s2 {
|
||||
status = "disable";
|
||||
|
||||
};
|
||||
|
||||
/*test LCD*/
|
||||
<dc {
|
||||
status = "okay";
|
||||
|
||||
port {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
ltdc_ep0_out: endpoint@0 {
|
||||
reg = <0>;
|
||||
remote-endpoint = <&panel_in>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
|
||||
/*
|
||||
* Copyright (C) 100ASK 2020 - All Rights Reserved
|
||||
* Author: 100ask
|
||||
* support: weidongshan@qq.com
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
|
||||
#include "stm32mp157c-100ask-512d-v1.dts"
|
||||
|
||||
/ {
|
||||
model = "100ASK YA157C v2 www.100ask.com";
|
||||
compatible = "st,stm32mp157c-100ask-512d-v1", "st,stm32mp157";
|
||||
|
||||
|
||||
framebuffer-mylcd {
|
||||
compatible = "100ask,lcd_drv";
|
||||
reg = <0x5a001000 0x400>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <<dc_pins_a>;
|
||||
backlight-gpios = <&gpioe 11 GPIO_ACTIVE_HIGH>;
|
||||
clocks = <&rcc LTDC_PX>;
|
||||
clock-names = "lcd";
|
||||
|
||||
display = <&display0>;
|
||||
|
||||
display0: display {
|
||||
bits-per-pixel = <16>;
|
||||
bus-width = <24>;
|
||||
|
||||
display-timings {
|
||||
native-mode = <&timing0>;
|
||||
|
||||
timing0: timing0_1024x600 {
|
||||
clock-frequency = <50000000>;
|
||||
hactive = <1024>;
|
||||
vactive = <600>;
|
||||
hfront-porch = <160>;
|
||||
hback-porch = <140>;
|
||||
hsync-len = <20>;
|
||||
vback-porch = <20>;
|
||||
vfront-porch = <12>;
|
||||
vsync-len = <3>;
|
||||
|
||||
hsync-active = <0>;
|
||||
vsync-active = <0>;
|
||||
de-active = <1>;
|
||||
pixelclk-active = <0>;
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/*LCD Panel*/
|
||||
panel {
|
||||
status = "disabled";
|
||||
compatible = "myir,070tft";
|
||||
interrupts = <11 IRQ_TYPE_EDGE_FALLING>;
|
||||
interrupt-parent = <&gpioe>;
|
||||
pinctrl-names = "default", "sleep";
|
||||
pinctrl-0 = <<dc_pins_a>;
|
||||
pinctrl-1 = <<dc_pins_sleep_a>;
|
||||
//reset-gpios = <&gpioe 12 GPIO_ACTIVE_LOW>;
|
||||
|
||||
backlight = <&panel_backlight>;
|
||||
|
||||
port {
|
||||
panel_in: endpoint {
|
||||
remote-endpoint = <<dc_ep0_out>;
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&panel_backlight {
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
&spi5 {
|
||||
pinctrl-names = "default", "sleep";
|
||||
pinctrl-0 = <&spi5_pins_a>;
|
||||
pinctrl-1 = <&spi5_sleep_pins_a>;
|
||||
status = "okay";
|
||||
cs-gpios = <&gpioh 5 GPIO_ACTIVE_LOW>;
|
||||
spidev: icm20608@0{
|
||||
compatible = "invensense,icm20608";
|
||||
interrupts = <0 IRQ_TYPE_EDGE_FALLING>;
|
||||
interrupt-parent = <&gpioz>;
|
||||
spi-max-frequency = <8000000>;
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
/* test HDMI*/
|
||||
<dc {
|
||||
status = "okay";
|
||||
port {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
ltdc_ep1_out: endpoint@1 {
|
||||
reg = <1>;
|
||||
remote-endpoint = <&sii9022_in>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/*HDMI*/
|
||||
&i2c4 {
|
||||
clock-frequency = <100000>;
|
||||
hdmi-transmitter@40 { // use a dummy device
|
||||
compatible = "sil,sii9022";
|
||||
reg = <0x40>;
|
||||
reset-gpios = <&gpiob 10 GPIO_ACTIVE_LOW>;
|
||||
interrupts = <13 IRQ_TYPE_EDGE_FALLING>;
|
||||
interrupt-parent = <&gpiob>;
|
||||
//pinctrl-names = "default", "sleep";
|
||||
//pinctrl-0 = <<dc_pins_a>;
|
||||
//pinctrl-1 = <<dc_pins_sleep_a>;
|
||||
status = "disabled";
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
sii9022_in: endpoint {
|
||||
remote-endpoint = <<dc_ep1_out>;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
#if 1
|
||||
sii902x: sii902x@39 {
|
||||
compatible = "SiI,sii902x";
|
||||
reset-gpios = <&gpiob 10 GPIO_ACTIVE_LOW>;
|
||||
//pinctrl-names = "default";
|
||||
//pinctrl-0 = <&pinctrl_sii902x>;
|
||||
interrupts = <13 IRQ_TYPE_EDGE_FALLING>;
|
||||
interrupt-parent = <&gpiob>;
|
||||
mode_str ="1280x720M@60";
|
||||
bits-per-pixel = <16>;
|
||||
reg = <0x39>;
|
||||
status = "okay";
|
||||
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
&i2s2 {
|
||||
status = "disable";
|
||||
|
||||
};
|
||||
|
||||
/*test LCD*/
|
||||
<dc {
|
||||
status = "okay";
|
||||
|
||||
port {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
ltdc_ep0_out: endpoint@0 {
|
||||
reg = <0>;
|
||||
remote-endpoint = <&panel_in>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
GCC = $(CROSS_COMPILE)gcc
|
||||
|
||||
multi_framebuffer_test: multi_framebuffer_test.c
|
||||
$(GCC) -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -f multi_framebuffer_test
|
||||
@@ -0,0 +1,184 @@
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/fb.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <time.h>
|
||||
|
||||
static int fd_fb;
|
||||
static struct fb_fix_screeninfo fix; /* Current fix */
|
||||
static struct fb_var_screeninfo var; /* Current var */
|
||||
static int screen_size;
|
||||
static unsigned char *fb_base;
|
||||
static unsigned int line_width;
|
||||
static unsigned int pixel_width;
|
||||
|
||||
/**********************************************************************
|
||||
* 函数名称: lcd_put_pixel
|
||||
* 功能描述: 在LCD指定位置上输出指定颜色(描点)
|
||||
* 输入参数: x坐标,y坐标,颜色
|
||||
* 输出参数: 无
|
||||
* 返 回 值: 会
|
||||
* 修改日期 版本号 修改人 修改内容
|
||||
* -----------------------------------------------
|
||||
* 2020/05/12 V1.0 zh(angenao) 创建
|
||||
***********************************************************************/
|
||||
void lcd_put_pixel(void *fb_base, int x, int y, unsigned int color)
|
||||
{
|
||||
unsigned char *pen_8 = fb_base+y*line_width+x*pixel_width;
|
||||
unsigned short *pen_16;
|
||||
unsigned int *pen_32;
|
||||
|
||||
unsigned int red, green, blue;
|
||||
|
||||
pen_16 = (unsigned short *)pen_8;
|
||||
pen_32 = (unsigned int *)pen_8;
|
||||
|
||||
switch (var.bits_per_pixel)
|
||||
{
|
||||
case 8:
|
||||
{
|
||||
*pen_8 = color;
|
||||
break;
|
||||
}
|
||||
case 16:
|
||||
{
|
||||
/* 565 */
|
||||
red = (color >> 16) & 0xff;
|
||||
green = (color >> 8) & 0xff;
|
||||
blue = (color >> 0) & 0xff;
|
||||
color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
|
||||
*pen_16 = color;
|
||||
break;
|
||||
}
|
||||
case 32:
|
||||
{
|
||||
*pen_32 = color;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
printf("can't surport %dbpp\n", var.bits_per_pixel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void lcd_draw_screen(void *fb_base, unsigned int color)
|
||||
{
|
||||
int x, y;
|
||||
for (x = 0; x < var.xres; x++)
|
||||
for (y = 0; y < var.yres; y++)
|
||||
lcd_put_pixel(fb_base, x, y, color);
|
||||
}
|
||||
|
||||
|
||||
/* ./multi_framebuffer_test single
|
||||
* ./multi_framebuffer_test double
|
||||
*/
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
int nBuffers;
|
||||
int nNextBuffer = 1;
|
||||
char *pNextBuffer;
|
||||
unsigned int colors[] = {0x00FF0000, 0x0000FF00, 0x000000FF, 0, 0x00FFFFFF}; /* 0x00RRGGBB */
|
||||
struct timespec time;
|
||||
|
||||
time.tv_sec = 0;
|
||||
time.tv_nsec = 100000000;
|
||||
|
||||
if (argc != 2)
|
||||
{
|
||||
printf("Usage : %s <single|double>\n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fd_fb = open("/dev/fb0", O_RDWR);
|
||||
if (fd_fb < 0)
|
||||
{
|
||||
printf("can't open /dev/fb0\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix))
|
||||
{
|
||||
printf("can't get fix\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))
|
||||
{
|
||||
printf("can't get var\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
line_width = var.xres * var.bits_per_pixel / 8;
|
||||
pixel_width = var.bits_per_pixel / 8;
|
||||
screen_size = var.xres * var.yres * var.bits_per_pixel / 8;
|
||||
|
||||
nBuffers = fix.smem_len / screen_size;
|
||||
printf("nBuffers = %d\n", nBuffers);
|
||||
|
||||
fb_base = (unsigned char *)mmap(NULL , fix.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
|
||||
if (fb_base == (unsigned char *)-1)
|
||||
{
|
||||
printf("can't mmap\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((argv[1][0] == 's') || (nBuffers == 1))
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
/* use single buffer */
|
||||
for (i = 0; i < sizeof(colors)/sizeof(colors[0]); i++)
|
||||
{
|
||||
lcd_draw_screen(fb_base, colors[i]);
|
||||
nanosleep(&time, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* use double buffer */
|
||||
/* a. enable use multi buffers */
|
||||
var.yres_virtual = nBuffers * var.yres;
|
||||
ioctl(fd_fb, FBIOPUT_VSCREENINFO, &var);
|
||||
|
||||
while (1)
|
||||
{
|
||||
for (i = 0; i < sizeof(colors)/sizeof(colors[0]); i++)
|
||||
{
|
||||
/* get buffer */
|
||||
pNextBuffer = fb_base + nNextBuffer * screen_size;
|
||||
|
||||
/* set buffer */
|
||||
lcd_draw_screen(pNextBuffer, colors[i]);
|
||||
|
||||
/* switch buffer */
|
||||
var.yoffset = nNextBuffer * var.yres;
|
||||
ioctl(fd_fb, FBIOPAN_DISPLAY, &var);
|
||||
|
||||
ret = 0;
|
||||
ioctl(fd_fb, FBIO_WAITFORVSYNC, &ret);
|
||||
|
||||
nNextBuffer = !nNextBuffer;
|
||||
nanosleep(&time, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
munmap(fb_base , screen_size);
|
||||
close(fd_fb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/fb.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
static int fd_fb;
|
||||
static struct fb_var_screeninfo var; /* Current var */
|
||||
static int screen_size;
|
||||
static unsigned char *fb_base;
|
||||
static unsigned int line_width;
|
||||
static unsigned int pixel_width;
|
||||
|
||||
/**********************************************************************
|
||||
* 函数名称: lcd_put_pixel
|
||||
* 功能描述: 在LCD指定位置上输出指定颜色(描点)
|
||||
* 输入参数: x坐标,y坐标,颜色
|
||||
* 输出参数: 无
|
||||
* 返 回 值: 会
|
||||
* 修改日期 版本号 修改人 修改内容
|
||||
* -----------------------------------------------
|
||||
* 2020/05/12 V1.0 zh(angenao) 创建
|
||||
***********************************************************************/
|
||||
void lcd_put_pixel(int x, int y, unsigned int color)
|
||||
{
|
||||
unsigned char *pen_8 = fb_base+y*line_width+x*pixel_width;
|
||||
unsigned short *pen_16;
|
||||
unsigned int *pen_32;
|
||||
|
||||
unsigned int red, green, blue;
|
||||
|
||||
pen_16 = (unsigned short *)pen_8;
|
||||
pen_32 = (unsigned int *)pen_8;
|
||||
|
||||
switch (var.bits_per_pixel)
|
||||
{
|
||||
case 8:
|
||||
{
|
||||
*pen_8 = color;
|
||||
break;
|
||||
}
|
||||
case 16:
|
||||
{
|
||||
/* 565 */
|
||||
red = (color >> 16) & 0xff;
|
||||
green = (color >> 8) & 0xff;
|
||||
blue = (color >> 0) & 0xff;
|
||||
color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
|
||||
*pen_16 = color;
|
||||
break;
|
||||
}
|
||||
case 32:
|
||||
{
|
||||
*pen_32 = color;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
printf("can't surport %dbpp\n", var.bits_per_pixel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
|
||||
fd_fb = open("/dev/fb0", O_RDWR);
|
||||
if (fd_fb < 0)
|
||||
{
|
||||
printf("can't open /dev/fb0\n");
|
||||
return -1;
|
||||
}
|
||||
if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))
|
||||
{
|
||||
printf("can't get var\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
line_width = var.xres * var.bits_per_pixel / 8;
|
||||
pixel_width = var.bits_per_pixel / 8;
|
||||
screen_size = var.xres * var.yres * var.bits_per_pixel / 8;
|
||||
fb_base = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
|
||||
if (fb_base == (unsigned char *)-1)
|
||||
{
|
||||
printf("can't mmap\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 清屏: 全部设为白色 */
|
||||
memset(fb_base, 0xff, screen_size);
|
||||
|
||||
/* 随便设置出100个为红色 */
|
||||
for (i = 0; i < 100; i++)
|
||||
lcd_put_pixel(var.xres/2+i, var.yres/2, 0xFF0000);
|
||||
|
||||
munmap(fb_base , screen_size);
|
||||
close(fd_fb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||