ACTIONS S900 uboot阶段显示模块分析!

首先,显示子系统的相关代码在\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,
};
结构体是灵魂,这是肯定要贴的。控制器注册函数 又回到ctrl.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的用得顺手,这可能就是差距吧。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值