add: 03_LCD/14,15,18 for stm32mp157

This commit is contained in:
weidongshan
2021-02-03 18:15:01 +08:00
parent a5fc1d05af
commit 7a4d88f8be
29 changed files with 2397 additions and 13 deletions

View File

@@ -222,8 +222,8 @@ CFBADD设置LAYER1图层1的显存地址。
![](pic/02_LCD驱动/030_LTDC_L1CFBLR.png)
CFBPFramebuffer中行像素所占据的字节数
CFBLLFramebuffer中行像素所占据的字节数+7
CFBPFramebuffer中行像素所占据的字节数
CFBLLFramebuffer中行像素所占据的字节数+7

View File

@@ -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控制器

View 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开发板"
* 下载开发板配套资料
* 下载完后,工具在如下目录里:
![image-20210122094724188](pic/02_LCD驱动/041_pins_tools.png)
### 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结构体里
![image-20210125185436508](pic/02_LCD驱动/042_display_timing.png)
参考内核文件:
* `drivers\video\of_display_timing.c`
* `drivers\video\fbdev\mxsfb.c`
#### 3.2 使用参数配置LCD控制器
根据芯片手册,一个一个设置寄存器:
* Framebuffer地址设置
* Framebuffer中数据格式设置
* LCD时序参数设置
* LCD引脚极性设置

View 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`出现如下问题:
![image-20210203095417662](pic/02_LCD驱动/043_make_menuconfig_ncurse.png)
* 就先执行以下命令:
```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`
![image-20210203132435507](pic/02_LCD驱动/047_boot_mount.png)
* 更新单板文件
```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`后就再无输出,串口信息如下:
![image-20210203094102367](pic/02_LCD驱动/044_cannot_boot.png)
这表示内核或设备树有问题只能使用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. 问题所在
* 设备树中引脚冲突,如下修改
![image-20210203094835973](pic/02_LCD驱动/045_pins_conflict.png)
* 驱动程序函数使用方法有变化
![image-20210203095040925](pic/02_LCD驱动/046_dma_alloc_wc.png)

View File

@@ -19,16 +19,118 @@
### 1. 单Buffer的缺点
* 如果APP速度很慢可以看到它在LCD上缓慢绘制图案
* 即使APP速度很高LCD控制器不断从Framebuffer中读取数据来显示而APP不断把数据写入Framebuffer
* 假设APP想把LCD显示为整屏幕的蓝色、红色
* 很大几率出现这种情况:
* LCD控制器读取Framebuffer数据读到一半时在LCD上显示了半屏幕的蓝色
* 这是APP非常高效地把整个Framebuffer的数据都改为了红色
* LCD控制器继续读取数据于是LCD上就会显示半屏幕蓝色、半屏幕红色
* 人眼就会感觉到屏幕闪烁、撕裂
![image-20210203172823180](pic/02_LCD驱动/048_singble_buffer.png)
### 2. 使用多Buffer来改进
上述两个缺点的根源是一致的Framebuffer中的数据还没准备好整帧数据就被LCD控制器使用了。
使用双buffer甚至多buffer可以解决这个问题
### 3. 内核驱动程序分析
### 4. APP编写方法
* 假设有2个FramebufferFB0、FB1
* LCD控制器正在读取FB0
* APP写FB1
* 写好FB1后让LCD控制器切换到FB1
* APP写FB0
* 写好FB0后让LCD控制器切换到FB0
### 3. 内核驱动程序、APP互相配合使用多buffer
流程如下:
![image-20210203180534206](pic/02_LCD驱动/049_drv_app_use_double_buff.png)
* 驱动分配多个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) // 调用硬件相关的函数
```
示例:
![image-20210203180357860](pic/02_LCD驱动/050_mxsfb_pan_display.png)
* APP等待切换完成(在驱动程序中已经等待切换完成了,所以这个调用并无必要)
```c
ret = 0;
ioctl(fd_fb, FBIO_WAITFORVSYNC, &ret);
```

