# simple-framebuffer
## simplefb简介
simple-framebuffer,在uboot中设置好了显示硬件,并给出了显存地址。
chosen节点用于uboot和linux之间传递参数,所以simple-fb的参数也是在这里传递。
设备树中下面这段只用于uboot,内核不管。
~~~
chosen {
#address-cells = <1>;
#size-cells = <1>;
ranges;
simplefb_lcd: framebuffer@0 {
compatible = "allwinner,simple-framebuffer",
"simple-framebuffer";
allwinner,pipeline = "de0-lcd0";
clocks = <&ccu CLK_BUS_TCON0>, <&display_clocks 0>,
<&display_clocks 6>, <&ccu CLK_TCON0>;
status = "disabled";
};
};
~~~
实际启动后,可以看到fb后面被填充了实际地址
~~~
# cat /proc/device-tree/chosen/framebuffer@43e89000/
allwinner,pipeline format reg width
clocks height status
compatible name stride
~~~
所以,我们要做的就是在uboot中设置好lcd,并传递显存地址到设备树。
## uboot修改
### menuconfig
CONFIG_VIDEO
CONFIG_VIDEO_LCD_MODE drivers/video/sunxi_display.c
VIDEO_LCD_PANEL_PARALLEL
VIDEO_LCD_IF_PARALLEL drivers/video/sunxi_display.c
在 board/sunxi/Kconfig 中加入8080选项。
### drivers/video/cfb_console.c
cfg_video_init 里调用了各个架构的video_hw_init来初始化。
~~~
#define VIDEO_VISIBLE_COLS (pGD->winSizeX)
#define VIDEO_VISIBLE_ROWS (pGD->winSizeY)
#define VIDEO_PIXEL_SIZE (pGD->gdfBytesPP)
#define VIDEO_DATA_FORMAT (pGD->gdfIndex) GDF_16BIT_565RGB
#define VIDEO_FB_ADRS (pGD->frameAdrs)
~~~
### drivers/video/sunxi_display.c
video_hw_init
video_get_ctfb_res_modes(RES_MODE_1024x768, 24, &mode,&sunxi_display.depth, &options); //先写个默认配置
--sunxi_get_default_mon 填到sunxi_display.monitor
----sunxi_has_lcd
在配置里填了LCD参数,就会默认选择LCD作为显示
~~~
static bool sunxi_has_lcd(void)
{
char *lcd_mode = CONFIG_VIDEO_LCD_MODE;
return lcd_mode[0] != 0;
}
~~~
--sunxi_display.depth = video_get_params(&custom, lcd_mode);
--mode = &custom;
这里获取了menuconfig里配置的参数:
~~~
struct ctfb_res_modes
/* x y hz pixclk ps/kHz le ri up lo hs vs s vmode */
~~~
--sunxi_display.fb_size =(mode->xres * mode->yres * 4 + 0xfff) & ~0xfff; //显存4KB对齐
--if (sunxi_display.fb_size > CONFIG_SUNXI_MAX_FB_SIZE) //判断显存大小
~~~
#ifndef CONFIG_MACH_SUN8I_V3S
#define CONFIG_SUNXI_MAX_FB_SIZE (16 << 20)
#else //V3S使用4MB显存
#define CONFIG_SUNXI_MAX_FB_SIZE (4 << 20)
#endif
~~~
-- gd->fb_base = gd->bd->bi_dram[0].start + gd->bd->bi_dram[0].size - sunxi_display.fb_size; //显存放到最后4MB
-- sunxi_engines_init(); //初始化de
-- fb_dma_addr = gd->fb_base - CONFIG_SYS_SDRAM_BASE; //相对偏移
-- sunxi_mode_set(mode, fb_dma_addr); //设置显示模式
~~~
graphic_device->frameAdrs = sunxi_display.fb_addr;
graphic_device->gdfIndex = GDF_32BIT_X888RGB;
graphic_device->gdfBytesPP = 4;
graphic_device->winSizeX = mode->xres - 2 * overscan_x;
graphic_device->winSizeY = mode->yres - 2 * overscan_y;
graphic_device->plnSizeX = mode->xres * graphic_device->gdfBytesPP;
return graphic_device;
~~~
#### sunxi_engines_init();
完成对时钟的初始化
sunxi_composer_init(); //sunxi_composer_init();
~~~
clock_set_de2_mod_clock(&ccm->de_clk_cfg, 300000000); //pll6是600M,2分频,30M
writel(CCM_DE2_CTRL_GATE | CCM_DE2_CTRL_PLL6_2X | CCM_DE2_CTRL_M(div),
~~~
sunxi_lcdc_init();
~~~
u32 lcd0_ch0_clk_cfg; /* 0x118 LCD0 CH0 module clock */ //TCON
~~~
~~~
//先开启TCON时钟
//关闭tcon,中断,像素时钟
//设置所有io为三态
~~~
sunxi_drc_init();
#### sunxi_mode_set(mode, fb_dma_addr);
~~~
switch (sunxi_display.monitor) {
case sunxi_monitor_lcd:
sunxi_lcdc_panel_enable(); //复位,背光
……
sunxi_composer_mode_set(mode, address); //de设置
sunxi_lcdc_tcon0_mode_set(mode, false); //关键的显示方式设置
sunxi_composer_enable(); //使能de
sunxi_lcdc_enable(); //使能tcon
……
sunxi_lcdc_backlight_enable(); //使能背光
break;
~~~
#### sunxi_lcdc_tcon0_mode_set
x:800,y:480,depth:18,pclk_khz:33000,le:87,ri:40,up:31,lo:13,hs:1,vs:1,sync:3,vmode:0
~~~
//初始化IO
for (pin = SUNXI_GPE(0); pin <= SUNXI_GPE(24); pin++) {
if (pin >= SUNXI_GPE(20) && pin <= SUNXI_GPE(22))
continue; /* These pins are not LCD */
sunxi_gpio_set_cfgpin(pin, SUN8I_V3S_GPE_LCD); //IO复用为LCD
}
sunxi_lcdc_pll_set(0, mode->pixclock_khz, &clk_div, &clk_double); //设置video的时钟
/* Use tcon0 */
clrsetbits_le32(&lcdc->ctrl, SUNXI_LCDC_CTRL_IO_MAP_MASK,
SUNXI_LCDC_CTRL_IO_MAP_TCON0);
clk_delay = sunxi_lcdc_get_clk_delay(mode, 0); //同步时间的延时
writel(SUNXI_LCDC_TCON0_CTRL_ENABLE |
SUNXI_LCDC_TCON0_CTRL_CLK_DELAY(clk_delay), &lcdc->tcon0_ctrl);
writel(SUNXI_LCDC_TCON0_DCLK_ENABLE |
SUNXI_LCDC_TCON0_DCLK_DIV(clk_div), &lcdc->tcon0_dclk);
writel(SUNXI_LCDC_X(mode->xres) | SUNXI_LCDC_Y(mode->yres),
&lcdc->tcon0_timing_active);
bp = mode->hsync_len + mode->left_margin;
total = mode->xres + mode->right_margin + bp;
writel(SUNXI_LCDC_TCON0_TIMING_H_TOTAL(total) |
SUNXI_LCDC_TCON0_TIMING_H_BP(bp), &lcdc->tcon0_timing_h);
bp = mode->vsync_len + mode->upper_margin;
total = mode->yres + mode->lower_margin + bp;
writel(SUNXI_LCDC_TCON0_TIMING_V_TOTAL(total) |
SUNXI_LCDC_TCON0_TIMING_V_BP(bp), &lcdc->tcon0_timing_v);
#ifdef CONFIG_VIDEO_LCD_IF_PARALLEL
writel(SUNXI_LCDC_X(mode->hsync_len) | SUNXI_LCDC_Y(mode->vsync_len),
&lcdc->tcon0_timing_sync); //basic3, HSPW,VSPW
writel(0, &lcdc->tcon0_hv_intf); //RGB接口配置
writel(0, &lcdc->tcon0_cpu_intf); //8080 接口配置!!!
#endif
if (sunxi_display.depth == 18 || sunxi_display.depth == 16) {
writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[0]);
……
writel(SUNXI_LCDC_TCON0_FRM_SEED, &lcdc->tcon0_frm_seed[5]);
……
writel(SUNXI_LCDC_TCON0_FRM_TAB3, &lcdc->tcon0_frm_table[3]);
writel(((sunxi_display.depth == 18) ?
SUNXI_LCDC_TCON0_FRM_CTRL_RGB666 :
SUNXI_LCDC_TCON0_FRM_CTRL_RGB565),
&lcdc->tcon0_frm_ctrl);
}
val = SUNXI_LCDC_TCON0_IO_POL_DCLK_PHASE(CONFIG_VIDEO_LCD_DCLK_PHASE);
if (!(mode->sync & FB_SYNC_HOR_HIGH_ACT))
val |= SUNXI_LCDC_TCON_HSYNC_MASK;
if (!(mode->sync & FB_SYNC_VERT_HIGH_ACT))
val |= SUNXI_LCDC_TCON_VSYNC_MASK;
#ifdef CONFIG_VIDEO_VGA_VIA_LCD_FORCE_SYNC_ACTIVE_HIGH
if (for_ext_vga_dac)
val = 0;
#endif
writel(val, &lcdc->tcon0_io_polarity);
writel(0, &lcdc->tcon0_io_tristate); //使能
~~~
#### 新增的CPU接口配置
//arch/arm/include/asm/arch-sunxi/display.h加上
//#define SUNXI_LCDC_TCON0_IF(n) (((n) & 3) << 24)
//0是RGB,1是8080
~~~
writel(SUNXI_LCDC_TCON0_CTRL_ENABLE | SUNXI_LCDC_TCON0_IF(1)
SUNXI_LCDC_TCON0_CTRL_CLK_DELAY(clk_delay), &lcdc->tcon0_ctrl);
if((panel->lcd_if==LCD_IF_CPU) || (panel->lcd_if==LCD_IF_DSI && panel->lcd_dsi_if==LCD_DSI_IF_COMMAND_MODE))
{
lcd_dev[sel]->tcon0_cpu_tri0.bits.block_space = panel->lcd_ht - panel->lcd_x - 1;
lcd_dev[sel]->tcon0_cpu_tri2.bits.trans_start_set = panel->lcd_x-1;
}
if(lcd_te == 2)//falling mode
{
lcd_dev[sel]->tcon0_cpu_tri3.bits.tri_int_mode = 3;
}
else if(lcd_te == 1)//rising mode
{
lcd_dev[sel]->tcon0_cpu_tri3.bits.tri_int_mode = 2;
return 0;
}
else
{
// u32 cntr_set = (panel->lcd_dclk_freq*1000*1000/(60*4));
u32 cntr_set = panel->lcd_ht*panel->lcd_vt/4;
u32 cntr_n,cntr_m;
for(cntr_m=1;cntr_m<256;cntr_m++)
{
if((cntr_set/cntr_m)<65535)
{
cntr_n = cntr_set/cntr_m;
lcd_dev[sel]->tcon0_cpu_tri3.bits.counter_m = cntr_m-1;
lcd_dev[sel]->tcon0_cpu_tri3.bits.counter_n = cntr_n-1;
lcd_dev[sel]->tcon0_cpu_tri3.bits.tri_int_mode = 1;
return 0;
}
}
lcd_dev[sel]->tcon0_cpu_tri3.bits.tri_int_mode = 0;
return -1;
}
}
else if(panel->lcd_if == LCD_IF_CPU)
{
lcd_dev[sel]->tcon0_ctl.bits.tcon0_if = 1;
lcd_dev[sel]->tcon0_cpu_ctl.bits.cpu_mode = panel->lcd_cpu_if;
//lcd_dev[sel]->tcon0_cpu_ctl.bits.da = 1; //flush image data 1,flush command 0
lcd_dev[sel]->tcon_ecfifo_ctl.bits.ecc_fifo_setting = (1<<3);
panel->lcd_fresh_mode = 1;
if(panel->lcd_cpu_mode == 0)
tcon0_cfg_mode_auto(sel,panel);
else
tcon0_cfg_mode_tri(sel,panel);
lcd_dev[sel]->tcon0_cpu_tri4.bits.en = 0;
}
if(panel->lcd_fresh_mode == 1)
{
u32 lcd_te;
lcd_te = (panel->lcd_if==LCD_IF_CPU)? panel->lcd_cpu_te: panel->lcd_dsi_te;
lcd_dev[sel]->tcon0_io_tri.bits.io0_output_tri_en = (lcd_te==0)? 0:1;
}
else
{
lcd_dev[sel]->tcon0_io_tri.bits.io0_output_tri_en = 0;
}
~~~
~~~
struct sunxi_lcdc_reg {
u32 ctrl; /* 0x00 */
u32 int0; /* 0x04 */
u32 int1; /* 0x08 */
u8 res0[0x04]; /* 0x0c */
u32 tcon0_frm_ctrl; /* 0x10 */
u32 tcon0_frm_seed[6]; /* 0x14 */
u32 tcon0_frm_table[4]; /* 0x2c */
u8 res1[4]; /* 0x3c */
u32 tcon0_ctrl; /* 0x40 */
u32 tcon0_dclk; /* 0x44 */
u32 tcon0_timing_active; /* 0x48 */
u32 tcon0_timing_h; /* 0x4c */
u32 tcon0_timing_v; /* 0x50 */
u32 tcon0_timing_sync; /* 0x54 */
u32 tcon0_hv_intf; /* 0x58 */
u8 res2[0x04]; /* 0x5c */
u32 tcon0_cpu_intf; /* 0x60 */
u32 tcon0_cpu_wr_dat; /* 0x64 */
u32 tcon0_cpu_rd_dat0; /* 0x68 */
u32 tcon0_cpu_rd_dat1; /* 0x6c */
u32 tcon0_ttl_timing0; /* 0x70 */
u32 tcon0_ttl_timing1; /* 0x74 */
u32 tcon0_ttl_timing2; /* 0x78 */
u32 tcon0_ttl_timing3; /* 0x7c */
u32 tcon0_ttl_timing4; /* 0x80 */
u32 tcon0_lvds_intf; /* 0x84 */
u32 tcon0_io_polarity; /* 0x88 */
u32 tcon0_io_tristate; /* 0x8c */
u32 tcon1_ctrl; /* 0x90 */
u32 tcon1_timing_source; /* 0x94 */
u32 tcon1_timing_scale; /* 0x98 */
u32 tcon1_timing_out; /* 0x9c */
u32 tcon1_timing_h; /* 0xa0 */
u32 tcon1_timing_v; /* 0xa4 */
u32 tcon1_timing_sync; /* 0xa8 */
u8 res3[0x44]; /* 0xac */
u32 tcon1_io_polarity; /* 0xf0 */
u32 tcon1_io_tristate; /* 0xf4 */
u8 res4[0x108]; /* 0xf8 */
u32 mux_ctrl; /* 0x200 */
u8 res5[0x1c]; /* 0x204 */
u32 lvds_ana0; /* 0x220 */
u32 lvds_ana1; /* 0x224 */
~~~
参考资料:
Documentation/devicetree/bindings/display/simple-framebuffer-sunxi.txt
- 前言
- 荔枝派TODO任务领取
- linux使用小贴士
- 入门篇
- 板卡介绍
- 开箱指南
- 烧录启动系统
- 联网方法
- 镜像使用
- 镜像说明
- buildroot系统使用
- debian系统使用
- 外设操作
- 外设操作概览
- 低速外设
- GPIO
- GPIO模拟低速接口
- UART
- PWM
- I2C
- SPI
- 高速接口
- SDIO
- USB
- EtherNet
- DVP CSI
- MIPI CSI
- 模拟外设
- CODEC
- LRADC
- 常见设备驱动
- USB摄像头
- USB 3G/4G 网卡
- 舵机
- 开发篇
- UBOOT适配
- UBOOT编译
- UBOOT配置
- UBOOT配置屏幕分辨率
- UBOOT配置SPI启动
- Linux内核开发
- Linux内核编译
- BSP Linux内核编译.md
- Linux内核选项
- 外设驱动与设备树
- RTL8723BS驱动
- 根文件系统定制
- buildroot定制系统
- buildroot添加软件包
- openwrt定制系统
- emdebian定制系统
- camdriod开发
- camdriod编译
- 主线Uboot引导Camdriod
- 系统镜像打包
- XBOOT适配
- 荔枝运行XBOOT
- 应用篇
- 游戏机-基于EmulationStation
- 游戏机-gnuboy
- 语音识别-科大讯飞云
- GUI-QT5
- 语音识别-离线关键词识别
- 路由器-Lichee Zero
- 投稿文章
- 荔枝派Zero开箱指南
- Zero i2c oled使用指南
- zero SPI LCD使用指南
- Zero u-boot编译和使用指南
- TF WiFi使用方法
- Zero Ethernet使用指南
- Zero 移植Qt5.4.1
- ZeroSpiNorFlash启动系统制作指南
- Visio-uboot-sunxi流程
- lichee 编译踩坑记录(ilichee ZERO)
- lichee_zero_外设GPIO接口
- TF WIFI 小白编
- 从零开始LicheePi Zero的开发
- 认识Zero的硬件
- 搭建Zero的开发环境
- 主线Uboot
- 主线kernel
- BSP kernel
- BSP内核启动
- bsp内核的摄像头使用
- BSP内核中的保留内存
- uboot启动BSP内核常见错误
- BSP内核 FBTFT移植
- BSP内核启动错误及警告解决
- buildroot 根文件系统
- emdebian 根文件系统
- SPI Flash 系统编译
- sunxi-fel增加对16M 以上flash的支持
- overlayfs的使用
- jffs2系统挂载不上的常见原因
- JFFS2 文件系统简介
- uboot对spi flash的识别
- bsp内核的SPI flash启动
- Docker开发环境
- Docker 命令速查
- 基础ubuntu系统配置
- docker离线镜像
- Zero系统烧录
- dd镜像烧录
- 分区镜像烧录
- SPI Flash系统烧录
- 一键镜像烧录
- Zero外设把玩
- I2C操作
- PWM输出
- CODEC的使用
- 以太网使用指南
- GPIO操作
- 文件IO方式
- C语言接口(mmap)
- Python操作GPIO
- pinctrl-sunxi介绍
- UART操作
- 点屏
- 点屏之RGB屏
- 点屏之SPI屏 ili9341
- 点屏之SPI OLED
- 点屏之I2C OLED
- 点屏之SPI屏 ili9488
- 点屏之MCU屏
- 点屏之触摸屏驱动
- 点屏之simple-framebuffer
- 点屏之屏幕时序
- 时钟控制器CCM
- 摄像头
- BSP DVP摄像头
- BSP MIPI 摄像头
- 主线DVP摄像头
- 主线 MIPI摄像头
- SPI 操作
- 应用层开发
- 开机自启动
- Segment Fault调试
- Zero通过OTG共享PC网络
- USB摄像头使用
- 基于QT的GUI开发
- 移植tslib
- 移植QT5.9.1
- 移植QT4.8.7
- QtCreator使用
- Qt5.x移植到Qt4.8
- Qt字体相关
- Qt移植总结
- Qt裁剪
- Qt去除鼠标指针显示
- zero_imager使用
- 驱动开发
- 设备树简介
- GPU/DRM 显示驱动
- sys下设备树查看
- atmel触摸屏驱动分析
- atmel触摸屏中断改轮询
- uboot下gpio操作
- helloworld驱动编译演示
- FBTFT分析
- 内核模块静态加载的顺序
- SPI驱动分析
- SPI 驱动编写
- Uboot开发
- 开机logo
- 看门狗的使用
- 关于系统reboot
- 内核printk等级设置