高通lk阶段mipi 代码解析以及新屏幕添加
参考:https://www.cnblogs.com/linhaostudy/p/9237526.html
背景
前段时间做了这块的工作,但总结完以后领导说我的认识还是过于肤浅,因此重新再看了一下。
确实是有一些当时不知道的部分。
代码分析
以 MIPI屏幕中的VIDEO类型为例。
函数入口
aboot_init()来到target_display_init();
然后根据target中的不同,来判断是否进入哪一个函数来处理(见附录):
target_display_init() 函数里有很重要函数就是gcdb_display_init();
void target_display_init(const char *panel_name)
{
uint32_t panel_loop = 0;
uint32_t ret = 0;
panel_name += strspn(panel_name, " ");
if (!strcmp(panel_name, NO_PANEL_CONFIG)
|| !strcmp(panel_name, SIM_VIDEO_PANEL)
|| !strcmp(panel_name, SIM_CMD_PANEL)) {
dprintf(INFO, "Selected %s: Skip panel configuration\n",
panel_name);
return;
}
do {
target_force_cont_splash_disable(false);
ret = gcdb_display_init(panel_name, MDP_REV_305, MIPI_FB_ADDR);
if (!ret || ret == ERR_NOT_SUPPORTED) {
break;
} else {
target_force_cont_splash_disable(true);
msm_display_off();
}
} while (++panel_loop <= oem_panel_max_auto_detect_panels());
}
这就是高通原生lk LCD 兼容的关键所在。
有关的兼容文档:高通平台 在lk阶段通过读取屏的ID实现多LCD屏兼容
gcdb_display_init
GCDB:Global Component Database全局组件数据库
gcdb_display_init会根据不同的PANEL类型(由oem_panel_select的返回值决定,类似多态)初始化不同的参数
1、决定pan的行为(以函数指针的形式):
- pll_clk_func(如果有)
- power_func
- bl_func
2、设置对应的pan属性
- 宽高
- 数据类型
int gcdb_display_init(const char *panel_name, uint32_t rev, void *base)
{
int ret = NO_ERROR;
int pan_type;
pan_type = oem_panel_select(panel_name, &panelstruct, &(panel.panel_info),
&dsi_video_mode_phy_db);
// DSI
if (pan_type == PANEL_TYPE_DSI) {
init_platform_data();
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.power_func = mdss_dsi_panel_power;
panel.pre_init_func = mdss_dsi_panel_pre_init;
panel.bl_func = mdss_dsi_bl_enable;
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;
// EDP
} 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;
// SPI
} else if (pan_type == PANEL_TYPE_SPI) {
dprintf(CRITICAL, "add the spi panel init config data!\n");
panel.panel_info.xres = panelstruct.panelres->panel_width;
panel.panel_info.yres = panelstruct.panelres->panel_height;
panel.panel_info.bpp = panelstruct.color->color_format;
panel.power_func = mdss_spi_panel_power;
panel.bl_func = mdss_spi_bl_enable;
panel.fb.base = base;
panel.fb.width = panel.panel_info.xres;
panel.fb.height = panel.panel_info.yres;
panel.fb.bpp = panel.panel_info.bpp;
panel.fb.format = FB_FORMAT_RGB565;
panel.panel_info.type = SPI_PANEL;
} 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;
}
初始化好之后就调用msm_display_init()函数。
msm_display_init
刚刚执行的东西会在这里调用:
在msm_display_init()里先Turn on panel,再Turn on backlight;
所有的行为都在上面进行了初始化。
int msm_display_init(struct msm_fb_panel_data *pdata)
{
int ret = NO_ERROR;
panel = pdata;
if (!panel) {
ret = ERR_INVALID_ARGS;
goto msm_display_init_out;
}
/* Turn on panel */
if (pdata->power_func)
ret = pdata->power_func(1, &(panel->panel_info));
/* Enable clock */
if (pdata->clk_func)
ret = pdata->clk_func(1);
/* Only enabled for auto PLL calculation */
if (pdata->pll_clk_func)
ret = pdata->pll_clk_func(1, &(panel->panel_info));
/* pinfo prepare */
if (pdata->panel_info.prepare) {
/* this is for edp which pinfo derived from edid */
ret = pdata->panel_info.prepare();
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;
}
ret = msm_fb_alloc(&(panel->fb));
ret = msm_display_config();
fbcon_setup(&(panel->fb));
display_image_on_screen();
ret = msm_display_on();
if (pdata->post_power_func)
ret = pdata->post_power_func(1);
mdelay(100);
/* Turn on backlight */
if (pdata->bl_func)
ret = pdata->bl_func(1);
msm_display_init_out:
return ret;
}
给L2、L6、L17供电
/* Turn on panel */
if (pdata->power_func)
ret = pdata->power_func(1, &(panel->panel_info));
mdss_dsi_panel_power
ret = target_ldo_ctrl(enable, pinfo);
regulator_enable(); /* L2, L6, and L17 */
配置时钟
调用calculate_clock_config(pinfo)计算时钟配置
调用target_panel_clock(enable, pinfo)配置目标panel的时钟。
/* Enable clock */
if (pdata->clk_func)
ret = pdata->clk_func(1);
mdss_dsi_panel_clock
ret = calculate_clock_config(pinfo);
ret = target_panel_clock(enable, pinfo);
分配并设置帧缓存
dev/gcdb/display/gcdb_display.c
msm_fb_alloc(&(panel->fb))分配帧缓冲器(frame buffer)所需的内存
和fbcon_setup(&(panel->fb))为分配内存。
msm_display_config根据细分panel的类型进行配置
ret = msm_fb_alloc(&(panel->fb));
ret = msm_display_config();
fbcon_setup(&(panel->fb));
/**********************************************/
int msm_display_config()
{
int ret = NO_ERROR;
int mdp_rev;
struct msm_panel_info *pinfo;
pinfo = &(panel->panel_info);
/* Set MDP revision */
mdp_set_revision(panel->mdp_rev);
switch (pinfo->type) {
case LVDS_PANEL:
dprintf(INFO, "Config LVDS_PANEL.\n");
ret = mdp_lcdc_config(pinfo, &(panel->fb));
break;
case MIPI_VIDEO_PANEL:
dprintf(INFO, "Config MIPI_VIDEO_PANEL.\n");
mdp_rev = mdp_get_revision();
if (mdp_rev == MDP_REV_50 || mdp_rev == MDP_REV_304 ||
mdp_rev == MDP_REV_305)
ret = mdss_dsi_config(panel); // 看下面
else
ret = mipi_config(panel);
ret = mdp_dsi_video_config(pinfo, &(panel->fb));
break;
case MIPI_CMD_PANEL:
dprintf(INFO, "Config MIPI_CMD_PANEL.\n");
//...
ret = mdp_dsi_cmd_config(pinfo, &(panel->fb));
if (ret)
goto msm_display_config_out;
break;
default:
return ERR_INVALID_ARGS;
};
if (pinfo->config)
ret = pinfo->config((void *)pinfo);
msm_display_config_out:
return ret;
}
配置控制器
int mdss_dsi_config(struct msm_fb_panel_data *panel)
{
int ret = NO_ERROR;
struct msm_panel_info *pinfo;
struct mipi_dsi_panel_config mipi_pinfo;
#if (DISPLAY_TYPE_MDSS == 1)
if (!panel)
return ERR_INVALID_ARGS;
memset(&mipi_pinfo, 0, sizeof(mipi_pinfo));
pinfo = &(panel->panel_info);
mipi_pinfo.mode = pinfo->mipi.mode;
mipi_pinfo.num_of_lanes = pinfo->mipi.num_of_lanes;
mipi_pinfo.mdss_dsi_phy_config = pinfo->mipi.mdss_dsi_phy_db;
mipi_pinfo.panel_cmds = pinfo->mipi.panel_cmds;
mipi_pinfo.num_of_panel_cmds = pinfo->mipi.num_of_panel_cmds;
mipi_pinfo.lane_swap = pinfo->mipi.lane_swap;
mipi_pinfo.pack = 0;
mipi_pinfo.t_clk_pre = pinfo->mipi.t_clk_pre;
mipi_pinfo.t_clk_post = pinfo->mipi.t_clk_post;
mipi_pinfo.signature = pinfo->mipi.signature;
mipi_pinfo.force_clk_lane_hs = pinfo->mipi.force_clk_lane_hs;
mipi_pinfo.cmds_post_tg = pinfo->mipi.cmds_post_tg;
mipi_pinfo.panel_read_cmds = pinfo->mipi.panel_read_cmds;
// 如果有两个MIPI DSI接口MIPI_DSI0和MIPI_DSI1就调用两次mdss_dsi_phy_init(),msm8909只有MIPI_DSI0,MSM8994等有两个DSI接口。
mdss_dsi_phy_init(&mipi_pinfo, MIPI_DSI0_BASE, DSI0_PHY_BASE);
if (pinfo->mipi.dual_dsi)
mdss_dsi_phy_init(&mipi_pinfo, MIPI_DSI1_BASE, DSI1_PHY_BASE);
//初始化DSI接口的host控制器。
ret = mdss_dsi_host_init(&mipi_pinfo, pinfo->mipi.dual_dsi,
pinfo->mipi.broadcast);
if (ret) {
dprintf(CRITICAL, "dsi host init error\n");
goto error;
}
mdss_dsi_phy_contention_detection(&mipi_pinfo, DSI0_PHY_BASE);
// 看下面
if (panel->pre_init_func) {
ret = panel->pre_init_func();
if (ret) {
dprintf(CRITICAL, "pre_init_func error\n");
goto error;
}
}
if (mipi_pinfo.force_clk_lane_hs)
mdss_dsi_force_clk_lane_hs(pinfo->mipi.dual_dsi);
if (!mipi_pinfo.cmds_post_tg) {
ret = mdss_dsi_panel_initialize(&mipi_pinfo, pinfo->mipi.broadcast);
if (ret) {
dprintf(CRITICAL, "dsi panel init error\n");
goto error;
}
}
if (pinfo->rotate && panel->rotate)
pinfo->rotate();
#endif
error:
return ret;
}
调用if (panel->pre_init_func) {}函数:
因为
panelstruct.paneldata->panel_lp11_init在init_panel_data()函数赋值为1,所以调用
mdss_dsi_panel_reset()根据reset时序来复位panel。
static int mdss_dsi_panel_pre_init(void)
{
intret = NO_ERROR;
if(panelstruct.paneldata->panel_lp11_init) {
ret= mdss_dsi_panel_reset(1);
if(ret) {
dprintf(CRITICAL,"panel reset failed\n");
return ret;
}
}
if(panelstruct.paneldata->panel_init_delay)
udelay(panelstruct.paneldata->panel_init_delay);
dprintf(SPEW,"Panel pre init done\n");
return ret;
}
显示logo
display_image_on_screen调用fetch_image_from_partition()从splash分区获取lk logo图片,如果splash分区没有满足要求的数据,就显示默认的logo。
display_image_on_screen
fetch_image_from_partition
初始化panel
主要部分见注释:
int msm_display_on()
{
int ret = NO_ERROR;
int mdp_rev;
struct msm_panel_info *pinfo;
bs_set_timestamp(BS_SPLASH_SCREEN_DISPLAY);
pinfo = &(panel->panel_info);
if (pinfo->pre_on) {
ret = pinfo->pre_on();
}
switch (pinfo->type) {
//...
case MIPI_VIDEO_PANEL:
dprintf(INFO, "Turn on MIPI_VIDEO_PANEL.\n");
ret = mdp_dsi_video_on(pinfo); // 使能DSI VIDEO
ret = mdss_dsi_post_on(panel); // 使用初始化命令来初始化panel,包括识别屏幕(兼容有关)
ret = mipi_dsi_on();
break;
case MIPI_CMD_PANEL:
dprintf(INFO, "Turn on MIPI_CMD_PANEL.\n");
ret = mdp_dma_on(pinfo);
mdp_rev = mdp_get_revision();
if (mdp_rev...) {
ret = mipi_cmd_trigger();
}
ret = mdss_dsi_post_on(panel);
break;
default:
return ERR_INVALID_ARGS;
};
if (pinfo->on)
ret = pinfo->on();
msm_display_on_out:
return ret;
}
打开背光
/* Turn on backlight */
if (pdata->bl_func)
ret = pdata->bl_func(1);
在gcdb_display_init()函数中有一个函数指针mdss_dsi_bl_enable:
这个函数是用来调整背光的和使能背光的,通过PWM来作用:(具体要看原理图)
static int mdss_dsi_bl_enable(uint8_t enable)
{
int ret = NO_ERROR;
ret = panel_backlight_ctrl(enable);
if (ret)
dprintf(CRITICAL, "Backlight %s failed\n", enable ? "enable" :
"disable");
return ret;
}
static uint32_t panel_backlight_ctrl(uint8_t enable)
{
uint32_t ret = NO_ERROR;
if (panelstruct.backlightinfo)
ret = target_backlight_ctrl(panelstruct.backlightinfo, enable);
return ret;
}
所以bootloader的背光是通过target_backlight_ctrl()控制的。
int target_backlight_ctrl(struct backlight *bl, uint8_t enable)
{
struct pm8x41_mpp mpp;
uint32_t hw_id = board_hardware_id();
struct board_pmic_data pmic_info;
int rc;
if (bl->bl_interface_type == BL_DCS)
return 0;
board_pmic_info(&pmic_info, 1);
if (pmic_info.pmic_version == PM8916_VER)
mpp.base = PM8x41_MMP4_BASE;
else
mpp.base = PM8x41_MMP2_BASE;
mpp.vin = MPP_VIN0;
if (enable) {
pm_pwm_enable(false);
rc = pm_pwm_config(PWM_DUTY_US, PWM_PERIOD_US);
if (rc < 0)
mpp.mode = MPP_HIGH;
else {
mpp.mode = MPP_DTEST1;
pm_pwm_enable(true);
}
pm8x41_config_output_mpp(&mpp);
pm8x41_enable_mpp(&mpp, MPP_ENABLE);
} else {
pm_pwm_enable(false);
pm8x41_enable_mpp(&mpp, MPP_DISABLE);
}
mdelay(20);
if (board_hardware_subtype() == HW_PLATFORM_SUBTYPE_IOE)
bkl_gpio.pin_id = 99;
if (enable) {
if (hw_id == HW_PLATFORM_SURF || (hw_id == HW_PLATFORM_MTP)) {
/* configure backlight gpio for CDP and MTP */
gpio_tlmm_config(bkl_gpio.pin_id, 0,
bkl_gpio.pin_direction, bkl_gpio.pin_pull,
bkl_gpio.pin_strength, bkl_gpio.pin_state);
gpio_set(bkl_gpio.pin_id, 2);
}
}
return 0;
}
如何增加一个panel
以 video类型的 xxx 为例。
声明panel枚举
在oem_panel.c中添加一个panel枚举
enum {
ILI9881D_720P_VIDEO_PANEL,
HX8394D_480P_VIDEO_PANEL,
HX8394D_720P_VIDEO_PANEL,
SHARP_QHD_VIDEO_PANEL,
TRULY_WVGA_CMD_PANEL,
HX8379A_FWVGA_SKUA_VIDEO_PANEL,
ILI9806E_FWVGA_VIDEO_PANEL,
HX8394D_QHD_VIDEO_PANEL,
HX8379C_FWVGA_VIDEO_PANEL,
FL10802_FWVGA_VIDEO_PANEL,
AUO_QVGA_CMD_PANEL,
AUO_CX_QVGA_CMD_PANEL,
HX8394F_720P_VIDEO_PANEL,
ILI9881C_720P_VIDEO_PANEL,
GC9306_QVGA_SPI_CMD_PANEL,
XXX_VIDEO_PANEL, // 举个例子
UNKNOWN_PANEL
};
绑定panel枚举与具体的panel属性
1、增加相应的头文件#include "include/xxx_video.h"
头文件可以通过GCDB工具配置生成、懒得做也可以找供应商要。
头文件放在:
bootable/bootloader/lk/dev/gcdb/display/include中
2、在supp_panels中添加绑定
3、照猫画虎添加对应的属性。对应的属性来自xx_panel.h(具体由GCDB生成)
#include "include/xxx_video.h"
// 如果要增加一个panel就需要在这里增加一个supp_panels
static struct panel_list supp_panels[] = {
{"truly_1080p_video", TRULY_1080P_VIDEO_PANEL},
{"truly_1080p_cmd", TRULY_1080P_CMD_PANEL},
{"r69006_1080p_video", R69006_1080P_VIDEO_PANEL},
{"r69006_1080p_cmd", R69006_1080P_CMD_PANEL},
{"truly_wuxga_video", TRULY_WUXGA_VIDEO_PANEL},
{"nt35523_720p_video", NT35523_720P_VIDEO_PANEL},
{"xxx_video", XXX_VIDEO_PANEL}, // 新增绑定
};
static int init_panel_data(struct panel_struct *panelstruct,
struct msm_panel_info *pinfo,
struct mdss_dsi_phy_ctrl *phy_db)
{
int pan_type = PANEL_TYPE_DSI;
switch (panel_id) {
case GC9306_QVGA_SPI_CMD_PANEL:
panelstruct->paneldata = &gc9306_qvga_cmd_panel_data;
panelstruct->panelres = &gc9306_qvga_cmd_panel_res;
panelstruct->color = &gc9306_qvga_cmd_color;
panelstruct->panelresetseq
= &gc9306_qvga_cmd_reset_seq;
panelstruct->backlightinfo = &gc9306_qvga_cmd_backlight;
pinfo->spi.panel_cmds
= gc9306_qvga_cmd_on_command;
pinfo->spi.num_of_panel_cmds
= GC9306_QVGA_CMD_ON_COMMAND;
pan_type = PANEL_TYPE_SPI;
break;
// 添加 属性
case XXX_VIDEO_PANEL:
// 照猫画虎添加对应的属性。
case UNKNOWN_PANEL:
default:
memset(panelstruct, 0, sizeof(struct panel_struct));
memset(pinfo->mipi.panel_cmds, 0, sizeof(struct mipi_dsi_cmd));
pinfo->mipi.num_of_panel_cmds = 0;
memset(phy_db->timing, 0, TIMING_SIZE);
pan_type = PANEL_TYPE_UNKNOWN;
break;
}
return pan_type;
}
为平台指定Panel枚举
在oem_panel_select()函数中指定对应平台的panel_id = XXX_VIDEO_PANEL;:
这个函数需要做的工作是:主要是识别不同IC,赋值给参数panel_id,panel_id的使用在同一文件中的 init_panel_data()函数中。
int oem_panel_select(const char *panel_name, struct panel_struct *panelstruct,
struct msm_panel_info *pinfo,
struct mdss_dsi_phy_ctrl *phy_db)
{
//...
switch (hw_id) {
case xx:
panel_id = XXX_VIDEO_PANEL;
break;
default:
dprintf(CRITICAL, "Display not enabled for %d HW type\n",
hw_id);
return PANEL_TYPE_UNKNOWN;
}
//...
}
附录:LCD参数说明
VBPD(verticalback porch):表示在一帧图像开始时,垂直同步信号以后的无效的行数。
VFPD(verticalfront porch):表示在一帧图像结束后,垂直同步信号以前的无效的行数。
VSPW(verticalsync pulse width):表示垂直同步脉冲的宽度,用行数计算。
HBPD(horizontal back porch):表示从水平同步信号开始到一行的有效数据开始之间的VCL的个数。
HFPD(horizontal front porch):表示一行的有效数据结束到下一个水平同步信号开始 之间的VCLK的个数。
HSPW(horizontalsync pulse width):表示水平同步信号的宽度,用VCLK计算
附录:根据target选择不同的target_display
不同的平台有相同的target_display.c,例如:
bootable/bootloader/lk/target/msm8909/target_display.c
bootable/bootloader/lk/target/target_display.c
bootable/bootloader/lk/target/msm8974/target_display.c
bootable/bootloader/lk/target/msm8994/target_display.c
bootable/bootloader/lk/target/apq8084/target_display.c
bootable/bootloader/lk/target/msm8610/target_display.c
bootable/bootloader/lk/target/msm7627a/target_display.c
bootable/bootloader/lk/target/msm8960/target_display.c
bootable/bootloader/lk/target/msm8226/target_display.c
bootable/bootloader/lk/target/msm8916/target_display.c
实际上根据编译系统,很容易找到对应的文件是怎么编译的。
bootable/bootloader/lk/makefile:134:include target/$(TARGET)/rules.mkbootable/bootloader/lk/target/rules.mk
LOCAL_DIR := $(GET_LOCAL_DIR)
OBJS += \
$(LOCAL_DIR)/init.o \
$(LOCAL_DIR)/target_display.o
最终由lunch的项目来决定:
project/apq8084.mk:9:ifeq ($(TARGET_BUILD_VARIANT),user)
project/msm7627a.mk:5:TARGET := msm7627a
project/msm8909.mk:5:TARGET := msm8909
project/msm8909.mk:9:ifeq ($(TARGET_BUILD_VARIANT),user)
project/msm8960.mk:5:TARGET := msm8960
project/msm8960.mk:9:ifeq ($(TARGET_BUILD_VARIANT),user)
project/qsd8250_surf.mk:5:TARGET := qsd8250_surf
project/msm8226.mk:5:TARGET := msm8226
project/msm8226.mk:9:ifeq ($(TARGET_BUILD_VARIANT),user)
project/aboot-surf7k.mk:5:TARGET := surf-msm7k
project/fsm9010.mk:5:TARGET := fsm9010
project/fsm9010.mk:9:ifeq ($(TARGET_BUILD_VARIANT),user)
project/msm8660_surf.mk:5:TARGET := msm8660_surf
高通lk阶段mipi 代码解析以及新屏幕添加的更多相关文章
- 高通lk屏幕向kernel传参
LK把相关参数报存到cmdline上: 在Bootable\bootloader\lk\dev\gcdb\display\gcdb_display_param.c上gcdb_display_cmdli ...
- 高通安卓调试LCD几方面总结
来公司上班现在已经整整一个月了,蔽人不才,能力有限,学习进度缓慢,不过也是有一点点的收获与心得,在这里写出来与大家分享,养成良好的记录习惯也免得后忘记. 不啰嗦了,开入正题.来公司一个月左右的时间,主 ...
- 高通 MSM8K bootloader : SBL1 .
一. MSM8K Boot Flow 图1: 高通MSM8K平台bootloader启动流程基本类似,但具体各平台,比如MSM8974.MSM8916.MSM8994等,会有微小区别. 从上图,可以看 ...
- 高通 MSM8K bootloader之一: SBL1
MSM8K Boot Flow 转自:http://www.cnblogs.com/liang123/p/6325257.html http://blog.csdn.net/F ...
- 高通LCD驱动调试
本文转载自:http://www.itgo.me/a/x6305658852004979994/lcd%20qcom 来自 :http://blog.csdn.net/dacaozuo/article ...
- 高通MSM8998 ABL的调试
高通在MSM8998上引入了UEFI,用来代替LK(Little Kernel).高通UEFI由XBL和ABL两部分组成.XBL负责芯片驱动及充电等核心应用功能.ABL包括芯片无关的应用如fastbo ...
- 高通电源管理qpnp-vm-bms驱动
1. compatible节点: qpnp-vm-bms.c使用来控制电池曲线的和BMS功能的,其compatible节点是"qcom,qpnp-vm-bms" 2. probe函 ...
- 高通电池管理基于qpnp-vm-bms电压模式
CV:Constant Voltage恒压 SMMB charger:Switch-ModeBattery Charger and Boost peripheral开关模式电池充电器和升压外围设备 O ...
- 高通移植mipi LCD的过程LK代码
lk部分:(实现LCD兼容) 1. 函数定位 aboot_init()来到target_display_init(): 这就是高通原生lk LCD 兼容的关键所在.至于你需要兼容多少LCD 就在whi ...
- 高通spi 屏幕 -lk代码分析
lk SPI驱动 1. 初始化时钟 在lk中,我们是从kmain开始执行下来的,而执行顺序则是先初始化时钟,也就是在platform_early_init函数中开始执行的: 在这里我们需要修改这个函数 ...
随机推荐
- 12、web 中间件加固-apache 加固
1.账号设置 1.1.防止 webshell 越权使用 修改 httpd.conf:/etc/httpd/conf/httpd.conf 或编译路径下 /conf/httpd.conf 检查程序启动账 ...
- windows系统桌面壁纸切换的三种csharp办法,兼容win10及旧版,还有一个现成桌面小程序
我自己用这些代码做的小app如下: 最新版本已经改成了服务的方式,也可以选择性添加系统的右键菜单,并且我自己使用的源码库已经开源到了nuget,大家可以直接拿来做二次开发, 新版的下载地址为:http ...
- go 操作elasticsearch
olivere/elastic 包 github.com/olivere/elastic doc: https://pkg.go.dev/github.com/olivere/elastic?utm_ ...
- docker-compose搭建的Mysql主从复制
设置前注意下面几点: 1)要保证同步服务期间之间的网络联通.即能相互ping通,能使用对方授权信息连接到对方数据库(防火墙开放3306端口). 2)关闭selinux. 3)同步前,双方数据库中需要同 ...
- pdo类
testmysql.php <?php require_once "./mypdo.php"; //do something... //查一行 $id = 3; //$sql ...
- CSS动画-数字轮盘滚动效果实现(组件封装,快速使用)
效果图: 原理分析:这玩意就和垂直方向的轮播图差不多,只是把轮播的图换成数字 主要实现:父组件:父组件接收一个curNum属性,这个属性代表当前需要显示的数字.它将这个数字传递给子组件AnimateN ...
- 卷爆短剧出海:五大关键,由AIGC重构
短剧高温下,谈谈AIGC的助攻路线. 短剧,一个席卷全球的高温赛道. 以往只是踏着霸总题材,如今,内容循着精品化.IP化的自然发展风向,给内容.制作.平台等产业全链都带来新机,也让短剧消费走向文化深处 ...
- Android 11(R) MultiMedia(十五)MediaCodec同步模式实现一个简易播放器
这一节是实践,学习android原生SimplePlayer并自己码一遍,果然学到很多. MyPlayer.h #include <media/stagefright/foundation/AH ...
- Flask学习记录:在w3cschool资料的基础上的个人摘录、实践与总结
学习与转载自w3cschool,在w3cschool资料的基础上的个人摘录.实践与总结,如有错误望留言. 一.Flask 概述 2021-08-25 14:01 更新 1.1 什么是Web Frame ...
- C#开发的股票盯盘小工具——摸鱼助手,附源码
写了个盯盘小工具,最近发现很多炒股的小伙伴,上班期间看手机频繁是不是影响不好?电脑上打开交易软件,那影响是不是更不好?所以我就写了个小工具,给大家"摸鱼"用.虽然是摸鱼用,但是平常 ...