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项目总结(一)


下一篇:研发课堂丨i.MX8M核心板开发小贴士