首先,顯示子系統的相關代碼在/u-boot/drivers/video/owl目錄下,分析從owl_fb.c開始。
void *video_hw_init(void){ owl_pwm_init(gd->fdt_blob); owl_dss_init(gd->fdt_blob); if (owl_fb_init(&g_owl_fb) < 0) return NULL; owl_fb_display_on(&g_owl_fb); return &g_owl_fb.gd;}video_hw_init無法追蹤到上一級,應該是直接在匯編中特定的地址位置來調用。owl_pwm_init(gd->fdt_blob)先點背光,這里不深入分析。
owl_dss_init(gd->fdt_blob)進入顯示子系統初始化函數:
void owl_dss_init(const void *blob){ /* * core init */ owl_ctrl_init(); owl_panel_init(); /* * de_xxx init */#ifdef CONFIG_VIDEO_OWL_DE_S900 owl_de_s900_init(blob);#endif /* * controllers init */#ifdef CONFIG_VIDEO_OWL_DSI owl_dsic_init(blob);#endif /* * panels init */#ifdef CONFIG_VIDEO_OWL_DSI owl_panel_ls055r3sx01_init(blob);#endif}上面代碼去掉了一些兼容性的代碼,只針對S900平臺mipi dsi的屏來分析。這部分代碼結構還是比較清晰的,分四部分:
第一歩:owl_ctrl_init在ctrl.c文件中,初始化控制器,但還不是具體使用到的控制器,例如dsic控制器用于mipi dsi,這里只是相對于一個所有控制器的管理中心。
owl_panel_init在panel.c文件中,這里是屏終端管理中心。整個這一部分相當于屏和對應控制器的管理中心。
第二歩:owl_de_s900_init,顯示引擎初始化,在de_s900.c文件中。
int owl_de_s900_init(const void *blob){ …… owl_de_s9009.blob = blob; //數據和節點賦值給s900顯示引擎 owl_de_s9009.node = node; owl_de_register(&owl_de_s9009); //注冊s900 顯示引擎 ……}這里涉及到顯示引擎的管理中心de.c這個文件,核心是owl_de_register引擎注冊函數:int owl_de_register(struct owl_de_device *de){ debug("%s/n", __func__); int tmp, i; if (de == NULL) { //注冊的顯示引擎不能為空 error("de is NULL/n"); return -1; } if (cur_de != NULL) { //不能同時注冊多個顯示引擎 error("another de is already registered/n"); return -1; } cur_de = de; cur_de->base = fdtdec_get_addr(cur_de->blob, cur_de->node, "reg"); if (cur_de->base == FDT_ADDR_T_NONE) { error("Cannot find reg address/n"); return -1; } //獲取顯示引擎的寄存器地址 debug("%s: base is 0x%llx/n", __func__, cur_de->base); /* init de gamma state, parse 'gamma_adjust_needed' from DTS */ tmp = fdtdec_get_int(cur_de->blob, cur_de->node, "gamma_adjust_needed", 0); for (i = 0; i < cur_de->num_paths; i++) cur_de->paths[i].info.gamma_adjust_needed = tmp; /* result check, TODO */ if (cur_de->ops && cur_de->ops->power_on) cur_de->ops->power_on(cur_de); // 顯示引擎上電 /* result check, TODO */ if (cur_de->ops && cur_de->ops->init) cur_de->ops->init(cur_de); //顯示引擎初始化 return 0;}通過de.c引擎管理中心的調用,實際執行代碼在de_s900.c中(這里就是函數指針的魅力,提升代碼的層次性)。下面是s900 de的結構體:static struct owl_de_device owl_de_s9009 = { .hw_id = DE_HW_ID_S900, .num_paths = 2, //支持兩個通路同時輸出,lcd和 .paths = de_s900_paths, .num_videos = 2, .videos = de_s900_videos, .ops = &de_s900_device_ops,};static struct owl_de_path de_s900_paths[] = { { .id = 0, .name = "digit", .supported_displays = OWL_DISPLAY_TYPE_HDMI, .ops = &de_s900_path_ops, }, { .id = 1, .name = "lcd", .supported_displays = OWL_DISPLAY_TYPE_LCD | OWL_DISPLAY_TYPE_DSI | OWL_DISPLAY_TYPE_EDP | OWL_DISPLAY_TYPE_DUMMY, .ops = &de_s900_path_ops, },};static struct owl_de_video de_s900_videos[] = { { .id = 0, .name = "video0", .supported_colors = ATM9009_SUPPORTED_COLORS, .ops = &de_s900_video_ops, }, { .id = 1, .name = "video1", .supported_colors = ATM9009_SUPPORTED_COLORS, .ops = &de_s900_video_ops, },};static struct owl_de_device_ops de_s900_device_ops = { .power_on = de_s900_device_power_on, .init = de_s900_device_init, .dump_regs = de_s900_device_dump_regs,};static struct owl_de_path_ops de_s900_path_ops = { .enable = de_s900_path_enable, .apply_info = de_s900_path_apply_info, .set_go = de_s900_path_set_go, .set_gamma_table = de_s900_path_set_gamma_table, .get_gamma_table = de_s900_path_get_gamma_table, .gamma_enable = de_s900_path_gamma_enable,};static struct owl_de_video_ops de_s900_video_ops = { .enable = de_s900_video_enable, .apply_info = de_s900_video_apply_info,};這段結構體代碼有點長,但是很關鍵,所以全貼出來了。由de管理中心可知,首先是s900 de 上電(這里不分析,不然后面收不回來),然后初始化。static int de_s900_device_init(struct owl_de_device *de){ uint32_t val; val = readl(SHARESRAM_CTL); /* share mem */ val |= 0x3; /* tshi, ebox hdmi use 1 bit, pad edp use 0 bit. */ writel(val, SHARESRAM_CTL); ……}特意把初始化貼出來,是因為發現一個問題 val |= 0x3; 這里初始化的時候,寄存器同時打開了hdmi和mipi/edp,像這種地方要特意找很難找到,所以分析問題的時候,還是要思路清晰,系統的分析,這部分理論上方案公司是不需要修改的。第三歩:owl_dsic_init(blob); 具體控制器的初始化,在dsic.c文件中。
int owl_dsic_init(const void *blob){ …… dsic->dma_channel = owl_dma_request(); if (!dsic->dma_channel) { error("%s, owl_dma_request failed!/n", __func__); return -1; }//DMA通道申請,傳送長包的時候需要用到 dsic->base = fdtdec_get_addr(blob, node, "reg"); if (dsic->base == FDT_ADDR_T_NONE) { error("Cannot find dsic reg address/n"); return -1; } debug("%s: base is 0x%llx/n", __func__, dsic->base); ret = dsic_parse_config(blob, node, dsic); if (ret < 0) goto err_parse_config; dsic->ctrl = &owl_dsi_ctrl; owl_ctrl_set_drvdata(&owl_dsi_ctrl, dsic); ret = owl_ctrl_register(&owl_dsi_ctrl); if (ret < 0) goto err_ctrl_register; return 0; ……}關鍵部分為注冊dsi控制器owl_ctrl_register(&owl_dsi_ctrl);struct owl_display_ctrl_ops owl_dsi_ctrl_ops = { .enable = owl_dsic_enable, .disable = owl_dsic_disable, .power_on = owl_dsic_power_on, .power_off = owl_dsic_power_off, .aux_read = owl_dsic_aux_read, .aux_write = owl_dsic_aux_write,};static struct owl_display_ctrl owl_dsi_ctrl = { .name = "dsi_ctrl", .type = OWL_DISPLAY_TYPE_DSI, .ops = &owl_dsi_ctrl_ops,};結構體是靈魂,這是肯定要貼的??刂破髯院瘮涤只氐絚trl.c控制器管理中心。這部分沒有特別的,可以注冊4個控制器。目前分析的代碼中由于edp的宏沒關,所有注冊了2個控制器,dsic和edpc。
第四歩:owl_panel_ls055r3sx01_init(blob); 具體屏驅動的初始化。
屏驅動這部分就是大家最熟悉的了,簡單介紹一下:
在init部分注冊屏驅動到panel.c 屏終端管理中心,owl_panel_register(&owl_panel_mipi);
struct owl_panel_ops owl_panel_ls055r3sx01_ops = { .power_on = panel_ls055r3sx01_power_on, .power_off = panel_ls055r3sx01_power_off, .enable = panel_ls055r3sx01_enable, .disable = panel_ls055r3sx01_disable,};static struct owl_panel owl_panel_mipi = { .desc = { .name = "mipi_panel", .type = OWL_DISPLAY_TYPE_DSI, .ops = &owl_panel_ls055r3sx01_ops, },};到這里,owl_dss_init顯示子系統的初始化函數分析完畢!通過分析這個過程,把相關的東西都有涉及進來了。后面的分析,也是圍繞這些東西,主要就是疏通整個工作流程了。第二部分:owl_fb_init(&g_owl_fb),framebuffer的初始化。
static int owl_fb_init(struct owl_fb *fb){ debug("%s/n", __func__); /* * PRimary panel */ fb->panel = owl_panel_get_primary_panel(); if (fb->panel == NULL) { error("no primary panel/n"); return -ENODEV; } // 首先獲取主屏,在dts中配置 debug("%s: primary panel type is %d/n", __func__, owl_panel_get_type(fb->panel)); fb->path = owl_de_path_get_by_type(owl_panel_get_type(fb->panel)); if (fb->path == NULL) { error("can not get de path for primary panel/n"); return -EINVAL; } //根據panel類型(在屏驅動獲?。﹣矶@示引擎的通道 fb->video = owl_de_video_get_by_id(0); if (fb->video == NULL) { error("can not get de video for primary panel/n"); return -EINVAL; }// 默認0為主通道,獲取顯示引擎的video __owl_fb_init(fb, true); /* * second panel */ fb->second_panel = owl_panel_get_second_panel(); fb->second_path = owl_de_path_get_by_type(owl_panel_get_type(fb->second_panel));#if defined(CONFIG_VIDEO_OWL_DE_S700) && !defined(CONFIG_VIDEO_OWL_DE_S700_OTT) fb->second_video = owl_de_video_get_by_id(3);#else fb->second_video = owl_de_video_get_by_id(1);#endif if (fb->second_panel != NULL && fb->second_path != NULL && fb->second_video != NULL) { __owl_fb_init(fb, false); } else { debug("no valid second display device/n"); fb->second_panel = NULL; } /* * fill to graphic_device */ owl_panel_get_draw_size(fb->panel, (int *)&fb->gd.winSizeX, (int *)&fb->gd.winSizeY); fb->gd.gdfIndex = owl_color_mode_to_gdf_mode(OWLFB_COLOR_MODE); fb->gd.gdfBytesPP = owl_dss_get_color_bpp(OWLFB_COLOR_MODE) / 8; fb->gd.frameAdrs = OWLFB_BUF_ADDR; debug("%s: pGD info---/n", __func__); debug("%dx%d, gdfIndex %d, gdfBytesPP %d, frameAdrs %x/n", fb->gd.winSizeX, fb->gd.winSizeY, fb->gd.gdfIndex, fb->gd.gdfBytesPP, fb->gd.frameAdrs); return 0;}static void __owl_fb_init(struct owl_fb *fb, bool is_primary){ struct owl_panel *panel; struct owl_de_path *path; struct owl_de_video *video; struct owl_de_path_info p_info; struct owl_de_video_info v_info; debug("%s: is_primary %d/n", __func__, is_primary); if (is_primary) { panel = fb->panel; path = fb->path; video = fb->video; } else { panel = fb->second_panel; path = fb->second_path; video = fb->second_video; } owl_de_video_set_path(video, path); //根據framebuffer的path來設置DE的video path /* * init path info */ owl_de_path_get_info(path, &p_info); //初始化通道信息 p_info.type = owl_panel_get_type(panel); //獲取屏幕類型 owl_panel_get_resolution(panel, (int *)&p_info.width, (int *)&p_info.height); //獲取屏幕的分辨率 p_info.vmode = owl_panel_get_vmode(panel); switch (owl_panel_get_bpp(panel)) { case 16: p_info.dither_mode = DITHER_24_TO_16; break; case 18: p_info.dither_mode = DITHER_24_TO_18; break; default: p_info.dither_mode = DITHER_DISABLE; break; } owl_panel_get_gamma(panel, &p_info.gamma_r_val, &p_info.gamma_g_val, &p_info.gamma_b_val); owl_de_path_set_info(path, &p_info); //設置DE通道信息 /* * init video info */ owl_de_video_get_info(video, &v_info); //初始化video信息 v_info.color_mode = OWLFB_COLOR_MODE; v_info.blending = OWL_BLENDING_NONE; v_info.alpha = 0xff; v_info.xoff = 0; v_info.yoff = 0; /* input size is equal to primary panel's draw size */ owl_panel_get_draw_size(fb->panel, (int *)&v_info.width, (int *)&v_info.height); v_info.pos_x = 0; v_info.pos_y = 0; /* output size is equal to panel's resolution */ owl_panel_get_resolution(panel, (int *)&v_info.out_width, (int *)&v_info.out_height); v_info.addr[0] = OWLFB_BUF_ADDR; v_info.offset[0] = 0; v_info.pitch[0] = (owl_dss_get_color_bpp(v_info.color_mode) / 8) * v_info.width; owl_de_video_set_info(video, &v_info); //設置video信息}從framebuffer的init過程,我們可以清楚的了解到顯示子系統的一個工作流程:framebuffer——>de——>path——>video——>panel
細節太多繁雜,不過大部分修改的內容可能都在其中,在這篇文章不發散,不好發散,只能針對問題點分析,我們這篇文章的主要目的是搞清楚流程,然后學習它的設計思路。
第三部分:顯示部分owl_fb_display_on(&g_owl_fb);
static void owl_fb_display_on(struct owl_fb *fb){ debug("%s/n", __func__); debug("%s(primay panel)/n", __func__); owl_panel_enable(fb->panel); owl_de_video_enable(fb->video, true); owl_de_path_enable(fb->path, true); owl_de_path_set_go(fb->path); if (fb->second_panel != NULL) { debug("%s(secondary panel)/n", __func__); owl_panel_enable(fb->second_panel); owl_de_video_enable(fb->second_video, true); owl_de_path_enable(fb->second_path, true); owl_de_path_set_go(fb->second_path); }}主要也就是使能panel video path,使能的順序剛好和數據流的順序相反。這里不知道順序有沒有影響,沒驗證過。OVER,總的來說,這種設計符合我的思維邏輯,太高級了我看不懂,哈哈。
MTK的確實復雜些,看得沒這么順利,但是MTK的用得順手,這可能就是差距吧。
新聞熱點
疑難解答