ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

RK3288 Uboot Display 驱动详解

2021-02-26 20:32:20  阅读:262  来源: 互联网

标签:mipi Uboot screen fdt dsi RK3288 rk32 Display blob


怀揣着十几个疑问整理了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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有