msm8610 lcd driver code analysis

The version of qualcomm code is LNX.LA.3.2-01430-8x10.0

1  lcd probe 

The probe sequence is determined by compilation sequence
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

c0ace4b4 t __initcall_ iommu_init4
c0ace4b8 t __initcall_ msm_iommu_init4
c0ace4bc t __initcall_ msm_iommu_driver_init4

c0ace7d0 t __initcall_percpu_counter_startup6
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)
msm_mdp_interface::on_fnc : int (*)(msm_fb_data_type *)
|------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

=========Flow===========
01) mdp iommu enable  ->
02) mipi dsi on(clk, porch, dsi host reset, init)  ->
03) mipi panel on(initial cmds) ->
04) mdp clk(core, vsync) enable ->
05) enable mdp irq ->
06) mdp dma config(interface: dma_config) ->
07) load ppp lut and csc matrix ->
08) config(intf, &cfg); ->
09) mdp3_fbmem_clear() ->
10) start dma_p(depending on video or command mode) For video mode, maybe the primary dma transfer is started automatically every vsync; For command mode, the corresponding register MMSS_MDP_DMA_P_START should be set to start a DMA_P channel transfer.
11)Now it's time to enable timing generation and kick start DSI_VIDEO operation: MDP3_REG_WRITE(MDP3_REG_DSI_VIDEO_EN, BIT(0));

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

7.1.1 allocate struct msm_fb_data_type
	/*
	 * 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,
the 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;
	......
}
Function mdp3_alloc get ion_handle, virtuall address and physical address from ion.
Once the Ion heap is allocated in the kernel side, the client has the Ion handle. But the client  would need the kernel virtual address to access the Ion buffer that is associated with the Ion handle. ion_map_kernel can be used for this purpose. The return value of ion_map_kernel is a virtual address that the kernel can read and write directly:
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();

......

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 as carveout, 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);

Here, flags are telling which ion_heap_id should be assigned for this allocation. One client can call ion_alloc multimple times, which means a single client can have muiltiple memory chunks out of Ion heap. This requires ion_handle to be unique per each memory chunk.

ion_alloc isn't called until mdp3_ctrl_on is called. After ion client is created, now it's time to initialize domain and context, as follow:
mdp3_iommu_domain_init and mdp3_iommu_context_initwhich are placed in mdp3_mmu_init.
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;
}

Now at the stage of mdp3 probe, the memory is done.

7.3 probe of mdss dsi(msm_dsi_probe)

7.3.1 allocate dsi host private data in msm_dsi_init
		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);

The allocation is few, only two structures' size.

7.4 the allocation of dsi_panel_probe

For panel, only panel private data, tx, rx buffer and initial on/off commands need to be allocated.
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

fb_ioctl          fbmem.c
|--------do_fb_ioctl    fbmem.c
|--------fb_blank
	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,
};
|-------------------mdss_fb_blank in mdss_fb.c
|----------------------------mdss_fb_blank_sub in mdss_fb.c
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.
	case FB_BLANK_UNBLANK:
		if (!mfd->panel_power_on && mfd->mdp.on_fnc) {
			ret = mfd->mdp.on_fnc(mfd);
			if (ret == 0)
				mfd->panel_power_on = true;
			mutex_lock(&mfd->update.lock);
			mfd->update.type = NOTIFY_TYPE_UPDATE;
			mutex_unlock(&mfd->update.lock);
		}
		break;
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;

Please refer to 3.1 and 3.2
Last, dma function pointer start would be called, as follow:
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

Here, the double framebuffer address will be switched, so the performance can be improved.

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

Please refer to 80-BA103-1_A_Display_GCDB_XML_Entries.pdf

To use the parser script, go to @//~/device/qcom/common/display/tools/parser.pl. The command to run the script is: 
#perl parser.pl <.xml>  

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 -mdss.dtsi in @//~/kernel/arch/arm/boot/dts while the content of the header file should be copied to @//~/bootable/bootloader/lk/target//include/target/display.h for LK. In this example, 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.


Please refer to the following wiki picture, which is very clear:
http://en.wikipedia.org/wiki/File:Vector_Video_Standards4.svg
msm8610 lcd driver code analysis_第1张图片

This chart shows the most  common display resolutions , with the color of each resolution type indicating the display ratio (e.g., red indicates a 4:3 ratio)

12 dts and dtsi about lcd

12.1 On 8x10 platform, there is a version xx-xx-v2, this is corresponding to xx-v2-xx-dts, xx-v2-xx-dtsi, as follow:
~/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

~/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

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

Our product version is mtp. The following code snippet is related to lcd
msm8610-mdss.dtsi    According to the campatible name (msm-dsi-v2) of mdss_dsi, it will be related to dsi_host_v2.c, rather than mdss_dsi.c.

This dtsi should be read carefully, because most of host display parameters and platform resources are here.
dsi_host_v2.c
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 to mdss_dsi_panel.c, rather than dsi_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

MDP3_CLIENT_DMA_P, len:1658880
The actual size is 480(xres)*4(byte per pixel)*854(yres) = 1639690
The allocated size is 1658880, that is 0x1905000
if (iclient) {
		data->srcp_ihdl = ion_import_dma_buf(iclient, img->memory_id);
		if (IS_ERR_OR_NULL(data->srcp_ihdl)) {
			......
		}
		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)) {
			......
		}

mdp3_get_img(msmfb_data *, mdp3_img_data *, int) : int
|--------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
|
|--------mdp3_ppp_get_img(mdp_img *, mdp_blit_req *, mdp3_img_data *) : int

14 If reset isn't called first, sometimes slight flower screen would appear on lcd

We can refer to this file:  dts/dsi-panel-otm8018b-fwvga-video.dtsi
                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! */

Where do code parse this sequence? Please look at the following code snippet.
mdss_dsi_parse_reset_seq(device_node *, u32 *, u32 *, const char *) : int
|--------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); /*  I think here pr_debug should use pr_err. */
	} 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



你可能感兴趣的:(display,driver,on,soc)