高通平台lk下调屏小结

本文介绍在LK环境下调试屏幕的方法及代码流程。以msm8996平台为例,详细解析了LK启动过程中的屏幕初始化步骤,并说明如何适配不同的屏幕。

    在lk下调试屏幕,可先了解下lk的代码流程,方便接下来的调试。这里以msm8996平台为例,描述lk的代码结构。

bootable/bootloader/lk/kernel/maic.c

/* called from crt0.S */
void kmain(void) __NO_RETURN __EXTERNALLY_VISIBLE;
void kmain(void)
{
	// get us into some sort of thread context
	thread_init_early();

	// early arch stuff
	arch_early_init();

	// do any super early platform initialization
	platform_early_init();

	// do any super early target initialization
	target_early_init();

	dprintf(INFO, "welcome to lk\n\n");
	bs_set_timestamp(BS_BL_START);

	// deal with any static constructors
	dprintf(SPEW, "calling constructors\n");
	call_constructors();

	// bring up the kernel heap
	dprintf(SPEW, "initializing heap\n");
	heap_init();

	__stack_chk_guard_setup();

	// initialize the threading system
	dprintf(SPEW, "initializing threads\n");
	thread_init();

	// initialize the dpc system
	dprintf(SPEW, "initializing dpc\n");
	dpc_init();

	// initialize kernel timers
	dprintf(SPEW, "initializing timers\n");
	timer_init();

#if (!ENABLE_NANDWRITE)
	// create a thread to complete system initialization
	dprintf(SPEW, "creating bootstrap completion thread\n");
	thread_resume(thread_create("bootstrap2", &bootstrap2, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));

	// enable interrupts
	exit_critical_section();

	// become the idle thread
	thread_become_idle();
#else
        bootstrap_nandwrite();
#endif
}

bootstrap2

static int bootstrap2(void *arg)
{
	dprintf(SPEW, "top of bootstrap2()\n");

	arch_init();

	// XXX put this somewhere else
#if WITH_LIB_BIO
	bio_init();
#endif
#if WITH_LIB_FS
	fs_init();
#endif

	// initialize the rest of the platform
	dprintf(SPEW, "initializing platform\n");
	platform_init();

	// initialize the target
	dprintf(SPEW, "initializing target\n");
	target_init();

	dprintf(SPEW, "calling apps_init()\n");
	apps_init();

	return 0;
}

apps_init中调用了bootable/bootloader/lk/app/aboot/aboot.c

