如标题所说,居然在网上找不到人分析过mipi lcd驱动,表示很无奈,话说这些接口协议讲道理已经出来很久了,但是依然没人写过相应的文章去分析,大部分文章都是转载抄袭,觉得很没意思,连参考的文章都没有,更多的文章是写的怎么调试
现在只有自己去分析 ,猜测这个drm下面的应该和以前在2440上面搞得lcd驱动总的流程应该都差不多,当然这个是我的猜想,等我搞清楚了才完整记录,hahahaha 水贴
Boot阶段
#Boot ver: 2018-11-18#1.09
empty serial no.
normal boot.
checkKey
vbus = 1
no fuel gauge found power_rockchip.c(uboot)
no fuel gauge found
read logo on state from dts [1] fastboot.c(uboot)
no fuel gauge found
Using display timing dts rockchip_display.c(uboot)
Detailed mode clock 100000 kHz, flags[a] rockchip_display.c(uboot)
H: 1200 1260 1262 1342
V: 1920 1934 1937 1947
bus_format: 100e rockchip_display.c(uboot)
rk lcdc - 0 dclk set: dclk = 100000000HZ, pll select = 1, div = 1 clock-rk3399.c(uboot)
rockchip_phy_power_on: failed to find phy power on funcs rockchip_phy.c(uboot)
final DSI-Link bandwidth: 750 Mbps x 4 rockchip-dw-mipi-dsi.c(uboot)
read logo on state from dts [1] fastboot.c(uboot)
void board_fbt_preboot(void)
#if defined(CONFIG_LCD) || defined(CONFIG_ROCKCHIP_DISPLAY)
if (gd->fdt_blob) {
if (g_logo_on_state != 0) {
lcd_enable_logo(true);
drv_lcd_init();
lcd_base = map_sysmem(gd->fb_base, 0);
lcd_init(lcd_base);
/* Initialize the lcd controller */
debug("[LCD] Initializing LCD frambuffer at %p\n", lcdbase);
lcd_ctrl_init(lcdbase);
}
gd->uboot_logo = g_logo_on_state;
printf("read logo on state from dts [%d]\n", g_logo_on_state);
common/lcd.c:657: return show_resource_image(file_path) ? 0 : -1;
common/cmd_charge.c:529: return show_resource_image(path);
Using display timing dts rockchip_display.c(uboot)
Detailed mode clock 100000 kHz, flags[a] rockchip_display.c(uboot)
H: 1200 1260 1262 1342
V: 1920 1934 1937 1947
bus_format: 100e
U_BOOT_CMD(
cls, 1, 1, do_lcd_clear,
"clear screen",
""
);
static int do_lcd_clear(cmd_tbl_t *cmdtp, int flag, int argc,
lcd_clear();
if (lcd_show_logo)
debug("[LCD] Drawing the logo...\n");
lcd_console_address = lcd_logo();
bitmap_plot((panel_info.vl_col - BMP_LOGO_WIDTH)/2, (panel_info.vl_row - BMP_LOGO_HEIGHT)/2);
rk_bitmap_from_resource((unsigned short*)fb)
const char* file_path = "logo.bmp";
return show_resource_image(file_path) ? 0 : -1;
if (g_is_new_display)
return rockchip_show_bmp(image_path);
display_logo(s);
display_init(state);
ret = display_get_timing(state);
if (!display_get_timing_from_dts(panel, blob, mode)) {
printf("Using display timing dts\n");
static int display_get_timing_from_dts(int panel, const void *blob,struct drm_display_mode *mode)
{
int timing, phandle, native_mode;
int hactive, vactive, pixelclock;
int hfront_porch, hback_porch, hsync_len;
int vfront_porch, vback_porch, vsync_len;
int val, flags = 0;
timing = fdt_subnode_offset(blob, panel, "display-timings");
if (timing < 0)
return -ENODEV;
native_mode = fdt_subnode_offset(blob, timing, "timing");
if (native_mode < 0) {
phandle = fdt_getprop_u32_default_node(blob, timing, 0,
"native-mode", -1);
native_mode = fdt_node_offset_by_phandle_node(blob, timing, phandle);
if (native_mode <= 0) {
printf("failed to get display timings from DT\n");
return -ENXIO;
}
}
#define FDT_GET_INT(val, name) \
val = fdtdec_get_int(blob, native_mode, name, -1); \
if (val < 0) { \
printf("Can't get %s\n", name); \
return -ENXIO; \
}
FDT_GET_INT(hactive, "hactive");
FDT_GET_INT(vactive, "vactive");
FDT_GET_INT(pixelclock, "clock-frequency");
FDT_GET_INT(hsync_len, "hsync-len");
FDT_GET_INT(hfront_porch, "hfront-porch");
FDT_GET_INT(hback_porch, "hback-porch");
FDT_GET_INT(vsync_len, "vsync-len");
FDT_GET_INT(vfront_porch, "vfront-porch");
FDT_GET_INT(vback_porch, "vback-porch");
FDT_GET_INT(val, "hsync-active");
flags |= val ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC;
FDT_GET_INT(val, "vsync-active");
flags |= val ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC;
mode->hdisplay = hactive;
mode->hsync_start = mode->hdisplay + hfront_porch;
mode->hsync_end = mode->hsync_start + hsync_len;
mode->htotal = mode->hsync_end + hback_porch;
mode->vdisplay = vactive;
mode->vsync_start = mode->vdisplay + vfront_porch;
mode->vsync_end = mode->vsync_start + vsync_len;
mode->vtotal = mode->vsync_end + vback_porch;
mode->clock = pixelclock / 1000;
mode->flags = flags;
return 0;
}
goto done;
printf("Detailed mode clock %u kHz, flags[%x]\n"
" H: %04d %04d %04d %04d\n"
" V: %04d %04d %04d %04d\n"
"bus_format: %x\n",
mode->clock, mode->flags,
mode->hdisplay, mode->hsync_start,
mode->hsync_end, mode->htotal,
mode->vdisplay, mode->vsync_start,
mode->vsync_end, mode->vtotal,
conn_state->bus_format);
board/rockchip/common/rkboot/fastboot.c:519: drv_lcd_init();
common/stdio.c:257: drv_lcd_init ();
arch/arm/lib/board.c:707: board_late_init();
void board_init_r(gd_t *id, ulong dest_addr)
board_late_init();
common/board_r.c:859: board_late_init,
init_sequence_r[]
board_late_init,
rk lcdc - 0 dclk set: dclk = 100000000HZ, pll select = 1, div = 1
void board_init_r(gd_t *id, ulong dest_addr)
board_late_init();
board/rockchip/rk33xx/rk33xx.c:238: board_fbt_preboot();
board_fbt_preboot
if (g_logo_on_state != 0)
lcd_enable_logo(true);
drv_lcd_init();
lcd_init(lcd_base);
common/lcd.c:547: lcd_ctrl_init(lcdbase);
void lcd_ctrl_init(void *lcdbase) //rockchip_fb.c
int ret = rk_fb_parse_dt(fb, gd->fdt_blob);//获取dts里边的参数
panel_info.real_freq = rkclk_lcdc_clk_set(panel_info.lcdc_id,panel_info.vl_freq);
rkclk_lcdc_aclk_set(lcdc_id, VIO_ACLK_MAX);
rkclk_lcdc_hclk_set(lcdc_id, VIO_HCLK_MAX);
dclk_info = rkclk_lcdc_dclk_set(lcdc_id, dclk_hz);
printf("rk lcdc - %d dclk set: dclk = %dHZ, pll select = %d, div = %d\n", lcdc_id, dclk_hz, pll_sel, div);
const struct rockchip_connector_funcs rockchip_dw_mipi_dsi_funcs = {
.init = rockchip_dw_mipi_dsi_init,
.deinit = rockchip_dw_mipi_dsi_deinit,
.prepare = rockchip_dw_mipi_dsi_prepare,
.enable = rockchip_dw_mipi_dsi_enable,
.disable = rockchip_dw_mipi_dsi_disable,
.transfer = rockchip_dw_mipi_dsi_transfer,
};
rockchip_dw_mipi_dsi_prepare
rockchip_dsi_grf_config(pdata, dsi, crtc_state->crtc_id);
rockchip_dw_dsi_pre_init(state, dsi);
if (dsi->slave)
rockchip_dw_dsi_pre_init(state, dsi->slave);
printf("final DSI-Link bandwidth: %u Mbps x %d\n",
dsi->lane_mbps, dsi->lanes);
rockchip_dw_dsi_controller_init(dsi);
Starting kernel ...
[ 1.432697] [drm:drm_core_init] Initialized drm 1.1.0 20060810
[ 1.435926] init panel
[ 1.436837] register ok
[ 1.437096] dts get ok
[ 1.437105] 1111122222222222222222222222222222222222111111111111111111111111[24;80H[24;80H
[ 1.437134] register mipi dsi ok
Initialized drm 1.1.0 20060810
module_init(drm_core_init);
static int __init drm_core_init(void)
DRM_INFO("Initialized %s %d.%d.%d %s\n",CORE_NAME, CORE_MAJOR, CORE_MINOR, CORE_PATCHLEVEL, CORE_DATE);
gpu/drm/tegra/dsi.c:1433: .attach = tegra_dsi_host_attach,
gpu/drm/drm_prime.c:323: .attach = drm_gem_map_attach,
gpu/drm/udl/udl_dmabuf.c:190: .attach = udl_attach_dma_buf,
gpu/drm/rockchip/dw-mipi-dsi.c:796: .attach = dw_mipi_dsi_host_attach,
gpu/drm/msm/dsi/dsi_host.c:1342: .attach = dsi_host_attach,
gpu/drm/msm/msm_iommu.c:120: .attach = msm_iommu_attach,
drivers/gpu/drm/drm_fb_helper.c:1461: if (register_framebuffer(info) < 0)
drivers/gpu/drm/drm_fb_cma_helper.c:354: ret = drm_fb_helper_initial_config(helper, preferred_bpp);
drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c:180: ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP);
int rockchip_drm_fbdev_init(struct drm_device *dev)
ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP);
drivers/gpu/drm/rockchip/rockchip_drm_drv.c:1310: ret = rockchip_drm_fbdev_init(drm_dev);
rk3399.dtsi:2040: compatible = "rockchip,display-subsystem";//内核能匹配上的
static int rockchip_drm_bind(struct device *dev)
ret = rockchip_drm_fbdev_init(drm_dev);
dw-mipi-dsi.c
module_platform_driver(dw_mipi_dsi_driver);
dw_mipi_dsi_probe
dsi_id = of_alias_get_id(np, "dsi");
dsi->id = dsi_id;
dsi->dev = dev;
dsi->pdata = of_device_get_match_data(dev);
platform_set_drvdata(pdev, dsi);
dsi->base = devm_ioremap_resource(dev, res);
dsi->pclk = devm_clk_get(dev, "pclk");
ret = mipi_dphy_attach(dsi);
dsi->dphy.phy = devm_phy_optional_get(dev, "mipi_dphy");
dsi->dsi_host.ops = &dw_mipi_dsi_host_ops;
static const struct mipi_dsi_host_ops dw_mipi_dsi_host_ops = {
.attach = dw_mipi_dsi_host_attach,//.attach = dw_mipi_dsi_host_attach,
dsi->lanes = device->lanes;
dsi->channel = device->channel;
dsi->format = device->format;
dsi->mode_flags = device->mode_flags;
dsi->panel = of_drm_find_panel(device->dev.of_node);
.detach = dw_mipi_dsi_host_detach,
.transfer = dw_mipi_dsi_host_transfer,
};
dsi->dsi_host.dev = dev;
ret = mipi_dsi_host_register(&dsi->dsi_host);//mipi_dsi_host_register
ret = component_add(dev, &dw_mipi_dsi_ops);//component_add dw_mipi_dsi_ops
static const struct component_ops dw_mipi_dsi_ops = {
.bind = dw_mipi_dsi_bind,
ret = rockchip_dsi_dual_channel_probe(dsi);
ret = dw_mipi_dsi_register(drm, dsi);
drm_encoder_helper_add(&dsi->encoder,&dw_mipi_dsi_encoder_helper_funcs);//drm_encoder_helper_add
dw_mipi_dsi_encoder_helper_funcs = {
.mode_fixup = dw_mipi_dsi_encoder_mode_fixup,
.mode_set = dw_mipi_dsi_encoder_mode_set,
.enable = dw_mipi_dsi_encoder_enable,
.disable = dw_mipi_dsi_encoder_disable,
.atomic_check = dw_mipi_dsi_encoder_atomic_check,
};
ret = drm_encoder_init(drm, &dsi->encoder, &dw_mipi_dsi_encoder_funcs,DRM_MODE_ENCODER_DSI, NULL);
encoder->dev = dev;
encoder->encoder_type = encoder_type;
encoder->funcs = funcs;
encoder->name = kasprintf(GFP_KERNEL, "%s-%d",
drm_encoder_enum_list[encoder_type].name,
encoder->base.id);
list_add_tail(&encoder->head, &dev->mode_config.encoder_list);//list_add_tail encoder
dev->mode_config.num_encoder++;
drm_connector_helper_add(connector,&dw_mipi_dsi_connector_helper_funcs);//drm_connector_helper_add
drm_panel_attach(dsi->panel, &dsi->connector);
panel->connector = connector;
panel->drm = connector->dev;
drm_mode_connector_attach_encoder(connector, encoder);
.unbind = dw_mipi_dsi_unbind,
};
component = kzalloc(sizeof(*component), GFP_KERNEL);
component->ops = ops;
component->dev = dev;
list_add_tail(&component->node, &component_list);//list_add_tail component
ret = try_to_bring_up_masters(component);
list_for_each_entry(m, &masters, node) {
ret = try_to_bring_up_master(m, component);
if (master->bound)
return 0;
/*
* Search the list of components, looking for components that
* belong to this master, and attach them to the master.
*/
if (find_components(master))
..........
/* Found all components */
ret = master->ops->bind(master->dev);//dw_mipi_dsi_bind
master->bound = true;
if (ret != 0)
break;
dw_mipi_dsi_bind//dw_mipi_dsi_bind
ret = dw_mipi_dsi_register(drm, dsi);
drm_connector_helper_add(connector,&dw_mipi_dsi_connector_helper_funcs);
static struct drm_connector_helper_funcs dw_mipi_dsi_connector_helper_funcs = {
.loader_protect = dw_mipi_loader_protect,
.get_modes = dw_mipi_dsi_connector_get_modes,//dw_mipi_dsi_connector_get_modes
.mode_valid = dw_mipi_dsi_mode_valid,
.best_encoder = dw_mipi_dsi_connector_best_encoder,
};
dw_mipi_dsi_connector_get_modes//dw_mipi_dsi_connector_get_modes
return drm_panel_get_modes(dsi->panel);
return panel->funcs->get_modes(panel);//获取屏幕参数
static const struct drm_panel_funcs panel_simple_funcs = {
.loader_protect = panel_simple_loader_protect,
.disable = panel_simple_disable,
.unprepare = panel_simple_unprepare,
.prepare = panel_simple_prepare,
.enable = panel_simple_enable,
.get_modes = panel_simple_get_modes,//panel_simple_get_modes
.get_timings = panel_simple_get_timings,
};
panel_simple_get_modes //panel_simple_get_modes
num += panel_simple_of_get_native_mode(p);
ret = of_get_drm_display_mode(panel->dev->of_node, mode,OF_USE_NATIVE_MODE); //drivers/gpu/drm/drm_modes.c:672:int of_get_drm_display_mode(s
ret = of_get_videomode(np, &vm, index);//of_videomode.c
of_get_display_timings//drivers/video/of_display_timing.c:190:struct
r = of_parse_display_timing(entry, dt);
ret |= parse_timing_property(np, "hback-porch", &dt->hback_porch);
ret |= parse_timing_property(np, "hfront-porch", &dt->hfront_porch);
ret |= parse_timing_property(np, "hactive", &dt->hactive);
ret |= parse_timing_property(np, "hsync-len", &dt->hsync_len);
ret |= parse_timing_property(np, "vback-porch", &dt->vback_porch);
ret |= parse_timing_property(np, "vfront-porch", &dt->vfront_porch);
ret |= parse_timing_property(np, "vactive", &dt->vactive);
ret |= parse_timing_property(np, "vsync-len", &dt->vsync_len);
ret |= parse_timing_property(np, "clock-frequency", &dt->pixelclock);
disp->timings[disp->num_timings] = dt;
disp->num_timings++;//存起来
ret = videomode_from_timings(disp, vm, index);//drivers/video/videomode.c:32:
dt = display_timings_get(disp, index);
if (disp->num_timings > index)//返回index下标的timing
return disp->timings[index];
videomode_from_timing(dt, vm);//把dt里相应的赋给vm
vm->pixelclock = dt->pixelclock.typ;
vm->hactive = dt->hactive.typ;
vm->hfront_porch = dt->hfront_porch.typ;
vm->hback_porch = dt->hback_porch.typ;
vm->hsync_len = dt->hsync_len.typ;
vm->vactive = dt->vactive.typ;
vm->vfront_porch = dt->vfront_porch.typ;
vm->vback_porch = dt->vback_porch.typ;
vm->vsync_len = dt->vsync_len.typ;
vm->flags = dt->flags;
drm_display_mode_from_videomode(&vm, dmode);
dmode->hdisplay = vm->hactive;
dmode->hsync_start = dmode->hdisplay + vm->hfront_porch;
dmode->hsync_end = dmode->hsync_start + vm->hsync_len;
dmode->htotal = dmode->hsync_end + vm->hback_porch;
dmode->vdisplay = vm->vactive;
dmode->vsync_start = dmode->vdisplay + vm->vfront_porch;
dmode->vsync_end = dmode->vsync_start + vm->vsync_len;
dmode->vtotal = dmode->vsync_end + vm->vback_porch;
dmode->clock = vm->pixelclock / 1000;
drm_mode_probed_add(connector, mode);
list_add_tail(&mode->head, &connector->probed_modes);//list_add_tail mode
panel-simple.c
panel_simple_dsi_probe
.......
panel_simple_probe
.........
if (!of_property_read_u32(dev->of_node, "bus-format", &val))
of_desc->bus_format = val;
if (!of_property_read_u32(dev->of_node, "prepare-delay-ms", &val))
of_desc->delay.prepare = val;
if (!of_property_read_u32(dev->of_node, "enable-delay-ms", &val))
of_desc->delay.enable = val;
if (!of_property_read_u32(dev->of_node, "disable-delay-ms", &val))
.........
panel->supply = devm_regulator_get(dev, "power");
panel->enable_gpio = devm_gpiod_get_optional(dev, "enable", 0);
panel->reset_gpio = devm_gpiod_get_optional(dev, "reset", 0);
.........
drm_panel_init(&panel->base);
panel->base.dev = dev;
panel->base.funcs = &panel_simple_funcs;
static const struct drm_panel_funcs panel_simple_funcs = {
.loader_protect = panel_simple_loader_protect,
.disable = panel_simple_disable,
.unprepare = panel_simple_unprepare,
.prepare = panel_simple_prepare,
panel_simple_prepare
panel_simple_dsi_send_cmds
.enable = panel_simple_enable,
.get_modes = panel_simple_get_modes,
.get_timings = panel_simple_get_timings,
};
err = drm_panel_add(&panel->base);
list_add_tail(&panel->list, &panel_list);//list_add_tail panel
panel = dev_get_drvdata(&dsi->dev);
panel->dsi = dsi;
if (!of_property_read_u32(dsi->dev.of_node, "dsi,flags", &val))
dsi->mode_flags = val;
if (!of_property_read_u32(dsi->dev.of_node, "dsi,format", &val))
dsi->format = val;
if (!of_property_read_u32(dsi->dev.of_node, "dsi,lanes", &val))
dsi->lanes = val;
data = of_get_property(dsi->dev.of_node, "panel-init-sequence", &len);
data = of_get_property(dsi->dev.of_node, "panel-exit-sequence", &len);
return mipi_dsi_attach(dsi);
const struct mipi_dsi_host_ops *ops = dsi->host->ops;
return ops->attach(dsi->host, dsi);//dw-mipi-dsi.c
.attach = dw_mipi_dsi_host_attach,
dsi->lanes = device->lanes;
dsi->channel = device->channel;
dsi->format = device->format;
dsi->mode_flags = device->mode_flags;
dsi->panel = of_drm_find_panel(device->dev.of_node);
rockchip-drm-drv.c
static int rockchip_drm_platform_probe(struct platform_device *pdev) ->rockchip-drm-drv.c
return component_master_add_with_match(dev, &rockchip_drm_ops, match);
ret = try_to_bring_up_master(master, NULL);
ret = master->ops->bind(master->dev);
static const struct component_master_ops rockchip_drm_ops = {
.bind = rockchip_drm_bind,
.unbind = rockchip_drm_unbind,
};
rockchip_drm_bind
drm_dev = drm_dev_alloc(&rockchip_drm_driver, dev);
ret = rockchip_drm_fbdev_init(drm_dev);//rockchip_drm_fbdev.c
drm_fb_helper_prepare(dev, helper, &rockchip_drm_fb_helper_funcs);
ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP);
return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel);
/* push down into drivers */
ret = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes);//fb_probe 分配framebuffer
info = fb_helper->fbdev;
if (register_framebuffer(info) < 0)//register_framebuffer
............
dev_info(fb_helper->dev->dev, "fb%d: %s frame buffer device\n",info->node, info->fix.id);
............
list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
//fb_probe
static const struct drm_fb_helper_funcs rockchip_drm_fb_helper_funcs = {
..........
.fb_probe = rockchip_drm_fbdev_create,
};
static int rockchip_drm_fbdev_create(struct drm_fb_helper *helper,struct drm_fb_helper_surface_size *sizes)
fbi = drm_fb_helper_alloc_fbi(helper);//分配framebuffer
info = framebuffer_alloc(0, dev);//framebuffer_alloc
helper->fb = rockchip_drm_framebuffer_init(dev, &mode_cmd,private->fbdev_bo);
fb = rockchip_fb_alloc(dev, mode_cmd, &obj, NULL, 1);
ret = drm_framebuffer_init(dev, &rockchip_fb->fb,&rockchip_drm_fb_funcs);
dev->mode_config.num_fb++;
list_add(&fb->head, &dev->mode_config.fb_list);//加到链表
fbi->par = helper;
fbi->flags = FBINFO_FLAG_DEFAULT;
fbi->fbops = &rockchip_drm_fbdev_ops;
ret = drm_dev_register(drm_dev, 0);
component:vop,edp
encoder:编码器,与component相关
CRTC:vopb,vopl
vop:lcd控制器
想想,可能大多数人对于驱动这块也就是持一种会改会用会移植的态度,所以分析的人很少,确实一般这种复杂的驱动一般不会有什么问题,总结一下调试一块1200*1920屏幕中遇到过的问题,
首先屏幕驱动是有的,主要是调试,调试过程中遇到的问题:
1.显示不正常,屏幕四等分,左下角那一块无显示(解决的方式:修改频率,不要太低不要太高,慢慢试)
2.休眠唤醒后显示不正常(频率问题)
3.切换画面闪屏(屏幕配置问题,初始化序列这一块要引起注意,屏厂提供)
4.搞了这么久,都是忽略了初始化序列这一块的问题,说来话长,到此为止