mirror of
https://e.coding.net/weidongshan/linux/doc_and_source_for_drivers.git
synced 2025-12-03 05:11:14 +08:00
add: 03_LCD/14,15,18 for stm32mp157
This commit is contained in:
@@ -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
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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user