void aboot_init(const struct app_descriptor *app)
{
	unsigned reboot_mode = 0;

	/* Setup page size information for nv storage */
	if (target_is_emmc_boot())
	{
		page_size = mmc_page_size();
		page_mask = page_size - 1;
	}
	else
	{
		page_size = flash_page_size();
		page_mask = page_size - 1;
	}

	ASSERT((MEMBASE + MEMSIZE) > MEMBASE);

	read_device_info(&device);
	read_allow_oem_unlock(&device);

	/* Display splash screen if enabled */
#if DISPLAY_SPLASH_SCREEN
#if NO_ALARM_DISPLAY
	if (!check_alarm_boot()) {
#endif
		dprintf(SPEW, "Display Init: Start\n");
#if DISPLAY_HDMI_PRIMARY
	if (!strlen(device.display_panel))
		strlcpy(device.display_panel, DISPLAY_PANEL_HDMI,
			sizeof(device.display_panel));
#endif
#if ENABLE_WBC
		/* Wait if the display shutdown is in progress */
		while(pm_app_display_shutdown_in_prgs());
		if (!pm_appsbl_display_init_done())
			target_display_init(device.display_panel);
		else
			display_image_on_screen();
#else
		target_display_init(device.display_panel);
#endif
		dprintf(SPEW, "Display Init: Done\n");
#if NO_ALARM_DISPLAY
	}
#endif
#endif

	target_serialno((unsigned char *) sn_buf);
	dprintf(SPEW,"serial number: %s\n",sn_buf);

	memset(display_panel_buf, '\0', MAX_PANEL_BUF_SIZE);

	/*
	 * Check power off reason if user force reset,
	 * if yes phone will do normal boot.
	 */
	if (is_user_force_reset())
		goto normal_boot;

	/* Check if we should do something other than booting up */
	if (keys_get_state(KEY_VOLUMEUP) && keys_get_state(KEY_VOLUMEDOWN))
	{
		dprintf(ALWAYS,"dload mode key sequence detected\n");
		if (set_download_mode(EMERGENCY_DLOAD))
		{
			dprintf(CRITICAL,"dload mode not supported by target\n");
		}
		else
		{
			reboot_device(DLOAD);
			dprintf(CRITICAL,"Failed to reboot into dload mode\n");
		}
		boot_into_fastboot = true;
	}
	if (!boot_into_fastboot)
	{
		if (keys_get_state(KEY_HOME) || keys_get_state(KEY_VOLUMEUP))
			boot_into_recovery = 1;
		if (!boot_into_recovery &&
			(keys_get_state(KEY_BACK) || keys_get_state(KEY_VOLUMEDOWN)))
			boot_into_fastboot = true;
	}
	#if NO_KEYPAD_DRIVER
	if (fastboot_trigger())
		boot_into_fastboot = true;
	#endif

#if USE_PON_REBOOT_REG
	reboot_mode = check_hard_reboot_mode();
#else
	reboot_mode = check_reboot_mode();
#endif
	if (reboot_mode == RECOVERY_MODE)
	{
		boot_into_recovery = 1;
	}
	else if(reboot_mode == FASTBOOT_MODE)
	{
		boot_into_fastboot = true;
	}
	else if(reboot_mode == ALARM_BOOT)
	{
		boot_reason_alarm = true;
	}
#if VERIFIED_BOOT
#if !VBOOT_MOTA
	else if (reboot_mode == DM_VERITY_ENFORCING)
	{
		device.verity_mode = 1;
		write_device_info(&device);
	}
	else if (reboot_mode == DM_VERITY_LOGGING)
	{
		device.verity_mode = 0;
		write_device_info(&device);
	}
	else if (reboot_mode == DM_VERITY_KEYSCLEAR)
	{
		if(send_delete_keys_to_tz())
			ASSERT(0);
	}
#endif
#endif

normal_boot:
	if (!boot_into_fastboot)
	{
		if (target_is_emmc_boot())
		{
			if(emmc_recovery_init())
				dprintf(ALWAYS,"error in emmc_recovery_init\n");
			if(target_use_signed_kernel())
			{
				if((device.is_unlocked) || (device.is_tampered))
				{
				#ifdef TZ_TAMPER_FUSE
					set_tamper_fuse_cmd();
				#endif
				#if USE_PCOM_SECBOOT
					set_tamper_flag(device.is_tampered);
				#endif
				}
			}

			boot_linux_from_mmc();
		}
		else
		{
			recovery_init();
	#if USE_PCOM_SECBOOT
		if((device.is_unlocked) || (device.is_tampered))
			set_tamper_flag(device.is_tampered);
	#endif
			boot_linux_from_flash();
		}
		dprintf(CRITICAL, "ERROR: Could not do normal boot. Reverting "
			"to fastboot mode.\n");
	}

	/* We are here means regular boot did not happen. Start fastboot. */

	/* register aboot specific fastboot commands */
	aboot_fastboot_register_commands();

	/* dump partition table for debug info */
	partition_dump();

	/* initialize and start fastboot */
	fastboot_init(target_get_scratch_address(), target_get_max_flash_size());
#if FBCON_DISPLAY_MSG
	display_fastboot_menu_thread();
#endif
}
重点关注下target_display_init(device.display_panel),这里进行屏幕的初始化。

bootable/bootloader/lk/target/msm8996/target_display.c

