msm8610 lcd driver code analysis
---恢复内容开始---
1 lcd probe
mdss-mdp3-objs = mdp3.o mdp3_dma.o mdp3_ctrl.o #1 mdss-mdp3-objs += mdp3_ppp.o mdp3_ppp_hwio.o mdp3_ppp_data.o obj-$(CONFIG_FB_MSM_MDSS) += mdss-mdp3.o mdss-mdp-objs := mdss_mdp.o mdss_mdp_ctl.o mdss_mdp_pipe.o mdss_mdp_util.o mdss-mdp-objs += mdss_mdp_pp.o mdss-mdp-objs += mdss_mdp_intf_video.o mdss-mdp-objs += mdss_mdp_intf_cmd.o mdss-mdp-objs += mdss_mdp_intf_writeback.o mdss-mdp-objs += mdss_mdp_rotator.o mdss-mdp-objs += mdss_mdp_overlay.o mdss-mdp-objs += mdss_mdp_wb.o obj-$(CONFIG_FB_MSM_MDSS) += mdss-mdp.o ifeq ($(CONFIG_FB_MSM_MDSS),y) obj-$(CONFIG_DEBUG_FS) += mdss_debug.o endif dsi-v2-objs = dsi_v2.o dsi_host_v2.o dsi_io_v2.o dsi_panel_v2.o #2: dsi_host_v2, #3: dsi_panel_v2 obj-$(CONFIG_FB_MSM_MDSS) += dsi-v2.o mdss-dsi-objs := mdss_dsi.o mdss_dsi_host.o mdss-dsi-objs += mdss_dsi_panel.o mdss-dsi-objs += msm_mdss_io_8974.o obj-$(CONFIG_FB_MSM_MDSS) += mdss-dsi.o obj-$(CONFIG_FB_MSM_MDSS) += mdss_edp.o obj-$(CONFIG_FB_MSM_MDSS) += mdss_edp_aux.o obj-$(CONFIG_FB_MSM_MDSS) += mdss_io_util.o obj-$(CONFIG_FB_MSM_MDSS) += mdss_fb.o #4
c0ace4b8 t __initcall_msm_iommu_init4
c0ace4bc t __initcall_msm_iommu_driver_init4
c0ace7d4 t __initcall_dynamic_debug_init_debugfs6
c0ace7d8 t __initcall_qpnp_pin_init6
c0ace7dc t __initcall_mdp3_driver_init6 // (1)
c0ace7e0 t __initcall_mdss_mdp_driver_init6 // (2)
c0ace7e4 t __initcall_msm_dsi_v2_driver_init6 // (3)
c0ace7e8 t __initcall_dsi_panel_module_init6 // (4)
c0ace7ec t __initcall_mdss_dsi_driver_init6 // (5)
c0ace7f0 t __initcall_mdss_dsi_panel_init6 // (6)
c0ace7f4 t __initcall_mdss_edp_init6
c0ace7f8 t __initcall_mdss_wb_driver_init6
c0ace7fc t __initcall_mdss_fb_init6
c0ace800 t __initcall_pty_init6
c0ace804 t __initcall_sysrq_init6
c0ace808 t __initcall_msm_serial_hsl_init6
c0ace80c t __initcall_rand_initialize6
c0ace810 t __initcall_msm_rng_init6
c0ace814 t __initcall_diagchar_init6
c0ace818 t __initcall_ion_page_pool_init6
c0ace81c t __initcall_kgsl_core_init6
c0ace820 t __initcall_kgsl_3d_init6
c0ace824 t __initcall_kgsl_2d_init6
c0ace828 t __initcall_topology_sysfs_init6
c0ace82c t __initcall_brd_init6
c0ace830 t __initcall_loop_init6
c0ace834 t __initcall_qseecom_init6
c0ace838 t __initcall_wcd9xxx_init6
c0ace83c t __initcall_pn544_dev_init6
c0ace840 t __initcall_msm_spi_init6
c0ace844 t __initcall_dummy_init_module6
c0aa87dc t fbmem_init drivers/video/fbmem.c
c0aa888c t video_setup
drivers/video/fbmem.c
c0aa8938 t backlight_class_init
drivers/video/backlight/backlight.c
c0aa89a8 t mdp3_driver_init
drivers/video/msm/mdss/mdp3.c
c0aa89dc t mdss_mdp_driver_init
drivers/video/msm/mdss/mdss_mdp.c
c0aa8a10 t msm_dsi_v2_driver_init drivers/video/msm/mdss/dsi_host_v2.c
c0aa8a3c t dsi_panel_module_init
drivers/video/msm/mdss/dsi_panel_v2.c
c0aa8a48 t mdss_dsi_driver_init
drivers/video/msm/mdss/mdss_dsi.c
c0aa8a74 t mdss_dsi_panel_init
drivers/video/msm/mdss/mdss_dsi_panel.c
c0aa8a80 t mdss_edp_init
drivers/video/msm/mdss/mdss_edp.c
c0aa8ab4 t mdss_wb_driver_init
drivers/video/msm/mdss/mdss_wb.c
c0aa8ac0 T mdss_fb_init drivers/video/msm/mdss/mdss_fb.c
c0aa8ae0 t regulator_init drivers/regulator/core.c
c0aa8b60 t regulator_init_complete drivers/regulator/core.c
c0aa8ca4 T regulator_dummy_init drivers/regulator/dummy.c
c0aa8d2c T regulator_stub_init drivers/regulator/stub-regulator.c
c0aa8d5c T qpnp_regulator_init drivers/regulator/qpnp-regulator.c
c0aa8e04 t tty_class_init drivers/tty/tty_io.c
c0aa8e48 T console_init drivers/tty/tty_io.c
c0aa8e78 T tty_init drivers/tty/tty_io.c
c0aa8fbc t pty_init drivers/tty/pty.c
2 splash screen(boot from lk to kernel)
3 mdp3 dma and control
3.1 mdp3 control interface
int mdp3_ctrl_init(struct msm_fb_data_type *mfd) { struct device *dev = mfd->fbi->dev; struct msm_mdp_interface *mdp3_interface = &mfd->mdp; struct mdp3_session_data *mdp3_session = NULL; u32 intf_type = MDP3_DMA_OUTPUT_SEL_DSI_VIDEO; int rc; pr_debug("mdp3_ctrl_init\n"); mdp3_interface->on_fnc = mdp3_ctrl_on; mdp3_interface->off_fnc = mdp3_ctrl_off; mdp3_interface->do_histogram = NULL; mdp3_interface->cursor_update = NULL; mdp3_interface->dma_fnc = mdp3_ctrl_pan_display; mdp3_interface->ioctl_handler = mdp3_ctrl_ioctl_handler; mdp3_interface->kickoff_fnc = mdp3_ctrl_display_commit_kickoff; mdp3_interface->lut_update = mdp3_ctrl_lut_update; mdp3_session = kmalloc(sizeof(struct mdp3_session_data), GFP_KERNEL); if (!mdp3_session) { pr_err("fail to allocate mdp3 private data structure"); return -ENOMEM; } memset(mdp3_session, 0, sizeof(struct mdp3_session_data)); mutex_init(&mdp3_session->lock); INIT_WORK(&mdp3_session->vsync_work, mdp3_dispatch_vsync); mutex_init(&mdp3_session->histo_lock); mdp3_session->dma = mdp3_get_dma_pipe(MDP3_DMA_CAP_ALL); if (!mdp3_session->dma) { rc = -ENODEV; goto init_done; } rc = mdp3_dma_init(mdp3_session->dma); if (rc) { pr_err("fail to init dma\n"); goto init_done; } intf_type = mdp3_ctrl_get_intf_type(mfd); mdp3_session->intf = mdp3_get_display_intf(intf_type); if (!mdp3_session->intf) { rc = -ENODEV; goto init_done; } rc = mdp3_intf_init(mdp3_session->intf); if (rc) { pr_err("fail to init interface\n"); goto init_done; } mdp3_session->dma->output_config.out_sel = intf_type; mdp3_session->mfd = mfd; mdp3_session->panel = dev_get_platdata(&mfd->pdev->dev); mdp3_session->status = 0; mdp3_session->overlay.id = MSMFB_NEW_REQUEST; mdp3_bufq_init(&mdp3_session->bufq_in); mdp3_bufq_init(&mdp3_session->bufq_out); mdp3_session->histo_status = 0; mdp3_session->lut_sel = 0; init_timer(&mdp3_session->vsync_timer); mdp3_session->vsync_timer.function = mdp3_vsync_timer_func; mdp3_session->vsync_timer.data = (u32)mdp3_session; mdp3_session->vsync_period = 1000 / mfd->panel_info->mipi.frame_rate; mfd->mdp.private1 = mdp3_session; rc = sysfs_create_group(&dev->kobj, &vsync_fs_attr_group); if (rc) { pr_err("vsync sysfs group creation failed, ret=%d\n", rc); goto init_done; } kobject_uevent(&dev->kobj, KOBJ_ADD); pr_debug("vsync kobject_uevent(KOBJ_ADD)\n"); init_done: if (IS_ERR_VALUE(rc)) kfree(mdp3_session); return rc; }
mdp3_ctrl_init(msm_fb_data_type *) : int
|------mdp3_init(msm_fb_data_type *) : int
|------------ mdp3_probe(platform_device *) : int
|-------------------{init mdp3_driver}() : platform_driver
static int mdp3_ctrl_on(struct msm_fb_data_type *mfd)
|------mdp3_ctrl_init(msm_fb_data_type *) : int
|------mdss_fb_blank_sub(int, fb_info *, int) : int (2 matches)
|||||||||-----mdss_fb_blank(int, fb_info *) : int
|||||||||------mdss_fb_open(fb_info *, int) : int
|||||||||------mdss_fb_release(fb_info *, int) : int
|||||||||------mdss_fb_resume_sub(msm_fb_data_type *) : int
|||||||||----- -mdss_fb_set_par(fb_info *) : int (2 matches)
|||||||||------mdss_fb_suspend_sub(msm_fb_data_type *) : int
|------mdss_fb_dcm(msm_fb_data_type *, int) : int (2 matches)
|------mdss_mdp_overlay_init(msm_fb_data_type *) : int
|------mdss_qpic_overlay_init(msm_fb_data_type *) : int
3.2 interface of dma
int mdp3_dma_init(struct mdp3_dma *dma) { int ret = 0; pr_debug("mdp3_dma_init\n"); switch (dma->dma_sel) { case MDP3_DMA_P: dma->dma_config = mdp3_dmap_config; dma->config_cursor = mdp3_dmap_cursor_config; dma->config_ccs = mdp3_dmap_ccs_config; dma->config_histo = mdp3_dmap_histo_config; dma->config_lut = mdp3_dmap_lut_config; dma->update = mdp3_dmap_update; dma->update_cursor = mdp3_dmap_cursor_update; dma->get_histo = mdp3_dmap_histo_get; dma->histo_op = mdp3_dmap_histo_op; dma->vsync_enable = mdp3_dma_vsync_enable; dma->start = mdp3_dma_start; dma->stop = mdp3_dma_stop; break; case MDP3_DMA_S: dma->dma_config = mdp3_dmas_config; dma->config_cursor = NULL; dma->config_ccs = NULL; dma->config_histo = NULL; dma->config_lut = NULL; dma->update = mdp3_dmas_update; dma->update_cursor = NULL; dma->get_histo = NULL; dma->histo_op = NULL; dma->vsync_enable = mdp3_dma_vsync_enable; dma->start = mdp3_dma_start; dma->stop = mdp3_dma_stop; break; case MDP3_DMA_E: default: ret = -ENODEV; break; } spin_lock_init(&dma->dma_lock); spin_lock_init(&dma->histo_lock); init_completion(&dma->vsync_comp); init_completion(&dma->dma_comp); init_completion(&dma->histo_comp); dma->vsync_client.handler = NULL; dma->vsync_client.arg = NULL; dma->histo_state = MDP3_DMA_HISTO_STATE_IDLE; memset(&dma->cursor, 0, sizeof(dma->cursor)); memset(&dma->ccs_config, 0, sizeof(dma->ccs_config)); memset(&dma->histogram_config, 0, sizeof(dma->histogram_config)); return ret; }
int mdp3_intf_init(struct mdp3_intf *intf) { switch (intf->cfg.type) { case MDP3_DMA_OUTPUT_SEL_LCDC: intf->config = lcdc_config; intf->start = lcdc_start; intf->stop = lcdc_stop; break; case MDP3_DMA_OUTPUT_SEL_DSI_VIDEO: intf->config = dsi_video_config; intf->start = dsi_video_start; intf->stop = dsi_video_stop; break; case MDP3_DMA_OUTPUT_SEL_DSI_CMD: intf->config = dsi_cmd_config; intf->start = dsi_cmd_start; intf->stop = dsi_cmd_stop; break; default: return -EINVAL; } return 0; }
4 mipi dsi host controll
5 mipi dsi panel controll
6 interface for userspace
7 Memory Management of multimedia of display
7.1 probe of mdss_fb
[cpp] view plaincopyprint? /* * alloc framebuffer info + par data */ fbi = framebuffer_alloc(sizeof(struct msm_fb_data_type), NULL);
The main and large memory allocation is placed in mdss_fb_register.
fix->line_length = var->xres * bpp;
7.1.2 Now it's time to allocate framebuffer memory, the function is mdss_fb_alloc_fbmem in mdss_fb_register.
static int mdss_fb_alloc_fbmem(struct msm_fb_data_type *mfd) { if (mfd->mdp.fb_mem_alloc_fnc) return mfd->mdp.fb_mem_alloc_fnc(mfd); else if (mfd->mdp.fb_mem_get_iommu_domain) { int dom = mfd->mdp.fb_mem_get_iommu_domain(); if (dom >= 0) return mdss_fb_alloc_fbmem_iommu(mfd, dom); else return -ENOMEM; } else { pr_err("no fb memory allocator function defined\n"); return -ENOMEM; } }
Because the function pointer fb_mem_alloc_fnc is registered in mdp3_probe in mdp3.c,
.fb_mem_alloc_fnc = mdp3_fbmem_alloc,
he above function will be called. The implementation is as below,
static int mdp3_fbmem_alloc(struct msm_fb_data_type *mfd) { int ret = -ENOMEM, dom; void *virt = NULL; unsigned long phys = 0; size_t size; u32 yres = mfd->fbi->var.yres_virtual; size = PAGE_ALIGN(mfd->fbi->fix.line_length * yres); ...... ret = mdp3_alloc(size, &virt, &phys); ...... dom = (mdp3_res->domains + MDP3_IOMMU_DOMAIN)->domain_idx; ret = ion_map_iommu(mdp3_res->ion_client, mdp3_res->ion_handle, dom, 0, SZ_4K, 0, &mfd->iova, (unsigned long *)&size, 0, 0); ...... mfd->fbi->screen_base = virt; mfd->fbi->fix.smem_start = phys; mfd->fbi->fix.smem_len = size; return 0; ...... }
void *ion_map_kernel(struct ion_client *client, struct ion_handle 8 *handle, unsigned long flags);
ion_phys returns the physical address of the Ion buffer associated with the Ion handle. The 12 ion_phys function type definition is shown below. Its output is only correct if the heap returns 13 physically contiguous memory. In other cases, this API should not be implemented.
int ion_phys(struct ion_client *client, struct ion_handle *handle, ion_phys_addr_t *addr, size_t *len);
The implementation of mdp3_alloc is as follow:
static int mdp3_alloc(size_t size, void **virt, unsigned long *phys) { int ret = 0; if (mdp3_res->ion_handle) { pr_debug("memory already alloc\n"); *virt = mdp3_res->virt; *phys = mdp3_res->phys; return 0; } mdp3_res->ion_handle = ion_alloc(mdp3_res->ion_client, size, SZ_1M, ION_HEAP(ION_QSECOM_HEAP_ID), 0); if (!IS_ERR_OR_NULL(mdp3_res->ion_handle)) { *virt = ion_map_kernel(mdp3_res->ion_client, mdp3_res->ion_handle); ...... ret = ion_phys(mdp3_res->ion_client, mdp3_res->ion_handle, phys, &size); ...... mdp3_res->virt = *virt; mdp3_res->phys = *phys; mdp3_res->size = size; } else ...... return 0; ...... return -ENOMEM; }
Regarding to ion spec, the doc 80-N9225-1_D_Intro_Ion_APIs.pdf can be refered to. I pasted some introduces from this documents
int ion_map_iommu(struct ion_client *client, struct ion_handle *handle, int domain_num, int partition_num, unsigned long align, unsigned long iova_length, unsigned long *iova, unsigned long *buffer_size, unsigned long flags, unsigned long iommu_flags)
Domains
A domain represents the IOMMU virtual memory space. Each domain has its own virtual memory space in the IOMMU map. Four domains are defined in MSM8960 Android BSP release 1711, which means each context bank of 12 IOMMUs in the MSM8960 should be mapped into one of the four virtual memory spaces.
The number of domains and domain enumerations are located in the file arch/arm/mach-msm/include/mach/iommu_domains.h.
enum { VIDEO_DOMAIN, CAMERA_DOMAIN, DISPLAY_DOMAIN, ROTATOR_DOMAIN, MAX_DOMAINS };
When an Ion client requests IOMMU virtual→physical mapping, it must specify the domain to which the Ion client wants to draw the virtual→physical map; e.g., the MDP display driver specifies the DISPLAY_DOMAIN domain when it requests the IOMMU virtual map. The following code snippet is used:
ion_map_iommu(display_iclient, *srcp_ihdl, DISPLAY_DOMAIN, GEN_POOL, SZ_4K, 23 0, start, len, 0, ION_IOMMU_UNMAP_DELAYED);
Partitions
A partition is a virtual address window in a domain. Therefore, a different partition means a different virtual address window in the 4 GB memory space. When an Ion client requests virtual physical mapping, the client specifies the partition to which the client wants the virtual memory address to belong. There are currently three partitions. Each one has the following virtual address window (start address and size):
VIDEO_FIRMWARE_POOL 32
|----Virtual addr start = SZ_128K 33
|----Size of virtual address window = SZ_16M to SZ_128K
VIDEO_MAIN_POOL
|----Virtual addr start = SZ_16M
|----Size of virtual address window = SZ_256M to SZ_16M
GEN_POOL
|----Virtual addr start = SZ_256M
|----Size of virtual address window = SZ_2G to SZ_256M
For example, the MSM8960 video core firmware loading address must be less than 16 MB from the video core perspective because of the hardware requirement. The following code is an example to ensure that the virtual address belongs to the required address range. Here, VIDEO_FIRMWARE_POOL is passed to the ion_map_iommu call to ensure the virtual address range. The number and enumeration of the partition are located in the file arch/arm/mach-msm/include/mach/iommu_domains.h.
ion_map_iommu(ddl_context->video_ion_client, addr-13 >alloc_handle, VIDEO_DOMAIN, VIDEO_FIRMWARE_POOL, SZ_4K, 15 0, &iova, &buffer_size, UNCACHED, 0);
7.1.3 Allocate struct msm_fb_back_type
mfd->msm_fb_backup = kzalloc(sizeof(struct msm_fb_backup_type), GFP_KERNEL);
7.1.4 allocate cmap
ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
7.2 probe of mdp3
mdp3_res = devm_kzalloc(&pdev->dev, sizeof(struct mdp3_hw_resource), GFP_KERNEL);
static int mdp3_res_init(void) { int rc = 0; rc = mdp3_irq_setup(); <p> ......</p> rc = mdp3_clk_setup(); ...... mdp3_res->ion_client = msm_ion_client_create(-1, mdp3_res->pdev->name); ...... rc = mdp3_iommu_init(); ...... mdp3_res->bus_handle = mdp3_bus_handle; rc = mdp3_bus_scale_register(); ...... rc = mdp3_hw_init(); return rc; }
The kernel module creates a new client by calling ion_client_create. The function definition msm_ion_client_create is the wrapper function of the generic API ion_client_create:
struct ion_client *ion_client_create(struct ion_device *dev, unsigned int heap_mask, const char *name)
heap_mask indicates the type of heap that this client should allocate from all of the different heap types such ascarveout, system heap, system contig, IOMMU, etc. Once the client is created, ion_alloc can be used to allocate the actual memory out of the heap. ion_alloc is defined as:
struct ion_handle *ion_alloc(struct ion_client,*client, size_t len, size_t align, unsigned int flags);
int mdp3_iommu_domain_init(void) { struct msm_iova_layout layout; int i; if (mdp3_res->domains) { pr_warn("iommu domain already initialized\n"); return 0; } for (i = 0; i < MDP3_IOMMU_DOMAIN_MAX; i++) { int domain_idx; layout.client_name = mdp3_iommu_domains[i].client_name; layout.partitions = mdp3_iommu_domains[i].partitions; layout.npartitions = mdp3_iommu_domains[i].npartitions; layout.is_secure = false; domain_idx = msm_register_domain(&layout); if (IS_ERR_VALUE(domain_idx)) return -EINVAL; mdp3_iommu_domains[i].domain_idx = domain_idx; mdp3_iommu_domains[i].domain = msm_get_iommu_domain(domain_idx); if (IS_ERR_OR_NULL(mdp3_iommu_domains[i].domain)) { pr_err("unable to get iommu domain(%d)\n", domain_idx); if (!mdp3_iommu_domains[i].domain) return -EINVAL; else return PTR_ERR(mdp3_iommu_domains[i].domain); } } mdp3_res->domains = mdp3_iommu_domains; return 0; }
int mdp3_iommu_context_init(void) { int i; if (mdp3_res->iommu_contexts) { pr_warn("iommu context already initialized\n"); return 0; } for (i = 0; i < MDP3_IOMMU_CTX_MAX; i++) { mdp3_iommu_contexts[i].ctx = msm_iommu_get_ctx(mdp3_iommu_contexts[i].ctx_name); if (IS_ERR_OR_NULL(mdp3_iommu_contexts[i].ctx)) { pr_warn("unable to get iommu ctx(%s)\n", mdp3_iommu_contexts[i].ctx_name); if (!mdp3_iommu_contexts[i].ctx) return -EINVAL; else return PTR_ERR(mdp3_iommu_contexts[i].ctx); } } mdp3_res->iommu_contexts = mdp3_iommu_contexts; return 0; }
7.3 probe of mdss dsi(msm_dsi_probe)
dsi_host_private = kzalloc(sizeof(struct dsi_host_v2_private), GFP_KERNEL);
7.3.2 allocate dsi private io data in msm_dsi_io_init
dsi_io_private = kzalloc(sizeof(struct msm_dsi_io_private), GFP_KERNEL);
7.4 the allocation of dsi_panel_probe
int dsi_panel_init(void) { panel_private = kzalloc(sizeof(struct dsi_panel_private), GFP_KERNEL); dsi_buf_alloc(&panel_private->dsi_panel_tx_buf, ALIGN(DSI_BUF_SIZE, SZ_4K)); dsi_buf_alloc(&panel_private->dsi_panel_rx_buf, ALIGN(DSI_BUF_SIZE, SZ_4K)); return 0; }
dsi_panel_parse_init_cmds(platform_device *, dsi_panel_common_pdata *) : int
|-------dsi_panel_parse_dt(platform_device *, dsi_panel_common_pdata *, char *) : int
|--------------dsi_panel_probe(platform_device *) : int
|----------------------{init this_driver}() : platform_driver
static int dsi_panel_parse_init_cmds(struct platform_device *pdev, struct dsi_panel_common_pdata *panel_data) { struct device_node *np = pdev->dev.of_node; int i, len; int cmd_plen, data_offset; const char *data; const char *on_cmds_state, *off_cmds_state; int num_of_on_cmds = 0, num_of_off_cmds = 0; data = of_get_property(np, "qcom,panel-on-cmds", &len); panel_private->on_cmds = kzalloc(sizeof(char) * len, GFP_KERNEL); memcpy(panel_private->on_cmds, data, len); data_offset = 0; cmd_plen = 0; while ((len - data_offset) >= DT_CMD_HDR) { data_offset += (DT_CMD_HDR - 1); cmd_plen = panel_private->on_cmds[data_offset++]; data_offset += cmd_plen; num_of_on_cmds++; } panel_private->on_cmds_list = kzalloc(sizeof(struct dsi_panel_cmds_list), GFP_KERNEL); panel_private->on_cmds_list->buf = kzalloc((num_of_on_cmds * sizeof(struct dsi_cmd_desc)), GFP_KERNEL); data_offset = 0; for (i = 0; i < num_of_on_cmds; i++) { panel_private->on_cmds_list->buf[i].dtype = panel_private->on_cmds[data_offset++]; panel_private->on_cmds_list->buf[i].last = panel_private->on_cmds[data_offset++]; panel_private->on_cmds_list->buf[i].vc = panel_private->on_cmds[data_offset++]; panel_private->on_cmds_list->buf[i].ack = panel_private->on_cmds[data_offset++]; panel_private->on_cmds_list->buf[i].wait = panel_private->on_cmds[data_offset++]; panel_private->on_cmds_list->buf[i].dlen = panel_private->on_cmds[data_offset++]; panel_private->on_cmds_list->buf[i].payload = &panel_private->on_cmds[data_offset]; data_offset += (panel_private->on_cmds_list->buf[i].dlen); } panel_private->on_cmds_list->size = num_of_on_cmds; on_cmds_state = of_get_property(pdev->dev.of_node, "qcom,on-cmds-dsi-state", NULL); if (!strncmp(on_cmds_state, "DSI_LP_MODE", 11)) { panel_private->on_cmds_list->ctrl_state = DSI_LP_MODE; } else if (!strncmp(on_cmds_state, "DSI_HS_MODE", 11)) { panel_private->on_cmds_list->ctrl_state = DSI_HS_MODE; } else { pr_debug("%s: ON cmds state not specified. Set Default\n", __func__); panel_private->on_cmds_list->ctrl_state = DSI_LP_MODE; } panel_data->dsi_panel_on_cmds = panel_private->on_cmds_list; data = of_get_property(np, "qcom,panel-off-cmds", &len); panel_private->off_cmds = kzalloc(sizeof(char) * len, GFP_KERNEL); memcpy(panel_private->off_cmds, data, len); data_offset = 0; cmd_plen = 0; while ((len - data_offset) >= DT_CMD_HDR) { data_offset += (DT_CMD_HDR - 1); cmd_plen = panel_private->off_cmds[data_offset++]; data_offset += cmd_plen; num_of_off_cmds++; } panel_private->off_cmds_list = kzalloc(sizeof(struct dsi_panel_cmds_list), GFP_KERNEL); panel_private->off_cmds_list->buf = kzalloc(num_of_off_cmds * sizeof(struct dsi_cmd_desc), GFP_KERNEL); data_offset = 0; for (i = 0; i < num_of_off_cmds; i++) { panel_private->off_cmds_list->buf[i].dtype = panel_private->off_cmds[data_offset++]; panel_private->off_cmds_list->buf[i].last = panel_private->off_cmds[data_offset++]; panel_private->off_cmds_list->buf[i].vc = panel_private->off_cmds[data_offset++]; panel_private->off_cmds_list->buf[i].ack = panel_private->off_cmds[data_offset++]; panel_private->off_cmds_list->buf[i].wait = panel_private->off_cmds[data_offset++]; panel_private->off_cmds_list->buf[i].dlen = panel_private->off_cmds[data_offset++]; panel_private->off_cmds_list->buf[i].payload = &panel_private->off_cmds[data_offset]; data_offset += (panel_private->off_cmds_list->buf[i].dlen); } panel_private->off_cmds_list->size = num_of_off_cmds; off_cmds_state = of_get_property(pdev->dev.of_node, "qcom,off-cmds-dsi-state", NULL); if (!strncmp(off_cmds_state, "DSI_LP_MODE", 11)) { panel_private->off_cmds_list->ctrl_state = DSI_LP_MODE; } else if (!strncmp(off_cmds_state, "DSI_HS_MODE", 11)) { panel_private->off_cmds_list->ctrl_state = DSI_HS_MODE; } else { pr_debug("%s: ON cmds state not specified. Set Default\n", __func__); panel_private->off_cmds_list->ctrl_state = DSI_LP_MODE; } panel_data->dsi_panel_off_cmds = panel_private->off_cmds_list; return 0; }
7.5 unblank lcd: FB_BLANK_UNBLANK
case FBIOBLANK: if (!lock_fb_info(info)) return -ENODEV; console_lock(); info->flags |= FBINFO_MISC_USEREVENT; ret = fb_blank(info, arg); info->flags &= ~FBINFO_MISC_USEREVENT; console_unlock(); unlock_fb_info(info); break;
if (info->fbops->fb_blank) ret = info->fbops->fb_blank(blank, info);
The function pointer is registered in mdss_fb.c
static struct fb_ops mdss_fb_ops = { .owner = THIS_MODULE, .fb_open = mdss_fb_open, .fb_release = mdss_fb_release, .fb_check_var = mdss_fb_check_var, /* vinfo check */ .fb_set_par = mdss_fb_set_par, /* set the video mode */ .fb_blank = mdss_fb_blank, /* blank display */ .fb_pan_display = mdss_fb_pan_display, /* pan display */ .fb_ioctl = mdss_fb_ioctl, /* perform fb specific ioctl */ .fb_mmap = mdss_fb_mmap, };
static int mdss_fb_blank(int blank_mode, struct fb_info *info) { struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; mdss_fb_pan_idle(mfd); if (mfd->op_enable == 0) { if (blank_mode == FB_BLANK_UNBLANK) mfd->suspend.panel_power_on = true; else mfd->suspend.panel_power_on = false; return 0; } return mdss_fb_blank_sub(blank_mode, info, mfd->op_enable); }
If lcd is to be opened, the following case will be called.
If lcd is to be opened, the following case will be called.
msm_mdp_interface::on_fnc : int (*)(msm_fb_data_type *)
|--------mdp3_ctrl_init(msm_fb_data_type *) : int
|----------------mdp3_init(msm_fb_data_type *) : int
|-----------------------mdp3_probe(platform_device *) : int
|---------------------------{init mdp3_driver}() : platform_driver
|--------mdss_fb_blank_sub(int, fb_info *, int) : int (2 matches)
|----------------mdss_fb_blank(int, fb_info *) : int
|-----------------------{init mdss_fb_ops}() : fb_ops
|----------------mdss_fb_open(fb_info *, int) : int
|----------------mdss_fb_release(fb_info *, int) : int
|----------------mdss_fb_resume_sub(msm_fb_data_type *) : int
|----------------mdss_fb_set_par(fb_info *) : int (2 matches)
|----------------mdss_fb_suspend_sub(msm_fb_data_type *) : int
|--------mdss_fb_dcm(msm_fb_data_type *, int) : int (2 matches)
|--------mdss_mdp_overlay_init(msm_fb_data_type *) : int
|--------mdss_qpic_overlay_init(msm_fb_data_type *) : int
Then the on_fnc is called, as follow:
mdp3_interface->on_fnc = mdp3_ctrl_on; mdp3_interface->off_fnc = mdp3_ctrl_off; mdp3_interface->do_histogram = NULL; mdp3_interface->cursor_update = NULL; mdp3_interface->dma_fnc = mdp3_ctrl_pan_display; mdp3_interface->ioctl_handler = mdp3_ctrl_ioctl_handler; mdp3_interface->kickoff_fnc = mdp3_ctrl_display_commit_kickoff; mdp3_interface->lut_update = mdp3_ctrl_lut_update;
static int mdp3_dma_start(struct mdp3_dma *dma, struct mdp3_intf *intf) { unsigned long flag; int cb_type = MDP3_DMA_CALLBACK_TYPE_VSYNC; u32 dma_start_offset = MDP3_REG_DMA_P_START; if (dma->dma_sel == MDP3_DMA_P) dma_start_offset = MDP3_REG_DMA_P_START; else if (dma->dma_sel == MDP3_DMA_S) dma_start_offset = MDP3_REG_DMA_S_START; else return -EINVAL; spin_lock_irqsave(&dma->dma_lock, flag); if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) { cb_type |= MDP3_DMA_CALLBACK_TYPE_DMA_DONE; MDP3_REG_WRITE(dma_start_offset, 1); } intf->start(intf); wmb(); init_completion(&dma->vsync_comp); spin_unlock_irqrestore(&dma->dma_lock, flag); mdp3_dma_callback_enable(dma, cb_type); pr_debug("mdp3_dma_start wait for vsync_comp in\n"); wait_for_completion_killable(&dma->vsync_comp); pr_debug("mdp3_dma_start wait for vsync_comp out\n"); return 0; }
7.6 Now it's time to introduce overlay play and commit
8. suspend and resume
static struct platform_driver mdss_fb_driver = { .probe = mdss_fb_probe, .remove = mdss_fb_remove, .suspend = mdss_fb_suspend, .resume = mdss_fb_resume, .shutdown = mdss_fb_shutdown, .driver = { .name = "mdss_fb", .of_match_table = mdss_fb_dt_match, .pm = &mdss_fb_pm_ops, }, };
static const struct dev_pm_ops mdss_fb_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(mdss_fb_pm_suspend, mdss_fb_pm_resume) };
.suspend = mdss_fb_pm_suspend, \ .resume = mdss_fb_pm_resume, \ .freeze = mdss_fb_pm_suspend, \ .thaw = mdss_fb_pm_resume, \ .poweroff = mdss_fb_pm_suspend, \ .restore = mdss_fb_pm_resume,
#if defined(CONFIG_PM) && !defined(CONFIG_PM_SLEEP) /* CONFIG_PM=y and CONFIG_PM_SLEEP=y*/ static int mdss_fb_suspend(struct platform_device *pdev, pm_message_t state) { struct msm_fb_data_type *mfd = platform_get_drvdata(pdev); if (!mfd) return -ENODEV; dev_dbg(&pdev->dev, "display suspend\n"); return mdss_fb_suspend_sub(mfd); } static int mdss_fb_resume(struct platform_device *pdev) { struct msm_fb_data_type *mfd = platform_get_drvdata(pdev); if (!mfd) return -ENODEV; dev_dbg(&pdev->dev, "display resume\n"); return mdss_fb_resume_sub(mfd); } #else #define mdss_fb_suspend NULL #define mdss_fb_resume NULL #endif #ifdef CONFIG_PM_SLEEP /* CONFIG_PM_SLEEP = y */ static int mdss_fb_pm_suspend(struct device *dev) { struct msm_fb_data_type *mfd = dev_get_drvdata(dev); if (!mfd) return -ENODEV; dev_dbg(dev, "display pm suspend\n"); return mdss_fb_suspend_sub(mfd); } static int mdss_fb_pm_resume(struct device *dev) { struct msm_fb_data_type *mfd = dev_get_drvdata(dev); if (!mfd) return -ENODEV; dev_dbg(dev, "display pm resume\n"); return mdss_fb_resume_sub(mfd); } #endif
9 Automatically generate lcd initial commands to dtsi
#perl parser.pl <.xml> <panel/platform>
Use the following command to generate the panel dtsi and header files:
#perl parser.pl panel_cmd.xml panel
This command generates dsi-panel-cmd.dtsi and panel_cmd.h files. Dtsi should be copied to dts folder @//~/kernel/arch/arm/boot/dts while header file should be copied to bootloader GCDB header file database @//~/bootable/bootloader/lk/dev/gcdb/display/include.
Use the following command to generate the platform dtsi and header files for platform-msm8610.xml:
#perl parser.pl platform-msm8610.xml platform
This command generates platform_msm8610.h and platform-msm8610.dtsi files. The content of the dtsi file should be copied to <target>-mdss.dtsi in @//~/kernel/arch/arm/boot/dts while the content of the header file should be copied to @//~/bootable/bootloader/lk/target/<target>/include/target/display.h for LK. In this example, <target> can be replaced with msm8610.
This process can be followed for any new display and platform, by updating the XML files. This process 16 is also documented at @//~/device/qcom/common/display/tools/README.txt.
10 vsync controlling of mdp
11 lcd common resolution of mobile phone
The display resolution of a digital television, computer monitor or display device is the number of distinct pixels in each dimension that can be displayed. It can be an ambiguous term especially as the displayed resolution is controlled by different factors in cathode ray tube(CRT), Flat panel display which includes Liquid crystal displays, or projection displays using fixed picture-element (pixel) arrays.
It is usually quoted as width × height, with the units in pixels: for example, "1024 × 768" means the width is 1024pixelsand the height is 768 pixels. This example would normally be spoken as "ten twenty-four by seven sixty-eight" or "ten twenty-four by seven six eight".
One use of the term “display resolution” applies to fixed-pixel-array displays such as plasma display panels (PDPs), liquid crystal displays (LCDs), digital light processing (DLP) projectors, or similar technologies, and is simply the physical number of columns and rows of pixels creating the display (e.g., 1920 × 1080). A consequence of having a fixed-grid display is that, for multi-format video inputs, all displays need a "scaling engine" (a digital video processor that includes a memory array) to match the incoming picture format to the display.
Note that for broadcast television standards the use of the word resolution here is a misnomer, though common. The term “display resolution” is usually used to mean pixel dimensions, the number of pixels in each dimension (e.g., 1920 × 1080), which does not tell anything about the pixel density of the display on which the image is actually formed: broadcast television resolution properly refers to the pixel density, the number of pixels per unit distance or area, not total number of pixels. In digital measurement, the display resolution would be given in pixels per inch. In analog measurement, if the screen is 10 inches high, then the horizontal resolution is measured across a square 10 inches wide. This is typically stated as "lines horizontal resolution, per picture height;"[1] for example, analog NTSC TVs can typically display about 340 lines of "per picture height" horizontal resolution from over-the-air sources, which is equivalent to about 440 total lines of actual picture information from left edge to right edge.
10 vsync controlling of mdp
11 lcd common resolution of mobile phone
The display resolution of a digital television, computer monitor or display device is the number of distinct pixels in each dimension that can be displayed. It can be an ambiguous term especially as the displayed resolution is controlled by different factors in cathode ray tube(CRT), Flat panel display which includes Liquid crystal displays, or projection displays using fixed picture-element (pixel) arrays.
It is usually quoted as width × height, with the units in pixels: for example, "1024 × 768" means the width is 1024pixelsand the height is 768 pixels. This example would normally be spoken as "ten twenty-four by seven sixty-eight" or "ten twenty-four by seven six eight".
One use of the term “display resolution” applies to fixed-pixel-array displays such as plasma display panels (PDPs), liquid crystal displays (LCDs), digital light processing (DLP) projectors, or similar technologies, and is simply the physical number of columns and rows of pixels creating the display (e.g., 1920 × 1080). A consequence of having a fixed-grid display is that, for multi-format video inputs, all displays need a "scaling engine" (a digital video processor that includes a memory array) to match the incoming picture format to the display.
Note that for broadcast television standards the use of the word resolution here is a misnomer, though common. The term “display resolution” is usually used to mean pixel dimensions, the number of pixels in each dimension (e.g., 1920 × 1080), which does not tell anything about the pixel density of the display on which the image is actually formed: broadcast television resolution properly refers to the pixel density, the number of pixels per unit distance or area, not total number of pixels. In digital measurement, the display resolution would be given in pixels per inch. In analog measurement, if the screen is 10 inches high, then the horizontal resolution is measured across a square 10 inches wide. This is typically stated as "lines horizontal resolution, per picture height;"[1] for example, analog NTSC TVs can typically display about 340 lines of "per picture height" horizontal resolution from over-the-air sources, which is equivalent to about 440 total lines of actual picture information from left edge to right edge.
12 dts and dtsi about lcd
~/mountpoint/project_xxxx/kernel/arch/arm/boot$ ll dts/dsi-panel-* -rw-r--r-- 1 yanghaibing users 3231 2013-11-06 06:42 dts/dsi-panel-generic-720p-cmd.dtsi -rw-r--r-- 1 yanghaibing users 3640 2013-11-06 06:42 dts/dsi-panel-hx8379a-wvga-video.dtsi -rw-r--r-- 1 yanghaibing users 3632 2013-11-06 06:42 dts/dsi-panel-hx8394a-720p-video.dtsi -rw-r--r-- 1 yanghaibing users 10926 2013-11-06 06:42 dts/dsi-panel-nt35521-720p-video.dtsi -rw-r--r-- 1 yanghaibing users 17782 2013-11-06 06:42 dts/dsi-panel-nt35590-720p-cmd.dtsi -rw-r--r-- 1 yanghaibing users 17623 2013-11-06 06:42 dts/dsi-panel-nt35590-720p-video.dtsi -rw-r--r-- 1 yanghaibing users 18348 2013-11-06 06:42 dts/dsi-panel-nt35596-1080p-video.dtsi -rw-r--r-- 1 yanghaibing users 2360 2013-11-06 06:42 dts/dsi-panel-orise-720p-video.dtsi -rw-r--r-- 1 yanghaibing users 6495 2013-11-06 06:42 dts/dsi-panel-otm8018b-fwvga-video.dtsi -rw-r--r-- 1 yanghaibing users 2911 2013-11-06 06:42 dts/dsi-panel-sharp-qhd-video.dtsi -rw-r--r-- 1 yanghaibing users 1663 2013-11-06 06:42 dts/dsi-panel-sim-video.dtsi -rw-r--r-- 1 yanghaibing users 4720 2013-11-06 06:42 dts/dsi-panel-ssd2080m-720p-video.dtsi -rw-r--r-- 1 yanghaibing users 4122 2013-11-06 06:42 dts/dsi-panel-toshiba-720p-video.dtsi -rw-r--r-- 1 yanghaibing users 5314 2013-11-06 06:42 dts/dsi-panel-truly-wvga-cmd.dtsi -rw-r--r-- 1 yanghaibing users 5149 2013-11-06 06:42 dts/dsi-panel-truly-wvga-video.dtsi [plain] view plaincopyprint? ~/mountpoint/project_xxxx/kernel/arch/arm/boot$ ll dts/dsi-v2-panel-* -rw-r--r-- 1 yanghaibing users 3290 2013-11-06 06:42 dts/dsi-v2-panel-hx8379a-wvga-video.dtsi -rw-r--r-- 1 yanghaibing users 15730 2013-11-06 06:42 dts/dsi-v2-panel-nt35590-720p-video.dtsi -rw-r--r-- 1 yanghaibing users 5817 2013-09-27 06:00 dts/dsi-v2-panel-otm8018b-fwvga-video.dtsi -rw-r--r-- 1 yanghaibing users 4659 2013-09-25 06:11 dts/dsi-v2-panel-truly-wvga-cmd.dtsi -rw-r--r-- 1 yanghaibing users 4583 2013-09-25 06:11 dts/dsi-v2-panel-truly-wvga-video.dtsi [plain] view plaincopyprint? yanghaibing@njyjs-cm:~/mountpoint/project_8210/kernel/arch/arm/boot/dts$ ll msm8610* -rw-r--r-- 1 yanghaibing users 19506 2013-11-06 06:42 msm8610-bus.dtsi -rw-r--r-- 1 yanghaibing users 1941 2013-09-25 06:11 msm8610-camera.dtsi -rw-r--r-- 1 yanghaibing users 10757 2013-11-06 06:42 msm8610-camera-sensor-cdp-mtp.dtsi -rw-r--r-- 1 yanghaibing users 9670 2013-11-06 06:42 msm8610-cdp.dtsi -rw-r--r-- 1 yanghaibing users 8723 2013-11-06 06:42 msm8610-coresight.dtsi -rw-r--r-- 1 yanghaibing users 28159 2013-11-06 06:42 msm8610.dtsi -rw-r--r-- 1 yanghaibing users 4186 2013-09-25 06:11 msm8610-gpu.dtsi -rw-r--r-- 1 yanghaibing users 1112 2013-09-25 06:11 msm8610-iommu-domains.dtsi -rw-r--r-- 1 yanghaibing users 1326 2013-09-25 06:11 msm8610-ion.dtsi -rw-r--r-- 1 yanghaibing users 2801 2013-11-06 06:42 msm8610-mdss.dtsi -rw-r--r-- 1 yanghaibing users 755 2013-11-06 06:42 msm8610-mdss-panels.dtsi -rw-r--r-- 1 yanghaibing users 9910 2013-11-06 06:42 msm8610-mtp.dtsi -rw-r--r-- 1 yanghaibing users 3187 2013-11-06 06:42 msm8610-qrd-camera-sensor.dtsi -rw-r--r-- 1 yanghaibing users 8959 2013-11-06 06:42 msm8610-qrd.dtsi -rw-r--r-- 1 yanghaibing users 1671 2013-11-06 06:42 msm8610-qrd-skuaa.dtsi -rw-r--r-- 1 yanghaibing users 3117 2013-11-06 06:42 msm8610-qrd-skuab.dtsi -rw-r--r-- 1 yanghaibing users 9764 2013-11-06 06:42 msm8610-regulator.dtsi -rw-r--r-- 1 yanghaibing users 794 2013-09-25 06:11 msm8610-rumi.dts -rw-r--r-- 1 yanghaibing users 1986 2013-11-06 06:42 msm8610-sim.dts -rw-r--r-- 1 yanghaibing users 5657 2013-09-25 06:11 msm8610-smp2p.dtsi -rw-r--r-- 1 yanghaibing users 850 2013-11-06 06:42 msm8610-v1-cdp.dts -rw-r--r-- 1 yanghaibing users 861 2013-11-06 06:42 msm8610-v1.dtsi -rw-r--r-- 1 yanghaibing users 850 2013-11-06 06:42 msm8610-v1-mtp.dts -rw-r--r-- 1 yanghaibing users 8892 2013-11-06 06:42 msm8610-v1-pm.dtsi -rw-r--r-- 1 yanghaibing users 791 2013-11-06 06:42 msm8610-v1-qrd-skuaa.dts -rw-r--r-- 1 yanghaibing users 783 2013-11-06 06:42 msm8610-v1-qrd-skuab.dts -rw-r--r-- 1 yanghaibing users 894 2013-11-06 06:42 msm8610-v2-cdp.dts -rw-r--r-- 1 yanghaibing users 906 2013-11-06 06:42 msm8610-v2.dtsi -rw-r--r-- 1 yanghaibing users 894 2013-11-06 06:42 msm8610-v2-mtp.dts -rw-r--r-- 1 yanghaibing users 7974 2013-11-06 06:42 msm8610-v2-pm.dtsi -rw-r--r-- 1 yanghaibing users 792 2013-11-06 06:42 msm8610-v2-qrd-skuaa.dts -rw-r--r-- 1 yanghaibing users 783 2013-11-06 06:42 msm8610-v2-qrd-skuab.dts
static const struct of_device_id msm_dsi_v2_dt_match[] = { {.compatible = "qcom,msm-dsi-v2"}, {} }; MODULE_DEVICE_TABLE(of, msm_dsi_v2_dt_match); static struct platform_driver msm_dsi_v2_driver = { .probe = msm_dsi_probe, .remove = __devexit_p(msm_dsi_remove), .shutdown = NULL, .driver = { .name = "msm_dsi_v2", .of_match_table = msm_dsi_v2_dt_match, }, };
mdss_dsi.c
static const struct of_device_id mdss_dsi_ctrl_dt_match[] = { {.compatible = "qcom,mdss-dsi-ctrl"}, {} }; MODULE_DEVICE_TABLE(of, mdss_dsi_ctrl_dt_match); static struct platform_driver mdss_dsi_ctrl_driver = { .probe = mdss_dsi_ctrl_probe, .remove = __devexit_p(mdss_dsi_ctrl_remove), .shutdown = NULL, .driver = { .name = "mdss_dsi_ctrl", .of_match_table = mdss_dsi_ctrl_dt_match, }, };
&soc { mdss_mdp: qcom,mdss_mdp@fd900000 { compatible = "qcom,mdss_mdp3"; reg = <0xfd900000 0x100000>; reg-names = "mdp_phys"; interrupts = <0 72 0>; mdss_fb0: qcom,mdss_fb_primary { cell-index = <0>; compatible = "qcom,mdss-fb"; qcom,memblock-reserve = <0x3200000 0x800000>; }; }; mdss_dsi0: qcom,mdss_dsi@fdd00000 { compatible = "qcom,msm-dsi-v2"; label = "MDSS DSI CTRL->0"; cell-index = <0>; reg = <0xfdd00000 0x100000>; interrupts = <0 30 0>; vdd-supply = <&pm8110_l4>; vdda-supply = <&pm8110_l19>; vddio-supply = <&pm8110_l14>; qcom,mdss-fb-map = <&mdss_fb0>; qcom,mdss-mdp = <&mdss_mdp>; qcom,platform-reset-gpio = <&msmgpio 41 0>; /* qcom,platform-te-gpio = <&msmgpio 12 0>; qcom,platform-mode-gpio = <&msmgpio 7 0>; */ qcom,platform-reset-sequence = <1 20 0 2 1 20 2>; qcom,platform-strength-ctrl = [ff 06]; qcom,platform-bist-ctrl = [03 03 00 00 0f 00]; qcom,platform-regulator-settings = [02 08 05 00 20 03]; qcom,platform-lane-config = [80 45 00 00 01 66 80 45 00 00 01 66 80 45 00 00 01 66 80 45 00 00 01 66 40 67 00 00 01 88]; qcom,platform-supply-entry1 { qcom,supply-name = "vdd"; qcom,supply-min-voltage = <1200000>; qcom,supply-max-voltage = <1200000>; qcom,supply-enable-load = <100000>; qcom,supply-disable-load = <100>; qcom,supply-pre-on-sleep = <0>; qcom,supply-post-on-sleep = <20>; qcom,supply-pre-off-sleep = <0>; qcom,supply-post-off-sleep = <20>; }; qcom,platform-supply-entry2 { qcom,supply-name = "vddio"; qcom,supply-min-voltage = <1800000>; qcom,supply-max-voltage = <1800000>; qcom,supply-enable-load = <100000>; qcom,supply-disable-load = <100>; qcom,supply-pre-on-sleep = <0>; qcom,supply-post-on-sleep = <0>; qcom,supply-pre-off-sleep = <0>; qcom,supply-post-off-sleep = <0>; }; qcom,platform-supply-entry3 { qcom,supply-name = "vdda"; qcom,supply-min-voltage = <2850000>; qcom,supply-max-voltage = <2850000>; qcom,supply-enable-load = <100000>; qcom,supply-disable-load = <100>; qcom,supply-pre-on-sleep = <0>; qcom,supply-post-on-sleep = <0>; qcom,supply-pre-off-sleep = <0>; qcom,supply-post-off-sleep = <0>; }; }; }; /include/ "msm8610-mdss-panels.dtsi"
msm8610-mdss-panels.dtsi (The default version is not v2.) So this dtsi will be related tomdss_dsi_panel.c, rather thandsi_panel_v2.c
/include/ "dsi-panel-truly-wvga-video.dtsi" /include/ "dsi-panel-truly-wvga-cmd.dtsi" /include/ "dsi-panel-nt35590-720p-video.dtsi" /include/ "dsi-panel-otm8018b-fwvga-video.dtsi" /include/ "dsi-panel-hx8379a-wvga-video.dtsi"
13 Actual memory allocated size of multimedia of lcd
if (iclient) { data->srcp_ihdl = ion_import_dma_buf(iclient, img->memory_id); if (IS_ERR_OR_NULL(data->srcp_ihdl)) { <span style="white-space:pre"> </span>...... } if (client == MDP3_CLIENT_DMA_P) { /* Here is the called path */ dom = (mdp3_res->domains + MDP3_DMA_IOMMU_DOMAIN)->domain_idx; ret = ion_map_iommu(iclient, data->srcp_ihdl, dom, /* Fetch the length */ 0, SZ_4K, 0, start, len, 0, 0); } else { ret = mdp3_self_map_iommu(iclient, data->srcp_ihdl, SZ_4K, data->padding, start, len, 0, 0); } if (IS_ERR_VALUE(ret)) { <span style="white-space:pre"> </span>...... }
|--------mdp3_overlay_queue_buffer(msm_fb_data_type *, msmfb_overlay_data *) : int
|----------------mdp3_overlay_play(msm_fb_data_type *, msmfb_overlay_data *) : int
|------------------------mdp3_ctrl_ioctl_handler(msm_fb_data_type *, u32, void *) : int
|--------------------------------mdp3_ctrl_init(msm_fb_data_type *) : int
|
14 If reset isn't called first, sometimes slight flower screen would appear on lcd
qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; qcom,mdss-dsi-off-command-state = "dsi_lp_mode"; qcom,mdss-dsi-h-sync-pulse = <0>; qcom,mdss-dsi-traffic-mode = <1>; qcom,mdss-dsi-lane-map = <1>; qcom,mdss-dsi-bllp-eof-power-mode; qcom,mdss-dsi-bllp-power-mode; qcom,mdss-dsi-lane-0-state; qcom,mdss-dsi-lane-1-state; qcom,mdss-dsi-panel-timings = [8B 1F 14 00 45 4A 19 23 23 03 04 00]; qcom,mdss-dsi-t-clk-post = <0x04>; qcom,mdss-dsi-t-clk-pre = <0x1b>; qcom,mdss-dsi-bl-min-level = <1>; qcom,mdss-dsi-bl-max-level = <255>; qcom,mdss-dsi-dma-trigger = <4>; qcom,mdss-dsi-mdp-trigger = <0>; qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; qcom,mdss-dsi-reset-sequence = <1 20>, <0 2>, <1 20>; /* This field is necessory! */
|--------mdss_panel_parse_dt(device_node *, mdss_dsi_ctrl_pdata *) : int
|----------------mdss_dsi_panel_init(device_node *, mdss_dsi_ctrl_pdata *, int) : int
|-------------------------msm_dsi_probe(platform_device *) : int
|---------------------------------{init msm_dsi_v2_driver}() : platform_driver
static int mdss_dsi_parse_reset_seq(struct device_node *np, u32 rst_seq[MDSS_DSI_RST_SEQ_LEN], u32 *rst_len, const char *name) { int num = 0, i; int rc; struct property *data; u32 tmp[MDSS_DSI_RST_SEQ_LEN]; *rst_len = 0; data = of_find_property(np, name, &num); num /= sizeof(u32); if (!data || !num || num > MDSS_DSI_RST_SEQ_LEN || num % 2) { pr_debug("%s:%d, error reading %s, length found = %d\n", __func__, __LINE__, name, num); <span style="font-family:Arial,Helvetica,sans-serif; font-size:12px">/* I think here pr_debug should use pr_err. */</span> } else { rc = of_property_read_u32_array(np, name, tmp, num); if (rc) pr_debug("%s:%d, error reading %s, rc = %d\n", __func__, __LINE__, name, rc); /* I think here pr_debug should use pr_err. */ else { for (i = 0; i < num; ++i) rst_seq[i] = tmp[i]; *rst_len = num; } } return 0; }
The following code snippet sequences are as following: if lcd is enabled, reset -> on cmds tx; if lcd is disabled, reset gpio set 0, and -> off cmds tx
static int dsi_panel_handler(struct mdss_panel_data *pdata, int enable) { int rc = 0; struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; pr_debug("dsi_panel_handler enable=%d\n", enable); if (!pdata) return -ENODEV; ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data); if (enable) { dsi_ctrl_gpio_request(ctrl_pdata); mdss_dsi_panel_reset(pdata, 1); rc = dsi_cmds_tx_v2(pdata, &dsi_panel_tx_buf, ctrl_pdata->on_cmds.cmds, ctrl_pdata->on_cmds.cmd_cnt); if (rc) pr_err("dsi_panel_handler panel on failed %d\n", rc); } else { if (dsi_intf.op_mode_config) dsi_intf.op_mode_config(DSI_CMD_MODE, pdata); dsi_cmds_tx_v2(pdata, &dsi_panel_tx_buf, ctrl_pdata->off_cmds.cmds, ctrl_pdata->off_cmds.cmd_cnt); mdss_dsi_panel_reset(pdata, 0); dsi_ctrl_gpio_free(ctrl_pdata); } return rc; }
The following code snippet is an interface for mdp_ctrl.c. It implements dsi on/off, panel on/off and splash screen.
static int dsi_event_handler(struct mdss_panel_data *pdata, int event, void *arg) { int rc = 0; if (!pdata) { pr_err("%s: Invalid input data\n", __func__); return -ENODEV; } switch (event) { case MDSS_EVENT_UNBLANK: rc = dsi_on(pdata); break; case MDSS_EVENT_BLANK: rc = dsi_off(pdata); break; case MDSS_EVENT_PANEL_ON: rc = dsi_panel_handler(pdata, 1); break; case MDSS_EVENT_PANEL_OFF: rc = dsi_panel_handler(pdata, 0); break; case MDSS_EVENT_CONT_SPLASH_BEGIN: rc = dsi_splash_on(pdata); break; default: pr_debug("%s: unhandled event=%d\n", __func__, event); break; } return rc; }
15 tips
Tools→Options for xshell
delimitor: \ :;`!@#$%^&*()=+|[]{}'",<>?
How to enable the debugfs
In kernel debug, sometimes you might need the debugfs (CONFIG_DEBUG_FS)。
you can manually mount as the commands:
# mount -t debugfs /sys/kernel/debug