# 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