void target_display_init(const char *panel_name)
{
	struct oem_panel_data oem;

	set_panel_cmd_string(panel_name);
	oem = mdss_dsi_get_oem_data();
	if (!strcmp(oem.panel, NO_PANEL_CONFIG)
		|| !strcmp(oem.panel, SIM_VIDEO_PANEL)
		|| !strcmp(oem.panel, SIM_DUALDSI_VIDEO_PANEL)
		|| !strcmp(oem.panel, SIM_CMD_PANEL)
		|| !strcmp(oem.panel, SIM_DUALDSI_CMD_PANEL)
		|| oem.skip) {
		dprintf(INFO, "Selected panel: %s\nSkip panel configuration\n",
			oem.panel);
		return;
	} else if ((!strcmp(oem.panel, HDMI_PANEL_NAME)) || \
		((!strlen(oem.panel)) && ((board_hardware_id() == HW_PLATFORM_SBC) ||
		(platform_is_apq8096_mediabox())))) {
		dprintf(INFO, "%s: HDMI is primary\n", __func__);
		mdss_hdmi_display_init(MDP_REV_50, (void *) HDMI_FB_ADDR);
		return;
	}

	if (gcdb_display_init(oem.panel, MDP_REV_50, (void *)MIPI_FB_ADDR)) {
		target_force_cont_splash_disable(true);
		msm_display_off();
	}

	if (!oem.cont_splash) {
		dprintf(INFO, "Forcing continuous splash disable\n");
		target_force_cont_splash_disable(true);
	}
}
gcdb_display_init调用pan_type = oem_panel_select(panel_name, &panelstruct, &(panel.panel_info), &dsi_video_mode_phy_db),选择具体的屏幕

int gcdb_display_init(const char *panel_name, uint32_t rev, void *base)
{
	int ret = NO_ERROR;
	int pan_type;

	dsi_video_mode_phy_db.pll_type = DSI_PLL_TYPE_28NM;
	pan_type = oem_panel_select(panel_name, &panelstruct, &(panel.panel_info),
				 &dsi_video_mode_phy_db);

	if (pan_type == PANEL_TYPE_DSI) {
		if (update_dsi_display_config())
			goto error_gcdb_display_init;
		target_dsi_phy_config(&dsi_video_mode_phy_db);
		mdss_dsi_check_swap_status();
		mdss_dsi_set_pll_src();
		if (dsi_panel_init(&(panel.panel_info), &panelstruct)) {
			dprintf(CRITICAL, "DSI panel init failed!\n");
			ret = ERROR;
			goto error_gcdb_display_init;
		}

		panel.panel_info.mipi.mdss_dsi_phy_db = &dsi_video_mode_phy_db;
		panel.pll_clk_func = mdss_dsi_panel_clock;
		panel.dfps_func = mdss_dsi_mipi_dfps_config;
		panel.power_func = mdss_dsi_panel_power;
		panel.pre_init_func = mdss_dsi_panel_pre_init;
		panel.bl_func = mdss_dsi_bl_enable;
		panel.dsi2HDMI_config = mdss_dsi2HDMI_config;
		/*
		 * If dfps enabled, reserve fb memory to store pll
		 * codes and pass pll codes values to kernel.
		 */
		if (panel.panel_info.dfps.panel_dfps.enabled) {
			panel.panel_info.dfps.dfps_fb_base = base;
			base += DFPS_PLL_CODES_SIZE;
			dprintf(SPEW, "dfps base=0x%p,d, fb_base=0x%p!\n",
					panel.panel_info.dfps.dfps_fb_base, base);
		}

		panel.fb.base = base;
		panel.fb.width =  panel.panel_info.xres;
		panel.fb.height =  panel.panel_info.yres;
		panel.fb.stride =  panel.panel_info.xres;
		panel.fb.bpp =  panel.panel_info.bpp;
		panel.fb.format = panel.panel_info.mipi.dst_format;
	} else if (pan_type == PANEL_TYPE_EDP) {
		mdss_edp_panel_init(&(panel.panel_info));
		/* prepare func is set up at edp_panel_init */
                panel.clk_func = mdss_edp_panel_clock;
                panel.power_func = mdss_edp_panel_power;
		panel.bl_func = mdss_edp_bl_enable;
                panel.fb.format = FB_FORMAT_RGB888;
	} else {
		dprintf(CRITICAL, "Target panel init not found!\n");
		ret = ERR_NOT_SUPPORTED;
		goto error_gcdb_display_init;
	}

	panel.fb.base = base;
	panel.mdp_rev = rev;

	ret = msm_display_init(&panel);

error_gcdb_display_init:
	display_enable = ret ? 0 : 1;
	return ret;
}
bootable/bootloader/lk/target/msm8996/oem_panel.c,找不到面板会根据平台有一个默认的屏幕配置。
int oem_panel_select(const char *panel_name, struct panel_struct *panelstruct,
			struct msm_panel_info *pinfo,
			struct mdss_dsi_phy_ctrl *phy_db)
{
	uint32_t hw_id = board_hardware_id();
	int32_t panel_override_id;
	uint32_t ret = 0;