View 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`
![image-20210203132435507](pic/02_LCD驱动/047_boot_mount.png)
* 更新单板文件
```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
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

View File

@@ -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格式是ARGB8888ARGB1555等会使用到如选的是RGB565,RBG888等者不设置也可以*/
lcdif->LTDC_L1CACR = 0xff;
/*
*BC = BF1 x C + BF2 x Cs
*BF1为LTDC_L1BFCR设置的[10:8]值,设置为100constant alpha即LTDC_L1CACR设置的值0xff表示完全不透明
*BF2为LTDC_L1BFCR设置的[2:0]值设置为101constant 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");

View File

@@ -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 = <&ltdc_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 = <&ltdc_pins_a>;
pinctrl-1 = <&ltdc_pins_sleep_a>;
//reset-gpios = <&gpioe 12 GPIO_ACTIVE_LOW>;
backlight = <&panel_backlight>;
status = "okay";
port {
panel_in: endpoint {
remote-endpoint = <&ltdc_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*/
&ltdc {
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 = <&ltdc_pins_a>;
//pinctrl-1 = <&ltdc_pins_sleep_a>;
status = "disabled";
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
sii9022_in: endpoint {
remote-endpoint = <&ltdc_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*/
&ltdc {
status = "okay";
port {
#address-cells = <1>;
#size-cells = <0>;
ltdc_ep0_out: endpoint@0 {
reg = <0>;
remote-endpoint = <&panel_in>;
};
};
};

View File

@@ -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

View 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格式是ARGB8888ARGB1555等会使用到如选的是RGB565,RBG888等者不设置也可以*/
lcdif->LTDC_L1CACR = 0xff;
/*
*BC = BF1 x C + BF2 x Cs
*BF1为LTDC_L1BFCR设置的[10:8]值,设置为100constant alpha即LTDC_L1CACR设置的值0xff表示完全不透明
*BF2为LTDC_L1BFCR设置的[2:0]值设置为101constant 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");

View File

@@ -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 = <&ltdc_pins_a>;
pinctrl-1 = <&ltdc_pins_sleep_a>;
//reset-gpios = <&gpioe 12 GPIO_ACTIVE_LOW>;
backlight = <&panel_backlight>;
status = "okay";
port {
panel_in: endpoint {
remote-endpoint = <&ltdc_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*/
&ltdc {
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 = <&ltdc_pins_a>;
//pinctrl-1 = <&ltdc_pins_sleep_a>;
status = "disabled";
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
sii9022_in: endpoint {
remote-endpoint = <&ltdc_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*/
&ltdc {
status = "okay";
port {
#address-cells = <1>;
#size-cells = <0>;
ltdc_ep0_out: endpoint@0 {
reg = <0>;
remote-endpoint = <&panel_in>;
};
};
};

View File

@@ -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 = <&ltdc_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 = <&ltdc_pins_a>;
pinctrl-1 = <&ltdc_pins_sleep_a>;
//reset-gpios = <&gpioe 12 GPIO_ACTIVE_LOW>;
backlight = <&panel_backlight>;
port {
panel_in: endpoint {
remote-endpoint = <&ltdc_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*/
&ltdc {
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 = <&ltdc_pins_a>;
//pinctrl-1 = <&ltdc_pins_sleep_a>;
status = "disabled";
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
sii9022_in: endpoint {
remote-endpoint = <&ltdc_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*/
&ltdc {
status = "okay";
port {
#address-cells = <1>;
#size-cells = <0>;
ltdc_ep0_out: endpoint@0 {
reg = <0>;
remote-endpoint = <&panel_in>;
};
};
};

View File

@@ -0,0 +1,7 @@
GCC = $(CROSS_COMPILE)gcc
multi_framebuffer_test: multi_framebuffer_test.c
$(GCC) -o $@ $<
clean:
rm -f multi_framebuffer_test

View File

@@ -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;
}

View File

@@ -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;
}