RK3288 Uboot Display 驱动详解
作者:互联网
怀揣着十几个疑问整理了rk3288 uboot 阶段display相关代码:
1、代码流程
由rk3288 uboot 启动流程分析可知,dispaly 驱动在board_fbt_preboot;中被调用,如下所示:
#ifdef CONFIG_LCD
/* logo state defautl init = 0 */
g_logo_on_state = 0;
if (gd->fdt_blob) {
int node = fdt_path_offset(gd->fdt_blob, "/fb");
g_logo_on_state = fdtdec_get_int(gd->fdt_blob, node, "rockchip,uboot-logo-on", 0);
}
printf("read logo on state from dts [%d]\n", g_logo_on_state);
if (g_logo_on_state != 0) {
lcd_enable_logo(true);
drv_lcd_init();
}
#endif
当定义了CONFIG_LCD后,这段代码被调用通过对g_logo_on_state的判断来确定是否在uboot阶段显示logo。
/* /common/lcd.c */
static void *lcd_base; /* Start of framebuffer memory */
lcd_enable_logo(true);
lcd_show_logo = 1;
drv_lcd_init();
/* 第一步:获取framebuffer memory的起始位置 */
lcd_base = map_sysmem(gd->fb_base, 0);/* framebuffer addr = 0x7dc00000 ,位于ddr地址的最后 */
/* 第二步: 初始化LCD */
/* 2.1 : 定义一个rockchip_fb的结构体指针 */
/* 2.2 : 解析屏幕相关的参数填充到panel_info结构体 */
/* 2.3 : 解析gpio相关的参数填充到pwr_ctr 结构体 */
/* 2.4 : 根据pwr_ctr结构体 控制gpio 输出 */
/* 2.5 : lcd 控制器的初始化 */
lcd_init(lcd_base); /* LCD initialization */
/* drivers/video/rockchip_fb.c */
struct rockchip_fb rockchip_fb; /* 定义了一个全局结构体 rockchip_fb */
lcd_ctrl_init(lcdbase);
/* 定义一个 rockchip_fb 结构体指针 */
struct rockchip_fb *fb = &rockchip_fb; /* rockchip_fb 是一个全局结构体 */
/* 从设备树解析屏幕参数 */
int ret = rk_fb_parse_dt(fb, gd->fdt_blob); /* gd->fdt_blob 决定了是否从dts中解析数据 */
/* 获得dtb下display-timings 节点的偏移 ,这个偏移可以代表这个节点 */
int node = fdt_path_offset(blob(gd->fdt_blob), "/display-timings");
phandle = fdt_getprop_u32_default_node(blob, node, 0, "native-mode", -1);
/* 填充panel_info结构体 */
/* panel_info 也是一个全局结构体 */
panel_info.vl_bpix = 5;
panel_info.lvds_ttl_en = 0;
panel_info.screen_type = fdtdec_get_int(blob, node, "screen-type", -1);
panel_info.color_mode = fdtdec_get_int(blob, node, "color-mode", 0);
panel_info.lcd_face = fdtdec_get_int(blob, node, "out-face", -1);
panel_info.vl_col = fdtdec_get_int(blob, node, "hactive", 0);
panel_info.vl_row = fdtdec_get_int(blob, node, "vactive", 0);
panel_info.vl_width = fdtdec_get_int(blob, node, "hactive", 0);
panel_info.vl_height = fdtdec_get_int(blob, node, "vactive", 0);
panel_info.vl_freq = fdtdec_get_int(blob, node, "clock-frequency", 0);
panel_info.vl_oep = fdtdec_get_int(blob, node, "de-active", -1);
panel_info.vl_hsp = fdtdec_get_int(blob, node, "hsync-active", -1);
panel_info.vl_vsp = fdtdec_get_int(blob, node, "vsync-active", -1);
panel_info.lvds_format = fdtdec_get_int(blob, node, "lvds-format", -1);
panel_info.vl_swap_rb = fdtdec_get_int(blob, node, "swap-rb", -1);
panel_info.vl_hspw = fdtdec_get_int(blob, node, "hsync-len", 0);
panel_info.vl_hfpd = fdtdec_get_int(blob, node, "hfront-porch", 0);
panel_info.vl_hbpd = fdtdec_get_int(blob, node, "hback-porch", 0);
panel_info.vl_vspw = fdtdec_get_int(blob, node, "vsync-len", 0);
panel_info.vl_vfpd = fdtdec_get_int(blob, node, "vfront-porch", 0);
panel_info.vl_vbpd = fdtdec_get_int(blob, node, "vback-porch", 0);
/* 解析lcdc0 节点 */
node = rk_fb_find_lcdc_node_dt(rk_fb(fb), blob);
node = fdt_path_offset(blob, "lcdc0");
/* 解析gpio 节点 */
rk_fb_pwr_ctr_parse_dt(rk_fb, blob);
panel_info.logo_rgb_mode = RGB565;
/* 使能对应的gpio口 */
rk_fb_pwr_enable(fb);
gpio_direction_output(pwr_ctr->gpio.gpio,pwr_ctr->atv_val);
mdelay(pwr_ctr->delay);
/* LCDC 控制器 初始化 */
/* drivers/video/rk32_lcdc.c */
rk_lcdc_init(panel_info.lcdc_id);
struct lcdc_device *lcdc_dev = &rk32_lcdc; /* rk32_lcdc 是一个全局结构体 */
lcdc_dev->soc_type = gd->arch.chiptype; /* 应该没什么用这个参数 */
lcdc_dev->id = lcdc_id; /* lcdc_id = panel_info.lcdc_id */
rk32_lcdc_parse_dt(lcdc_dev, gd->fdt_blob);
/* 解析得到lcdc0 节点 */
lcdc_dev->node = fdt_path_offset(blob, "lcdc0");
/* 获取lcdc 节点的regs 资源 */
lcdc_dev->regs = fdtdec_get_addr(blob, lcdc_dev->node, "reg");
/* 寄存器控制 */
/* 写 SYS_CTRL */
/* 写 DSP_CTRL1 */
/* cfg_done */
/*第三步 : 配置mipi dsi */
/*3.1 :解析display-timing
/*3.2 :解析dsi
/*3.3 :配置dsi host(初始化 phy等)
/*3.4 :发送屏幕初始化序列
/* 配置mipi dsi */
/* drivers/video/rk32_lcdc.c */
rk_lcdc_load_screen(&panel_info);
/* 使用在lcdc_init中初始化的lcdc_dev */
struct lcdc_device *lcdc_dev = &rk32_lcdc;
struct rk_screen *screen = lcdc_dev->screen;
/* 将panel_info 过渡给rk_screen */
/* drivers/video/rockchip_fb.c */
rk_fb_vidinfo_to_screen(vid, screen);
screen->type = vid->screen_type;
...
/* mipi dsi 初始化 */
/* drivers/video/transmitter/rk32_mipi_dsi.c */
rk32_mipi_enable(vid);
struct dsi *dsi;
struct mipi_dsi_ops *ops;
struct rk_screen *screen;
struct mipi_dsi_screen *dsi_screen;
/* drivers/video/screen/lcd_mipi.c */
rk_mipi_screen_probe();
gmipi_screen = calloc(1, sizeof(struct mipi_screen)); /* 全局变量 */
/* 解析dts */
ret = rk_mipi_screen_init_dt(gmipi_screen);
struct device_node *childnode, *grandchildnode, *root;
struct mipi_dcs_cmd_ctr_list *dcs_cmd;
struct list_head *pos;
struct property *prop;
/* 将mipi_screen 清 0 , mipi_screen 是一个全局变量 */
memset(screen, 0, sizeof(*screen));
/* 链表头初始化 */
INIT_LIST_HEAD(&screen->cmdlist_head);
/* 获取mipi_dsi_init 节点 */
childnode = of_find_node_by_name(NULL, "mipi_dsi_init");
/* 获取screen_init 属性的值赋予value */
ret = of_property_read_u32(childnode, "rockchip,screen_init", &value);
/* 赋值screen_init */
screen->screen_init = value ;
/* 获取 dsi_lane 属性的值 */
ret = of_property_read_u32(childnode, "rockchip,dsi_lane", &value);
screen->dsi_lane = value;
/* 获取dsi_hs_clk 属性的值 */
ret = of_property_read_u32(childnode, "rockchip,dsi_hs_clk", &value);
screen->hs_tx_clk = value*MHz;
/* 获取mipi_dsi_num 属性的值 */
ret = of_property_read_u32(childnode, "rockchip,mipi_dsi_num", &value);
screen->mipi_dsi_num = value ;
/* 获取 mipi_power_ctr 节点 */
childnode = of_find_node_by_name(NULL, "mipi_power_ctr");
/* 获取mipi_lcd_rst 节点 */
grandchildnode = of_get_child_by_name(childnode, "mipi_lcd_rst");
/* 获取 mipi reset delay 属性的值 */
ret = of_property_read_u32(grandchildnode, "rockchip,delay", &value);
screen->lcd_rst_delay = value;
/* 获取mipi reset gpio 属性 */
gpio = of_get_named_gpio_flags(grandchildnode, "rockchip,gpios", 0, &flags);
/* 获取对应的gpio */
ret = gpio_request(gpio,"mipi_lcd_rst");
screen->lcd_rst_gpio = gpio;
screen->lcd_rst_atv_val = (flags == GPIO_ACTIVE_HIGH)? 1:0;
/* 获取mipi_lcd_en 节点 */
grandchildnode = of_get_child_by_name(childnode, "mipi_lcd_en");
/* 获取 mipi en delay 属性的值 */
ret = of_property_read_u32(grandchildnode, "rockchip,delay", &value);
screen->lcd_en_delay = value;
/* 获取mipi en gpio */
gpio = of_get_named_gpio_flags(grandchildnode, "rockchip,gpios", 0, &flags);
ret = gpio_request(gpio,"mipi_lcd_en");
screen->lcd_en_gpio = gpio;
screen->lcd_en_atv_val= (flags == GPIO_ACTIVE_HIGH)? 1:0;
/* 获取screen-on-cmds 节点 */
root= of_find_node_by_name(NULL,"screen-on-cmds");
/* cmd 节点的东西很多 需要循环依次来获取存放 */
for_each_child_of_node(root, childnode)
/* 存放cmd的结构体 */
dcs_cmd = kmalloc(sizeof(struct mipi_dcs_cmd_ctr_list), GFP_KERNEL);
/* 区分不同的cmd */
strcpy(dcs_cmd->dcs_cmd.name, childnode->name);
/* 获取一个cmd的长度 */
prop = of_find_property(childnode, "rockchip,cmd", &length);
dcs_cmd->dcs_cmd.cmd_len = length / sizeof(u32) ;
/* 根据长度依次 将cmd保存到dcs_cmd->dcs_cmd.cmds */
for(i = 0; i < (length / sizeof(u32)); i++)
dcs_cmd->dcs_cmd.cmds[i] = cmds[i];
/* 获取dsi_id */
ret = of_property_read_u32(childnode, "rockchip,dsi_id", &value);
dcs_cmd->dcs_cmd.dsi_id = value;
/* 获取cmd_type */
ret = of_property_read_u32(childnode, "rockchip,cmd_type", &value);
dcs_cmd->dcs_cmd.type = value;
/* 获取cmd_delay */
ret = of_property_read_u32(childnode, "rockchip,cmd_delay", &value);
dcs_cmd->dcs_cmd.delay = value;
/* 解析完了一个cmd 后将包含这个cmd的信息链接到cmdlist_head链表中 */
list_add_tail(&dcs_cmd->list, &screen->cmdlist_head);
/* rk32_mipi_enable */
/* 获取之前解析出来的dsi 数量 */
dsi_number = rk_mipi_get_dsi_num();
/* 有几个dsi就搞几个dsi */
for(id = 0; id < dsi_number;)
/* 为每一个dsi分配空间 */
dsi = calloc(1, sizeof(struct dsi));
/* 通过id区分不同的dsi */
dsi->dsi_id = id++;
/* 解析dsi_host 就是为了dsi的基地址 */
rk_dsi_host_parse_dt(gd->fdt_blob,dsi);
/* 找到rk32_dsi 节点 */
node = fdt_node_offset_by_compatible(blob, 0, "rockchip,rk32-dsi");
/* 确定host 的基地址 */
dsi->host.membase = (void __iomem *)(unsigned long)fdtdec_get_int(blob, node, "reg", -1);
/* 分 配 rk_screen */
screen = calloc(1, sizeof(struct rk_screen));
/* 准备填充ops */
ops = &dsi->ops;
ops->dsi = dsi;
ops->get_id = rk32_mipi_dsi_get_id,
/* 关键操作函数 发送数据 */
ops->dsi_send_packet = rk32_mipi_dsi_send_packet;
ops->dsi_read_dcs_packet = rk32_mipi_dsi_read_dcs_packet,
ops->dsi_enable_video_mode = rk32_mipi_dsi_enable_video_mode,
ops->dsi_enable_command_mode = rk32_mipi_dsi_enable_command_mode,
ops->dsi_enable_hs_clk = rk32_mipi_dsi_enable_hs_clk,
ops->dsi_is_active = rk32_mipi_dsi_is_active,
ops->dsi_is_enable= rk32_mipi_dsi_is_enable,
ops->power_up = rk32_mipi_dsi_power_up,
ops->power_down = rk32_mipi_dsi_power_down,
ops->dsi_init = rk_mipi_dsi_init,
/* 填充mipi_dsi_screen */
dsi_screen = &dsi->screen;
dsi_screen->type = screen->type = vid->screen_type;
dsi_screen->face = screen->face = vid->lcd_face;
dsi_screen->pixclock = screen->mode.pixclock = vid->real_freq;
dsi_screen->pin_den = screen->pin_den = vid->vl_oep;
。。。
dsi_screen->lcdc_id = 1;
/* 注册dsi ops */
ret = rk_mipi_dsi_probe(dsi);
/* 就是将ops放入一个全局数组中通过dsi_id管理起来 */
register_dsi_ops(dsi->dsi_id, &dsi->ops);
dsi_ops[id] = ops; /*static struct mipi_dsi_ops *dsi_ops[MAX_DSI_CHIPS] = {NULL}; 全局变量 */
/* 探测当前的dsi chip是否存在 */
ret = dsi_probe_current_chip(dsi->dsi_id);
/* 不在dis_num 的for中 */
rk32_dsi_enable();
/* 判断dsi0 的时钟是否打开 dsi0 是一个全局变量*/
if (!dsi0->clk_on) {
/* 调用之前注册到dsi 的dsi_init ops函数 ,这里将dsi0 和前面的dsi关联起来了 */
dsi_init(0, 0);
/* 这里的dsi 对应的就是id = 0 的dsi */
ops->dsi_init(ops->dsi, n);
static int rk_mipi_dsi_init(void *arg, u32 n)
struct dsi *dsi = arg;
struct mipi_dsi_screen *screen = &dsi->screen;
/* 设置各种时钟 */
dsi->phy.Tpclk = div_u64(1000000000000llu, screen->pixclock);
dsi->phy.ref_clk = 24*MHZ;
dsi->phy.sys_clk = dsi->phy.ref_clk;
dsi->phy.ddr_clk = 1500 * MHz; /* default is 1.5HGz */
decimals = dsi->phy.ref_clk;
dsi->phy.ddr_clk = dsi->phy.ref_clk / dsi->phy.prediv * dsi->phy.fbdiv;
。。。
dsi->host.video_mode = VM_BM;
mdelay(10);
/* 计算完phy的时钟后开始进行操作了 */
rk_phy_power_up(dsi);
rk32_phy_power_up(dsi);
/* 开时钟 */
rk32_mipi_dsi_clk_enable(dsi);
val = 0x80000000;//bit31~bit16
writel(val, RK3288_CRU_PHYS + 0x174); /*24M*/
writel(val, RK3288_CRU_PHYS + 0x1a0); /*pclk*/
/* 设置dsi lane */
switch(dsi->host.lane)
/* 以4根lane为例 */
rk32_dsi_set_bits(dsi, 3, n_lanes);
/* 写寄存器了 */
rk32_dsi_set_bits(dsi, 1, phy_shutdownz);
rk32_dsi_set_bits(dsi, 1, phy_rstz);
rk32_dsi_set_bits(dsi, 1, phy_enableclk);
rk32_dsi_set_bits(dsi, 1, phy_forcepll);
/* dsi 上电? */
rk32_mipi_dsi_host_power_up(dsi);
/* 禁用所有中断 */
rk32_dsi_set_bits(dsi, 0x1fffff, INT_MKS0);
rk32_dsi_set_bits(dsi, 0x3ffff, INT_MKS1);
rk32_mipi_dsi_is_enable(dsi, 1);
rk32_dsi_set_bits(dsi, enable, shutdownz);
/* 检测phy clk 是否起来了 等待了一段时间 */
while(!rk32_dsi_get_bits(dsi, phylock) && val--)
/* phy 的初始化 */
rk_phy_init(dsi);
/* phy 的初始化 */
rk32_phy_init(dsi);
/* test data 那一套东西 */
/* dsi host 的初始化 */
rk32_mipi_dsi_host_init(dsi);
struct mipi_dsi_screen *screen = &dsi->screen;
rk32_dsi_set_bits(dsi, dsi->host.lane - 1, n_lanes);
rk32_dsi_set_bits(dsi, dsi->vid, dpi_vcid);
rk32_dsi_set_bits(dsi, 1, hsync_active_low);
。。。
rk32_dsi_set_bits(dsi, dsi->host.video_mode, vid_mode_type); //burst mode
。。。
/* 根据video mode screen type 等配置寄存器 */
/* 配置一些时序 寄存器 */
rk32_dsi_set_bits(dsi, dsi->phy.Tpclk * (screen->left_margin) / dsi->phy.Ttxbyte_clk, vid_hbp_time);
rk32_dsi_set_bits(dsi, screen->y_res , vid_active_lines);
rk32_dsi_set_bits(dsi, screen->lower_margin, vid_vfp_lines);
rk32_dsi_set_bits(dsi, screen->upper_margin, vid_vbp_lines);
。。。
/* driver/video/transmitter/rk32_mipi_dsi.c rk32_dsi_enable */
rk_mipi_screen_standby(0);
/* driver/video/screen/lcd_mipi.c */
int rk_mipi_screen_standby(u8 enable)
rk_dsi_num = gmipi_screen->mipi_dsi_num;
rk_mipi_screen(); /* enable = 0 时 */
rk_dsi_num = gmipi_screen->mipi_dsi_num;
/* 这个不需要也可以前面做过了 */
rk_mipi_screen_pwr_enable(gmipi_screen);
dsi_enable_hs_clk(0,1);
dsi_enable_video_mode(0,0);
/* 通过ops调用之前注册的ops函数中的dsi enable video mode函数 enable = 0 */
ops->dsi_enable_video_mode(ops->dsi, enable);
rk32_mipi_dsi_enable_video_mode(ops->dsi,0)
/* 设置相应的寄存器 */
rk32_dsi_set_bits(dsi, !enable, cmd_video_mode);
/* 打开command mode */
dsi_enable_command_mode(0, 1);
/* 通过command mode 发送屏幕初始化序列 */
rk_mipi_screen_cmd_init(gmipi_screen);
struct list_head *screen_pos;
struct mipi_dcs_cmd_ctr_list *dcs_cmd;
/* 通过screen 中的comlist_head链表去访问各个cmd */
list_for_each(screen_pos, &screen->cmdlist_head){
/* 发送函数 */
dsi_send_packet(1, cmds, len);
ops->dsi_send_packet(ops->dsi, packet, n);
static int rk32_mipi_dsi_send_packet(void *arg, unsigned char cmds[], u32 length)
/* 发送完初始化序列后,关闭command模式 */
dsi_enable_command_mode(0,0);
/* 打开video 模式 */
dsi_enable_video_mode(0,1);
dsi_is_enable(0, 0);
rk32_mipi_dsi_is_enable(dsi,0)
rk32_dsi_set_bits(dsi, enable, shutdownz);
dsi_enable_video_mode(0, 1);
dsi_is_enable(0, 1);
/* rk32_lcdc.c rk_lcdc_load_screen */
rk32_dsi_sync();
dsi_is_enable(0, 0);
dsi_enable_video_mode(0, 1);
dsi_is_enable(0, 1);
/* 第四步: 显示logo */
/*4.1 : 获取bmp问题的位置 */
/*4.2 : 获取framebuffer的位置 */
/*4.3 : 将bmp写到framebuffer中 */
/*4.4 : 设置图层显示framebuffer的内容 */
/* /common/lcd.c lcd_init */
/* lcd_ctrl_init 完成后,准备显示logo */
lcd_clear();
/* 画logo */
static void *lcd_logo(void)
/* 绘制位图并显示*/
bitmap_plot(0, 0);
/* 获取一个bmp_logo_palette 的数据 */
uint *cmap = (uint *)bmp_logo_palette;
ushort i, j;
uchar *bmap;
uchar *fb;
ushort *fb16;
unsigned bpix = NBITS(panel_info.vl_bpix);
/* 获取一个bmp_logo_bitmap 的数据 */
bmap = &bmp_logo_bitmap[0];
/* 设置fb 指针 */
fb = (uchar *)(lcd_base);
/* */
if(!rk_bitmap_from_resource((unsigned short*)fb))
show_resource_image(file_path)
/*把位图搬运到bmp->addr处 */
load_content_data(&image, 0, image.load_addr, blocks)
/* 把bmp->addr得东西按照格式搞到fb->add */
lcd_display_bitmap_center((uint32_t)(unsigned long)image.load_addr);
lcd_display_bitmap(bmp_image, (panel_info.vl_col - width)/2,(panel_info.vl_row - height)/2);
/* 写buffer */
/* 显示 drivers/video/rockchip_fb.c */
lcd_pandispaly(&fb_info);
/* drivers/video/rk32_lcdc.c */
rk_lcdc_set_par(info, &panel_info);
/* 有多个图层 选择一个作为默认的图层去显示 */
fb_info->layer_id = lcdc_dev->dft_win;
/* 通过win0 显示 */
win0_set_par(lcdc_dev, fb_info, vid);
/* 使用这个来表示一个图层 */
struct rk_lcdc_win win;
memset(&win, 0, sizeof(struct rk_lcdc_win));
/* 计算显示时x坐标 */
win.area[0].dsp_stx = dsp_x_pos(win.mirror_en, screen, win.area);
/* 计算显示时y坐标 */
win.area[0].dsp_sty = dsp_y_pos(win.mirror_en, screen, win.area);
/* 显示效果相关计算获取对应的参数 */
rk3288_lcdc_calc_scl_fac(&win, screen);
/* 显示格式 */
win.area[0].y_vir_stride = v_RGB565_VIRWIDTH(fb_info->xvir);
/* 写寄存器,将刚计算获得的值写到相应的寄存器中去 */
rk3288_win_0_1_reg_update(lcdc_dev, &win, fb_info->layer_id);
/* 设置将win0 的数据获取入口设置为framebuffer 的位置 */
lcdc_writel(lcdc_dev, WIN0_YRGB_MST, fb_info->yaddr);
lcdc_writel(lcdc_dev, BCSH_BCS, 0xd0010000);
。。。
lcdc_cfg_done(lcdc_dev);
/* bitmap_plot(0, 0); */
/* 刷新caches */
lcd_sync();
/* 第五步: 开背光 */
/* board/rockchip/common/rkboot/fastboot.c */
rk_pwm_bl_config(-1);
/* drives/video/backlight/pwm_bl.c */
int rk_pwm_bl_config(int brightness)
/* 设置pwm */
2、十二问
问题1、GD这个全局变量是什么?
/* include/asm-arm/global_data.h */
typedef struct global_data {
bd_t *bd;
unsigned long flags;
unsigned long baudrate;
unsigned long have_console; /* serial_init() was called */
unsigned long reloc_off; /* Relocation Offset */
unsigned long env_addr; /* Address of Environment struct */
unsigned long env_valid; /* Checksum of Environment valid? */
unsigned long fb_base; /* base address of frame buffer */
#ifdef CONFIG_VFD
unsigned char vfd_type; /* display type */
#endif
#if 0
unsigned long cpu_clk; /* CPU clock in Hz! */
unsigned long bus_clk;
unsigned long ram_size; /* RAM size */
unsigned long reset_status; /* reset status register at boot */
#endif
void **jt; /* jump table */
}gd_t;
GD全局变量是uboot启动的最开始就在ram中申请创建的一个结构体,它的生命周期贯穿uboot启动的整个阶段,所以可以用来传递大量信息。
uboot使用gd来传递信息是因为,有些时候uboot可能是在一些只读类存储器上运行的,在uboot被重定位到ram之前,是无法写入数据的,把gd放在ram中,就可以对齐进行读写保存需要的信息。
在gd初始化的时候,会将gd的地址放在r9寄存器中,后面需要使用到gd时直接访问r9即可。
问题2、gd->fb_base 是何时赋值为何?
在board_init_f 的list中有一个reserve_lcd。这个函数中对gd->fb_base进行了赋值 。
static int setup_fdt(void)
{
#ifdef CONFIG_OF_CONTROL
# ifdef CONFIG_OF_EMBED
/* 使用CONFIG_OF_EMBED 的方式,dtb集成到了uboot的bin文件中,通过_dtb_dt_begin 来获取dtb的地址 */
/* Get a pointer to the FDT */
gd->fdt_blob = __dtb_dt_begin;
# elif defined CONFIG_OF_SEPARATE
/* FDT is at end of image */
/* 使用CONFIG_OF_SEPARATE 的方式,此时dtb 是追加在uboot的bin文件后面的,所以通过_end符号来获取dtb的地址 */
gd->fdt_blob = (ulong *)&_end;
# elif defined(CONFIG_OF_HOSTFILE)
if (read_fdt_from_file()) {
puts("Failed to read control FDT\n");
return -1;
}
# endif
/* Allow the early environment to override the fdt address */
/* 也可以通过环境变量“fdtcontroladdr 来指定fdt的地址 */
gd->fdt_blob = (void *)getenv_ulong("fdtcontroladdr", 16,
(uintptr_t)gd->fdt_blob);
#endif
return 0;
}
问题3、gd->fdt_blob 是做什么的,何时初始化的?
gd->fdt_blob 保存的是fdt的地址,是由board_init_f 的list中的setup_fdt最先获取,注意:设备树编译生成的dtb文件并不包含在uboot.bin当中。
static int setup_fdt(void)
{
#ifdef CONFIG_OF_CONTROL
# ifdef CONFIG_OF_EMBED
/* 使用CONFIG_OF_EMBED 的方式,dtb集成到了uboot的bin文件中,通过_dtb_dt_begin 来获取dtb的地址 */
/* Get a pointer to the FDT */
gd->fdt_blob = __dtb_dt_begin;
# elif defined CONFIG_OF_SEPARATE
/* FDT is at end of image */
/* 使用CONFIG_OF_SEPARATE 的方式,此时dtb 是追加在uboot的bin文件后面的,所以通过_end符号来获取dtb的地址 */
gd->fdt_blob = (ulong *)&_end;
# elif defined(CONFIG_OF_HOSTFILE)
if (read_fdt_from_file()) {
puts("Failed to read control FDT\n");
return -1;
}
# endif
/* Allow the early environment to override the fdt address */
/* 也可以通过环境变量“fdtcontroladdr 来指定fdt的地址 */
gd->fdt_blob = (void *)getenv_ulong("fdtcontroladdr", 16,
(uintptr_t)gd->fdt_blob);
#endif
return 0;
}
由setup_fdt 获取fdt 最开始的地址后,我们还需要调用reserve_fdt来为其保留空间:
static int reserve_fdt(void)
{
/*
* If the device tree is sitting immediate above our image then we
* must relocate it. If it is embedded in the data section, then it
* will be relocated with other data.
*/
if (gd->fdt_blob) {
gd->fdt_size = ALIGN(fdt_totalsize(gd->fdt_blob) + 0x1000, 32);
gd->start_addr_sp -= gd->fdt_size;
gd->new_fdt = map_sysmem(gd->start_addr_sp, gd->fdt_size);
debug("Reserving %lu Bytes for FDT at: %08lx\n",
gd->fdt_size, gd->start_addr_sp);
}
return 0;
}
最后通过reloc_fdt来完成fdt的重定位并更新fdt的地址:
static int reloc_fdt(void)
{
if (gd->new_fdt) {
memcpy(gd->new_fdt, gd->fdt_blob, gd->fdt_size);
gd->fdt_blob = gd->new_fdt;
}
return 0;
}
问题4、 struct mipi_screen 中的cmdlist_head 链表的作用是什么?
struct list_head cmdlist_head; 在rk_mipi_screen_init_dt(gmipi_screen); 中调用 INIT_LIST_HEAD(&screen->cmdlist_head);完成了初始化,然后在后面解析cmd命令时,每解析完一条cmd(完成struct mipi_dcs_cmd_ctr_list 结构体的填充后)调用 list_add_tail(&dcs_cmd->list, &screen->cmdlist_head); 将struct mipi_dcs_cmd_ctr_list 中的dcs_cmd ->链接到cmdlist_head 中,就这样每解析了一条cmd就链接进去一条。最终实现了通过struct mipi_screen 结构体可以访问到所有cmd的功能。方便通过mipi 发送cmd去初始化屏幕。
问题5、rk32_mipi_enable 中的struct rk_screen *screen 与rk_mipi_screen_probe()中的struct mipi_screen gmipi_screen 及struct mipi_dsi_screen *dsi_screen;的关系是什么?
struct rk_screen *screen 是rk对支持的屏幕的一个集合,这些屏幕包括rgb屏幕、mipi屏幕、lvds屏幕等。当使用其中一种屏幕时有些成员时没有用的。其主要用于lcdc控制器中图层相关的寄存器配置上。而struct mipi_screen 是针对mipi屏幕的特点构成的一个集合,主要包含了mipi 屏幕使用dsi 通道数目、使能和复位的gpio口控制、需要发送的屏幕初始化序列。主要用来初始化屏幕。struct mipi_dsi_screen 是 struct dsi 中成员,在mipi dis host 初始化时会使用struct mipi_dsi_screen 成员的的值去配置相应的寄存器。如:vid_hline_time、vid_hbp_time、vid_hsa_time、vid_active_lines、vid_vfp_lines、vid_vbp_lines、vid_vsa_lines等。
总结:struct rk_screen *screen 用于lcdc控制器的寄存器配置、struct mipi_screen gmipi_screen 用于mipi 屏幕的初始化、struct mipi_dsi_screen *dsi_screen 用于MIPI DSI相关寄存器的配置。
问题6、 在rk32_mipi_enable(vid); 中会根据dsi_num去配置所有的dsi,dsi_num是什么含义?
查看rk3288 Block Diagram 可知,rk3288中集成了两个MIPI DSi PHY :dsihost0: mipi@ff960000 \dsihost1: mipi@ff964000。在初始化的时候需要将这两个dsi phy都初始化了,所以出现了dsi_num的参数。方便对一个或两个dsi phy进行访问控制。
问题7、rk32_dsi_enable 中出现的dsi0 和 rk32_mipi_enable 中初始化填充的dsi有什么关联?
没什么用,可以用自己建立的dsi去代替它
问题8、解析设备树获取节点属性信息时,有很多define值,他们定义在什么地方?
解析设备树时所有的信息都是从设备树文件中得到,打开查看设备树文件可以看到它也是有#include 头文件的。其中大部分头文件都是在kernel/arch/arm/boot/dts/include/dt-bindings/。。。 中,这些头文件就定义了绝大部分使用到的值,如:
./rkfb/rk_fb.h:#define SCREEN_MIPI 7
问题9、Uboot启动阶段一定会使能mipi dsi 的command mode去发送屏幕初始化序列么?
根据不同的lcd屏幕的spec 决定是否需要初始化,对于需要初始化的lcd屏幕就调用相应的接口发送初始化cmd进行初始化。
问题10、代码中的standby的含义是什么?
待机模式,当需要进入待机模式时调用rk_mipi_screen_standby(1),会通过dcs发送display_off命令给lcd。并进入sleep 模式。
最后关掉power。
退出standby模式时会首先打开dsi power。使能时钟,使能command模式,发送退出sleep mode 命令。发送display on命令,再关闭command模式。打开video mode。
问题11、bmp_logo_palette[] 是什么?
bmp_logo_palette 放在bmp_logo_data.h中,是一个数组。而我们在编译uboot的过程中可以看到以下打印信息:
tools/bmp_logo --gen-info /home/kai/work/rk3288/x3288_lollipop/u-boot/tools/logos/rockchip.bmp > /home/kai/work/rk3288/x3288_lollipop/u-boot/include/bmp_logo.h
tools/bmp_logo --gen-data /home/kai/work/rk3288/x3288_lollipop/u-boot/tools/logos/rockchip.bmp > /home/kai/work/rk3288/x3288_lollipop/u-boot/include/bmp_logo_data.h
可以看出bmp_logo_data.h 是由/home/kai/work/rk3288/x3288_lollipop/u-boot/tools/logos/rockchip.bmp 这个位图文件经由tools/bmp_logo 处理产生的
起始就是把一个.bmp位图转换成数组数据放在了.h文件中,方便程序去调用显示。
问题12、如何获取bmp?
bmp 可以通过上面bmp_logo_palette数组中的数据获得,也可以在一开始就在ram中保留一个区域,然后将img中的位图拷贝过去获得
标签:mipi,Uboot,screen,fdt,dsi,RK3288,rk32,Display,blob 来源: https://blog.csdn.net/qq_40629752/article/details/114151308