	phy_db->pll_type = DSI_PLL_TYPE_THULIUM;
	pinfo->has_bridge_chip = false;

	if (panel_name) {
		panel_override_id = panel_name_to_id(supp_panels,
				ARRAY_SIZE(supp_panels), panel_name);

		if (panel_override_id < 0) {
			dprintf(CRITICAL, "Not able to search the panel:%s\n",
					 panel_name);
		} else if (panel_override_id < UNKNOWN_PANEL) {
			/* panel override using fastboot oem command */
			panel_id = panel_override_id;

			dprintf(INFO, "OEM panel override:%s\n",
					panel_name);
			goto panel_init;
		}
	}

	switch (hw_id) {
	case HW_PLATFORM_MTP:
		panel_id = NT35596_1080P_VIDEO_PANEL;
       		break;
	case HW_PLATFORM_FLUID:
	case HW_PLATFORM_SURF:
		panel_id = SHARP_WQXGA_DUALDSI_VIDEO_PANEL;
		break;
	case HW_PLATFORM_QRD:
		panel_id = R69007_WQXGA_CMD_PANEL;
		break;
	case HW_PLATFORM_LIQUID:
		panel_id = JDI_4K_DUALDSI_VIDEO_NOFBC_PANEL;
		break;
	case HW_PLATFORM_DRAGON:
		panel_id = TRULY_FWVGA_VIDEO_PANEL;
		break;
	case HW_PLATFORM_ADP:
		panel_id = ADV7533_720P_VIDEO_PANEL;
		pinfo->has_bridge_chip = true;
		break;
	default:
		dprintf(CRITICAL, "Display not enabled for %d HW type\n"
					, hw_id);
		return PANEL_TYPE_UNKNOWN;
	}

panel_init:
	if (pinfo->has_bridge_chip) {        //i2c通信部分,如果该屏幕需要进行i2c初始化
		ret = oem_panel_bridge_chip_init(pinfo);
		if (ret) {
			dprintf(CRITICAL, "Error in initializing bridge chip\n");
			return ret;
		}
	}
	return init_panel_data(panelstruct, pinfo, phy_db);
}

        这里可以修改高通原有的屏幕参数,也可以自己新建一款屏幕。在bootable/bootloader/lk/dev/gcdb/display/include

有具体的屏幕参数。

           我们只需适配自己的屏幕,基本上就可以在lk下正常显示了(屏幕的参数最好仔细确认,因为平台的差异会导致屏幕的参数有细微的变化)。







