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

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