开发高通平台上的幕驱动程序涉及多个关键环节,包括对高通平台硬件架构的理解、幕接口协议的掌握、以及Linux内核中DRM/KMS框架的使用等。以下是一些主要步骤和指南,帮助开发者在高通平台上进行幕驱动开发: ### 3.1 高通平台幕接口支持 高通平台广泛支持多种显示接口,其中MIPI DSI(Mobile Industry Processor Interface - Display Serial Interface)是最常见的接口之一,尤其适用于高分辨率和高刷新率的移动设备显示。例如,高通骁龙410C集成了对DSI的支持,允许直接连接显示并进行高速数据传输[^2]。类似地,高通410C等中低端处理器也支持DSI接口,这使得开发者可以利用该接口进行幕驱动开发[^2]。 ### 3.2 MIPI DSI接口的基本原理 MIPI DSI是一种高速串行接口,用于连接显示控制器和显示。它支持多种模式,包括命令模式(Command Mode)和视频模式(Video Mode)。在开发过程中,开发者需要根据所使用的显示类型和控制器配置选择合适的模式。此外,MIPI DSI接口使用较少的引脚实现高分辨率显示,这对于移动设备的设计紧凑性和性能优化具有重要意义[^3]。 ### 3.3 Linux内核中的DRM/KMS框架 在基于Linux的高通平台上,幕驱动通常基于DRM(Direct Rendering Manager)和KMS(Kernel Mode Setting)框架进行开发。DRM提供了一套统一的接口来管理显示资源,而KMS则用于设置显示模式和管理显示输出。开发者需要熟悉这些子系统,并编写相应的驱动代码以支持特定的幕设备。 一个典型的MIPI DSI驱动程序结构包括以下几个部分: - **DSI控制器驱动**:负责初始化和配置DSI硬件控制器。 - **Panel驱动**:实现与特定显示的通信,包括初始化序列、电源控制、模式设置等。 - **桥接器(Bridge)**:用于连接不同的显示子模块,如将DSI控制器与显示桥接器(如MIPI DBI或LVDS)连接。 - **DRM设备注册**:将驱动注册到DRM框架中,以便上层应用可以访问和控制显示设备。 ### 3.4 高通平台驱动开发实践 在实际开发中,开发者可以高通平台提供的开源驱动代码,例如在Linux内核源码树中的`drivers/gpu/drm/msm`目录下,包含了高通Adreno GPU和DSI控制器的相关驱动代码。开发者可以基于这些代码进行修改和扩展,以适配特定的幕硬件。 以下是一个简化的MIPI DSI驱动初始化示例代码片段: ```c #include <drm/drm_mipi_dsi.h> #include <drm/drm_panel.h> struct my_dsi_panel { struct drm_panel base; struct mipi_dsi_device *dsi; }; static int my_dsi_panel_get_modes(struct drm_panel *panel, struct drm_connector *connector) { struct my_dsi_panel *my_panel = container_of(panel, struct my_dsi_panel, base); struct drm_display_mode *mode; mode = drm_mode_create(connector->dev); if (!mode) return -ENOMEM; mode->clock = 100000; // 设置像素时钟频率 mode->hdisplay = 1080; // 水平分辨率 mode->vdisplay = 1920; // 垂直分辨率 mode->vrefresh = 60; // 刷新率 drm_mode_set_name(mode); drm_mode_probed_add(connector, mode); return 1; } static const struct drm_panel_funcs my_dsi_panel_funcs = { .get_modes = my_dsi_panel_get_modes, }; static int my_dsi_probe(struct mipi_dsi_device *dsi) { struct my_dsi_panel *panel; panel = devm_kzalloc(&dsi->dev, sizeof(*panel), GFP_KERNEL); if (!panel) return -ENOMEM; panel->dsi = dsi; drm_panel_init(&panel->base, &dsi->dev, &my_dsi_panel_funcs, DRM_MODE_CONNECTOR_DSI); mipi_dsi_set_drvdata(dsi, panel); return 0; } static const struct of_device_id my_dsi_of_match[] = { { .compatible = "mycompany,my-dsi-panel" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, my_dsi_of_match); static struct mipi_dsi_driver my_dsi_driver = { .driver = { .name = "my-dsi-panel", .of_match_table = my_dsi_of_match, }, .probe = my_dsi_probe, }; module_mipi_dsi_driver(my_dsi_driver); ``` ### 3.5 调试与测试 在驱动开发完成后,开发者需要进行详细的调试和测试。可以使用以下工具和方法: - **内核日志分析**:通过`dmesg`命令查看内核日志,检查驱动加载是否成功,是否有错误信息。 - **DRM调试接口**:利用`/sys/class/drm`目录下的接口进行显示模式切换、电源状态控制等操作。 - **用户空间工具**:如`modetest`和`kmscube`,可用于测试DRM/KMS功能是否正常。 ### 3.6 开发资源与文档 - **高通官方文档**:高通为开发者提供了详细的硬件考手册和驱动开发指南,开发者可以从高通开发者网站获取。 - **开源社区资源**:Linux内核源码中的`Documentation/gpu/`目录下提供了DRM/KMS的开发文档。 - **开发板支持**:如正点原子ATK-DLRK3568等开发板提供了MIPI DSI接口的驱动实验环境,有助于开发者进行调试和验证[^3]。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值