Revert "Added video driver cleanup and tweaks by gauner1986. Thanks to huanyu for isolating them."
This reverts commit 935a9ce797.
			
			
This commit is contained in:
		| @@ -7,14 +7,29 @@ config FB_MSM | ||||
| 	default y | ||||
|  | ||||
| config FB_MSM_LCDC | ||||
| 	bool "Support for integrated LCD controller in qsd8x50" | ||||
| 	depends on FB_MSM && MSM_MDP31 | ||||
| 	bool "Support for integrated LCD controller in qsd8x50 and MSM7x27" | ||||
| 	depends on FB_MSM && (MSM_MDP31 || MSM_MDP302) | ||||
| 	default y | ||||
|  | ||||
| config FB_MSM_TVOUT | ||||
| 	bool "Support for TV-Out in qsd8x50" | ||||
| 	depends on FB_MSM && MSM_MDP31 | ||||
| 	default n | ||||
|  | ||||
| config FB_MSM_OVERLAY | ||||
| 	bool "Support for overlay in MSM7X30" | ||||
| 	depends on FB_MSM && MSM_MDP40 | ||||
| 	default y | ||||
|  | ||||
| config FB_MSM_DTV | ||||
| 	depends on FB_MSM_OVERLAY | ||||
| 	bool | ||||
| 	default n | ||||
|  | ||||
| config GPU_MSM_KGSL | ||||
| 	tristate "MSM 3D Graphics driver for QSD8x50 and MSM7x27" | ||||
| 	default n | ||||
| 	depends on FB_MSM && ARCH_QSD8X50 | ||||
| 	depends on FB_MSM && (ARCH_QSD8X50 || ARCH_MSM7227 || ARCH_MSM7X30) | ||||
| 	select GENERIC_ALLOCATOR | ||||
| 	select CONFIG_FW_LOADER | ||||
| 	help | ||||
| @@ -22,6 +37,24 @@ config GPU_MSM_KGSL | ||||
| 	  use hardware accelerated OpenGL ES 2.0 and 1.1 on these | ||||
| 	  chips. | ||||
|  | ||||
| config MSM_ROTATOR | ||||
|         tristate "MSM Offline Image Rotator Driver" | ||||
|         depends on ARCH_MSM7X30 && ANDROID_PMEM | ||||
|         default y | ||||
|         help | ||||
|           This driver provides support for the image rotator HW block in the | ||||
|           MSM 7x30 SoC. | ||||
|  | ||||
| config MSM_ROTATOR_USE_IMEM | ||||
|         bool "Enable rotator driver to use iMem" | ||||
|         depends on MSM_ROTATOR | ||||
|         default y | ||||
|         help | ||||
|           This option enables the msm_rotator driver to use the move efficient | ||||
|           iMem.  Some MSM platforms may not have iMem available for the rotator | ||||
|           block.  Or some systems may want the iMem to be dedicated to a | ||||
|           different function. | ||||
|  | ||||
| config MSM_KGSL_MMU | ||||
| 	bool "Turn on MMU for graphics driver " | ||||
| 	depends on GPU_MSM_KGSL && MMU | ||||
| @@ -30,6 +63,19 @@ config MSM_KGSL_MMU | ||||
| 	  If enabled, the GPU driver will allocate memory from vmalloc | ||||
| 	  and enable the use of GPU MMU, instead of using pmem. | ||||
|  | ||||
| config MSM_KGSL_PER_FD_PAGETABLE | ||||
|         bool "Turn on per-fd pagetable for MMU of graphics driver " | ||||
|         depends on MSM_KGSL_MMU && MMU | ||||
|         default n | ||||
|         help | ||||
|          If enabled, the MMU unit of GPU driver will use seperate | ||||
|          pagetables for each file descriptor | ||||
|  | ||||
| config MSM_HDMI | ||||
| 	bool "Support for HDMI in QCT platform" | ||||
| 	depends on MSM_MDP31 | ||||
| 	default n | ||||
|  | ||||
| config FB_MSM_LOGO | ||||
|        bool "Use boot splashscreen" | ||||
|        depends on FB_MSM | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
|  | ||||
| # core framebuffer | ||||
| # | ||||
| obj-y := msm_fb.o | ||||
| obj-y := msm_fb.o logo.o | ||||
| ifeq ($(CONFIG_FB_MSM_LOGO),y) | ||||
| obj-y += logo.o | ||||
| endif | ||||
| @@ -9,9 +9,14 @@ endif | ||||
| # MDP DMA/PPP engine | ||||
| # | ||||
| obj-y += mdp.o mdp_ppp.o | ||||
|  | ||||
| obj-$(CONFIG_FB_MSM_OVERLAY) += mdp4_util.o | ||||
| obj-$(CONFIG_FB_MSM_OVERLAY) += mdp4_overlay.o | ||||
| obj-$(CONFIG_FB_MSM_OVERLAY) += mdp4_overlay_mddi.o | ||||
| obj-$(CONFIG_MSM_MDP40) += msm_rotator.o | ||||
|  | ||||
| obj-$(CONFIG_MSM_MDP22) += mdp_ppp22.o | ||||
| obj-$(CONFIG_MSM_MDP30) += mdp_ppp22.o | ||||
| obj-$(CONFIG_MSM_MDP302)+= mdp_ppp22.o | ||||
| obj-$(CONFIG_MSM_MDP31) += mdp_ppp31.o | ||||
|  | ||||
| # MDDI interface | ||||
| @@ -21,11 +26,18 @@ obj-y += mddi.o | ||||
| # MDDI client/panel drivers | ||||
| # | ||||
| obj-y += mddi_client_dummy.o | ||||
| obj-y += mddi_client_toshiba.o | ||||
| obj-y += mddi_client_nt35399.o | ||||
| obj-y += mddi_client_epson.o | ||||
| obj-y += mddi_client_novb9f6_5582.o | ||||
|  | ||||
| # MDP LCD controller driver | ||||
| obj-$(CONFIG_FB_MSM_LCDC) += mdp_lcdc.o | ||||
| obj-$(CONFIG_FB_MSM_TVOUT) += tvenc.o tvfb.o | ||||
|  | ||||
| # Yamato GL driver | ||||
| ifeq ($(CONFIG_ARCH_MSM7X30),y) | ||||
| obj-$(CONFIG_GPU_MSM_KGSL) += gpu/kgsl_adreno205/ | ||||
| else | ||||
| obj-$(CONFIG_GPU_MSM_KGSL) += gpu/kgsl/ | ||||
| endif | ||||
|  | ||||
| obj-$(CONFIG_MSM_HDMI) += hdmi/ | ||||
|   | ||||
| @@ -119,10 +119,16 @@ static void kgsl_clk_enable(void) | ||||
| 	clk_set_rate(kgsl_driver.ebi1_clk, 128000000); | ||||
| 	clk_enable(kgsl_driver.imem_clk); | ||||
| 	clk_enable(kgsl_driver.grp_clk); | ||||
| #ifdef CONFIG_ARCH_MSM7227 | ||||
| 	clk_enable(kgsl_driver.grp_pclk); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| static void kgsl_clk_disable(void) | ||||
| { | ||||
| #ifdef CONFIG_ARCH_MSM7227 | ||||
| 	clk_disable(kgsl_driver.grp_pclk); | ||||
| #endif | ||||
| 	clk_disable(kgsl_driver.grp_clk); | ||||
| 	clk_disable(kgsl_driver.imem_clk); | ||||
| 	clk_set_rate(kgsl_driver.ebi1_clk, 0); | ||||
| @@ -163,7 +169,7 @@ static void kgsl_hw_put_locked(bool start_timer) | ||||
| { | ||||
| 	if ((--kgsl_driver.active_cnt == 0) && start_timer) { | ||||
| 		mod_timer(&kgsl_driver.standby_timer, | ||||
| 			  jiffies + msecs_to_jiffies(20)); | ||||
| 			  jiffies + msecs_to_jiffies(512)); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -1031,10 +1037,52 @@ static long kgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| static int kgsl_mmap(struct file *file, struct vm_area_struct *vma) | ||||
| { | ||||
| 	int result; | ||||
| 	struct kgsl_memdesc *memdesc = NULL; | ||||
| 	unsigned long vma_size = vma->vm_end - vma->vm_start; | ||||
| 	unsigned long vma_offset = vma->vm_pgoff << PAGE_SHIFT; | ||||
| 	struct kgsl_device *device = NULL; | ||||
|  | ||||
| 	mutex_lock(&kgsl_driver.mutex); | ||||
|  | ||||
| 	device = &kgsl_driver.yamato_device; | ||||
|  | ||||
| 	/*allow yamato memstore to be mapped read only */ | ||||
| 	if (vma_offset == device->memstore.physaddr) { | ||||
| 		if (vma->vm_flags & VM_WRITE) { | ||||
| 			result = -EPERM; | ||||
| 			goto done; | ||||
| 		} | ||||
| 		memdesc = &device->memstore; | ||||
| 	} | ||||
|  | ||||
| 	if (memdesc->size != vma_size) { | ||||
| 		KGSL_MEM_ERR("file %p bad size %ld, should be %d\n", | ||||
| 			file, vma_size, memdesc->size); | ||||
| 		result = -EINVAL; | ||||
| 		goto done; | ||||
| 	} | ||||
| 	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); | ||||
|  | ||||
| 	result = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, | ||||
| 				vma_size, vma->vm_page_prot); | ||||
| 	if (result != 0) { | ||||
| 		KGSL_MEM_ERR("remap_pfn_range returned %d\n", | ||||
| 				result); | ||||
| 		goto done; | ||||
| 	} | ||||
| done: | ||||
| 	mutex_unlock(&kgsl_driver.mutex); | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| static struct file_operations kgsl_fops = { | ||||
| 	.owner = THIS_MODULE, | ||||
| 	.release = kgsl_release, | ||||
| 	.open = kgsl_open, | ||||
| 	.mmap = kgsl_mmap, | ||||
| 	.unlocked_ioctl = kgsl_ioctl, | ||||
| }; | ||||
|  | ||||
| @@ -1096,6 +1144,9 @@ static int __devinit kgsl_platform_probe(struct platform_device *pdev) | ||||
| 	BUG_ON(kgsl_driver.grp_clk != NULL); | ||||
| 	BUG_ON(kgsl_driver.imem_clk != NULL); | ||||
| 	BUG_ON(kgsl_driver.ebi1_clk != NULL); | ||||
| #ifdef CONFIG_ARCH_MSM7227 | ||||
| 	BUG_ON(kgsl_driver.grp_pclk != NULL); | ||||
| #endif | ||||
|  | ||||
| 	kgsl_driver.pdev = pdev; | ||||
|  | ||||
| @@ -1126,6 +1177,15 @@ static int __devinit kgsl_platform_probe(struct platform_device *pdev) | ||||
| 	} | ||||
| 	kgsl_driver.ebi1_clk = clk; | ||||
|  | ||||
| #ifdef CONFIG_ARCH_MSM7227 | ||||
| 	clk = clk_get(&pdev->dev, "grp_pclk"); | ||||
| 	if (IS_ERR(clk)) { | ||||
| 		result = PTR_ERR(clk); | ||||
| 		KGSL_DRV_ERR("clk_get(grp_pclk) returned %d\n", result); | ||||
| 		goto done; | ||||
| 	} | ||||
| 	kgsl_driver.grp_pclk = clk; | ||||
| #endif | ||||
| 	/*acquire interrupt */ | ||||
| 	kgsl_driver.interrupt_num = platform_get_irq(pdev, 0); | ||||
| 	if (kgsl_driver.interrupt_num <= 0) { | ||||
|   | ||||
| @@ -47,7 +47,9 @@ struct kgsl_driver { | ||||
| 	struct clk *grp_clk; | ||||
| 	struct clk *imem_clk; | ||||
| 	struct clk *ebi1_clk; | ||||
|  | ||||
| #ifdef CONFIG_ARCH_MSM7227 | ||||
| 	struct clk *grp_pclk; | ||||
| #endif | ||||
| 	struct kgsl_devconfig yamato_config; | ||||
|  | ||||
| 	uint32_t flags_debug; | ||||
|   | ||||
| @@ -464,6 +464,9 @@ int kgsl_mmu_map(struct kgsl_pagetable *pagetable, | ||||
| 	KGSL_MEM_VDBG("enter (pt=%p, physaddr=%08x, range=%08d, gpuaddr=%p)\n", | ||||
| 		      pagetable, address, range, gpuaddr); | ||||
|  | ||||
| #ifdef CONFIG_CACHE_L2X0 | ||||
|         l2x0_cache_flush_all(); | ||||
| #endif | ||||
| 	mmu = pagetable->mmu; | ||||
|  | ||||
| 	BUG_ON(mmu == NULL); | ||||
|   | ||||
| @@ -63,7 +63,7 @@ inline unsigned int kgsl_ringbuffer_sizelog2quadwords(unsigned int sizedwords) | ||||
| 	return sizelog2quadwords; | ||||
| } | ||||
|  | ||||
|  | ||||
| #if defined(CONFIG_MACH_HTCLEO) | ||||
| // +Cotulla hack | ||||
| #ifndef ARRAYSIZE | ||||
| #define ARRAYSIZE(a)        (sizeof(a)/sizeof(a[0])) | ||||
| @@ -727,6 +727,7 @@ unsigned char fw_yamato_pm4[9220] = | ||||
| 	0x00, 0x00, 0x00, 0x00 | ||||
| }; | ||||
| // -Cotulla hack | ||||
| #endif | ||||
|  | ||||
| /* functions */ | ||||
| void kgsl_cp_intrcallback(struct kgsl_device *device) | ||||
| @@ -937,13 +938,12 @@ static int kgsl_ringbuffer_load_pm4_ucode(struct kgsl_device *device) | ||||
| { | ||||
| 	int status = 0; | ||||
| 	int i; | ||||
| //	const struct firmware *fw = NULL; | ||||
| 	unsigned int *fw_ptr = NULL; | ||||
| 	size_t fw_word_size = 0; | ||||
| 	struct firmware foo_fw; | ||||
| 	struct firmware *fw = &foo_fw; | ||||
| 	 | ||||
| #if !defined(CONFIG_MACH_HTCLEO) | ||||
| 	const struct firmware *fw = NULL; | ||||
|  | ||||
| /* | ||||
| 	status = request_firmware(&fw, YAMATO_PM4_FW, | ||||
| 					kgsl_driver.misc.this_device); | ||||
| 	if (status != 0) { | ||||
| @@ -951,10 +951,13 @@ static int kgsl_ringbuffer_load_pm4_ucode(struct kgsl_device *device) | ||||
| 				YAMATO_PM4_FW, status); | ||||
| 		goto done; | ||||
| 	} | ||||
| */ | ||||
| #else | ||||
| 	struct firmware foo_fw; | ||||
| 	struct firmware *fw = &foo_fw; | ||||
|  | ||||
| 	fw->data = fw_yamato_pm4; | ||||
| 	fw->size = ARRAYSIZE(fw_yamato_pm4);  | ||||
|  | ||||
| #endif | ||||
| 	/*this firmware must come in 3 word chunks. plus 1 word of version*/ | ||||
| 	if ((fw->size % (sizeof(uint32_t)*3)) != 4) { | ||||
| 		KGSL_DRV_ERR("bad firmware size %d.\n", fw->size); | ||||
| @@ -971,32 +974,38 @@ static int kgsl_ringbuffer_load_pm4_ucode(struct kgsl_device *device) | ||||
| 		kgsl_yamato_regwrite(device, REG_CP_ME_RAM_DATA, fw_ptr[i]); | ||||
|  | ||||
| done: | ||||
| //	release_firmware(fw); | ||||
| //	return status; | ||||
| #if !defined(CONFIG_MACH_HTCLEO) | ||||
| 	release_firmware(fw); | ||||
| 	return status; | ||||
| #else | ||||
| 	return 0; | ||||
| #endif | ||||
|  | ||||
| } | ||||
|  | ||||
| static int kgsl_ringbuffer_load_pfp_ucode(struct kgsl_device *device) | ||||
| { | ||||
| 	int status = 0; | ||||
| 	int i; | ||||
| 	//const struct firmware *fw = NULL; | ||||
| 	unsigned int *fw_ptr = NULL; | ||||
| 	size_t fw_word_size = 0; | ||||
| 	struct firmware foo_fw; | ||||
| 	struct firmware *fw = &foo_fw; | ||||
| #if !defined(CONFIG_MACH_HTCLEO) | ||||
| 	const struct firmware *fw = NULL; | ||||
|  | ||||
| /*	status = request_firmware(&fw, YAMATO_PFP_FW, | ||||
| 	status = request_firmware(&fw, YAMATO_PFP_FW, | ||||
| 				kgsl_driver.misc.this_device); | ||||
| 	if (status != 0) { | ||||
| 		KGSL_DRV_ERR("request_firmware for %s failed with error %d\n", | ||||
| 				YAMATO_PFP_FW, status); | ||||
| 		return status; | ||||
| 	} | ||||
| */ | ||||
| #else | ||||
| 	struct firmware foo_fw; | ||||
| 	struct firmware *fw = &foo_fw; | ||||
|  | ||||
| 	fw->data = fw_yamato_pfp; | ||||
| 	fw->size = ARRAYSIZE(fw_yamato_pfp);  | ||||
|  | ||||
| #endif | ||||
|  | ||||
| 	/*this firmware must come in 1 word chunks. */ | ||||
| 	if ((fw->size % sizeof(uint32_t)) != 0) { | ||||
| @@ -1013,9 +1022,12 @@ static int kgsl_ringbuffer_load_pfp_ucode(struct kgsl_device *device) | ||||
| 	for (i = 1; i < fw_word_size; i++) | ||||
| 		kgsl_yamato_regwrite(device, REG_CP_PFP_UCODE_DATA, fw_ptr[i]); | ||||
|  | ||||
| //	release_firmware(fw); | ||||
| //	return status; | ||||
| #if !defined(CONFIG_MACH_HTCLEO) | ||||
| 	release_firmware(fw); | ||||
| 	return status; | ||||
| #else | ||||
| 	return 0; | ||||
| #endif | ||||
| } | ||||
|  | ||||
| static int kgsl_ringbuffer_start(struct kgsl_ringbuffer *rb) | ||||
|   | ||||
							
								
								
									
										9
									
								
								drivers/video/msm/hdmi/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								drivers/video/msm/hdmi/Makefile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| msm_hdmi-objs =		\ | ||||
| 	transmitter.o	\ | ||||
| 	hdmi_lcdc.o	\ | ||||
| 	fb-hdmi.o	\ | ||||
| 	edid.o | ||||
|  | ||||
| obj-$(CONFIG_MSM_HDMI) += msm_hdmi.o | ||||
|  | ||||
| obj-$(CONFIG_MSM_HDMI) += silicon-image/ | ||||
							
								
								
									
										695
									
								
								drivers/video/msm/hdmi/edid.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										695
									
								
								drivers/video/msm/hdmi/edid.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,695 @@ | ||||
| /* | ||||
|  * Copyright (C) 2009 HTC | ||||
|  * | ||||
|  * This software is licensed under the terms of the GNU General Public | ||||
|  * License version 2, as published by the Free Software Foundation, and | ||||
|  * may be copied, distributed, and modified under those terms. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  | ||||
|  * Common function for accessing/debugging EDID data. | ||||
|  * Reference: | ||||
| 	http://en.wikipedia.org/wiki/Extended_display_identification_data | ||||
| */ | ||||
|  | ||||
| #include <linux/device.h> | ||||
| #include <linux/htc_hdmi.h> | ||||
| #include <linux/debugfs.h> | ||||
|  | ||||
| #include "include/fb-hdmi.h" | ||||
| #include "include/sil902x.h" | ||||
|                             | ||||
| #if 1 | ||||
| #define EDID_DBG(s...) printk("[hdmi/edid]" s) | ||||
| #else | ||||
| #define EDID_DBG(s...) do {} while (0) | ||||
| #endif | ||||
|  | ||||
| static struct video_mode established_timing_db[] = { | ||||
| 	{800, 600, 60, ASPECT(4, 3), PROGRESSIVE, false, "800x600 @ 60 Hz"}, | ||||
| 	{800, 600, 56, ASPECT(4, 3), PROGRESSIVE, false, "800x600 @ 56 Hz"}, | ||||
| 	{640, 480, 75, ASPECT(4, 3), PROGRESSIVE, false, "800x600 @ 75 Hz"}, | ||||
| 	{640, 480, 72, ASPECT(4, 3), PROGRESSIVE, false, "640x480 @ 72 Hz"}, | ||||
| 	{640, 480, 67, ASPECT(4, 3), PROGRESSIVE, false, "640x480 @ 67 Hz"}, | ||||
| 	{640, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, "640x480 @ 60 Hz"}, | ||||
| 	{720, 400, 88, ASPECT(4, 3), PROGRESSIVE, false, "720x400 @ 88 Hz"}, | ||||
| 	{720, 400, 70, ASPECT(4, 3), PROGRESSIVE, false, "720x400 @ 70 Hz"}, | ||||
|  | ||||
| 	{1280, 1024, 75, ASPECT(4, 3), PROGRESSIVE, false, "1280x1024@75 Hz"}, | ||||
| 	{1024, 768, 75, ASPECT(4, 3), PROGRESSIVE, false, "1024x768@75 Hz"}, | ||||
| 	{1024, 768, 70, ASPECT(4, 3), PROGRESSIVE, false, "1024x768@70 Hz"}, | ||||
| 	{1024, 768, 60, ASPECT(4, 3), PROGRESSIVE, false, "1024x768@60 Hz"}, | ||||
| 	{1024, 768, 87, ASPECT(4, 3), INTERLACE, false, | ||||
| 		"1024x768@87 Hz (Interlaced)"}, | ||||
| 	{832, 624, 75, ASPECT(4, 3), PROGRESSIVE, false, "832x624@75 Hz"}, | ||||
| 	{800, 600, 75, ASPECT(4, 3), PROGRESSIVE, false, "800x600@75 Hz"}, | ||||
| 	{800, 600, 72, ASPECT(4, 3), PROGRESSIVE, false, "800x600@72 Hz"}, | ||||
|  | ||||
| 	{1152, 870, 75, ASPECT(4, 3), PROGRESSIVE, false, "1152x870 @ 75 Hz"}, | ||||
| }; | ||||
|  | ||||
| static struct video_mode standard_timing_db[8]; | ||||
|  | ||||
| static struct video_mode additional_timing_db[] = { | ||||
| 	{640, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, | ||||
| 		" 1 DMT0659   4:3                640x480p @ 59.94/60Hz"}, | ||||
| 	{720, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, | ||||
| 		" 2 480p      4:3                720x480p @ 59.94/60Hz"}, | ||||
| 	{720, 480, 60, ASPECT(16, 9), PROGRESSIVE, false, | ||||
| 		" 3 480pH    16:9                720x480p @ 59.94/60Hz"}, | ||||
| 	{1280, 720, 60, ASPECT(16, 9), PROGRESSIVE, false, | ||||
| 		" 4 720p     16:9               1280x720p @ 59.94/60Hz"}, | ||||
| 	{1920, 1080, 60, ASPECT(4, 3), INTERLACE, false, | ||||
| 		" 5 1080i    16:9              1920x1080i @ 59.94/60Hz"}, | ||||
| 	{720, 480, 60, ASPECT(4, 3), INTERLACE, false, | ||||
| 		" 6 480i      4:3          720(1440)x480i @ 59.94/60Hz"}, | ||||
| 	{720, 480, 60, ASPECT(16, 9), INTERLACE, false, | ||||
| 		" 7 480iH    16:9          720(1440)x480i @ 59.94/60Hz"}, | ||||
| 	{720, 240, 60, ASPECT(4, 3), PROGRESSIVE, false, | ||||
| 		" 8 240p      4:3          720(1440)x240p @ 59.94/60Hz"}, | ||||
| 	{720, 480, 60, ASPECT(16, 9), PROGRESSIVE, false, | ||||
| 		" 9 240pH    16:9          720(1440)x240p @ 59.94/60Hz"}, | ||||
| 	{2880, 480, 60, ASPECT(4, 3), INTERLACE, false, | ||||
| 		"10 480i4x    4:3             (2880)x480i @ 59.94/60Hz"}, | ||||
| 	{2880, 480, 60, ASPECT(16, 9), INTERLACE, false, | ||||
| 		"11 480i4xH  16:9             (2880)x480i @ 59.94/60Hz"}, | ||||
| 	{2880, 240, 60, ASPECT(4, 3), PROGRESSIVE, false, | ||||
| 		"12 240p4x    4:3             (2880)x240p @ 59.94/60Hz"}, | ||||
| 	{2880, 240, 60, ASPECT(16, 9), PROGRESSIVE, false, | ||||
| 		"13 240p4xH  16:9             (2880)x240p @ 59.94/60Hz"}, | ||||
| 	{1440, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, | ||||
| 		"14 480p2x    4:3               1440x480p @ 59.94/60Hz"}, | ||||
| 	{1440, 480, 60, ASPECT(16, 9), PROGRESSIVE, false, | ||||
| 		"15 480p2xH  16:9               1440x480p @ 59.94/60Hz"}, | ||||
| 	{1920, 1080, 60, ASPECT(16, 9), PROGRESSIVE, false, | ||||
| 		"16 1080p    16:9              1920x1080p @ 59.94/60Hz"}, | ||||
| 	{720, 576, 50, ASPECT(4, 3), PROGRESSIVE, false, | ||||
| 		"17 576p      4:3                720x576p @ 50Hz"}, | ||||
| 	{720, 576, 50, ASPECT(16, 9), PROGRESSIVE, false, | ||||
| 		"18 576pH    16:9                720x576p @ 50Hz"}, | ||||
| 	{1280, 720, 50, ASPECT(16, 9), PROGRESSIVE, false, | ||||
| 		"19 720p50   16:9               1280x720p @ 50Hz"}, | ||||
| 	{1920, 1080, 50, ASPECT(16, 9), INTERLACE, false, | ||||
| 		"20 1080i25  16:9              1920x1080i @ 50Hz*"}, | ||||
| 	{1440, 576, 50, ASPECT(4, 3), INTERLACE, false, | ||||
| 		"21 576i      4:3          720(1440)x576i @ 50Hz"}, | ||||
| 	{1440, 576, 50, ASPECT(4, 3), PROGRESSIVE, false, | ||||
| 		"22 576iH    16:9          720(1440)x576i @ 50Hz"}, | ||||
| 	{720, 288, 50, ASPECT(4, 3), PROGRESSIVE, false, | ||||
| 		"23 288p      4:3          720(1440)x288p @ 50Hz"}, | ||||
| 	{720, 288, 50, ASPECT(16, 9), PROGRESSIVE, false, | ||||
| 		"24 288pH    16:9          720(1440)x288p @ 50Hz"}, | ||||
| 	{2880, 576, 50, ASPECT(4, 3), INTERLACE, false, | ||||
| 		"25 576i4x    4:3             (2880)x576i @ 50Hz"}, | ||||
| 	{2880, 576, 50, ASPECT(16, 9), INTERLACE, false, | ||||
| 		"26 576i4xH  16:9             (2880)x576i @ 50Hz"}, | ||||
| 	{2880, 288, 50, ASPECT(4, 3), PROGRESSIVE, false, | ||||
| 		"27 288p4x    4:3             (2880)x288p @ 50Hz"}, | ||||
| 	{2880, 288, 50, ASPECT(16, 9), PROGRESSIVE, false, | ||||
| 		"28 288p4xH  16:9             (2880)x288p @ 50Hz"}, | ||||
| 	{1440, 576, 50, ASPECT(4, 3), PROGRESSIVE, false, | ||||
| 		"29 576p2x    4:3               1440x576p @ 50Hz"}, | ||||
| 	{1440, 576, 50, ASPECT(16, 9), PROGRESSIVE, false, | ||||
| 		"30 576p2xH  16:9               1440x576p @ 50Hz"}, | ||||
| 	{1920, 1080, 50, ASPECT(16, 9), PROGRESSIVE, false, | ||||
| 		"31 1080p50  16:9              1920x1080p @ 50Hz"}, | ||||
| 	{1920, 1080, 24, ASPECT(16, 9), PROGRESSIVE, false, | ||||
| 		"32 1080p24  16:9              1920x1080p @ 23.98/24Hz"}, | ||||
| 	{1920, 1080, 25, ASPECT(16, 9), PROGRESSIVE, false, | ||||
| 		"33 1080p25  16:9              1920x1080p @ 25Hz"}, | ||||
| 	{1920, 1080, 30, ASPECT(16, 9), PROGRESSIVE, false, | ||||
| 		"34 1080p30  16:9              1920x1080p @ 29.97/30Hz"}, | ||||
| 	{2880, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, | ||||
| 		"35 480p4x    4:3             (2880)x480p @ 59.94/60Hz"}, | ||||
| 	{2880, 480, 60, ASPECT(16, 9), PROGRESSIVE, false, | ||||
| 		"36 480p4xH  16:9             (2880)x480p @ 59.94/60Hz"}, | ||||
| 	{2880, 576, 50, ASPECT(4, 3), PROGRESSIVE, false, | ||||
| 		"37 576p4x    4:3             (2880)x576p @ 50Hz"}, | ||||
| 	{2880, 576, 50, ASPECT(16, 9), PROGRESSIVE, false, | ||||
| 		"38 576p4xH  16:9             (2880)x576p @ 50Hz"}, | ||||
| 	{1920, 1080, 50, ASPECT(16, 9), INTERLACE, false, | ||||
| 		"39 108Oi25  16:9  1920x1080i(1250 Total) @ 50Hz*"}, | ||||
| 	{1920, 1080, 100, ASPECT(16, 9), INTERLACE, false, | ||||
| 		"40 1080i50  16:9              1920x1080i @ 100Hz"}, | ||||
| 	{1280, 720, 100, ASPECT(16, 9), PROGRESSIVE, false, | ||||
| 		"41 720p100  16:9               1280x720p @ 100Hz"}, | ||||
| 	{720, 576, 100, ASPECT(4, 3), PROGRESSIVE, false, | ||||
| 		"42 576p100   4:3                720x576p @ 100Hz"}, | ||||
| 	{720, 576, 100, ASPECT(16, 9), PROGRESSIVE, false, | ||||
| 		"43 576p100H 16:9                720x576p @ 100Hz"}, | ||||
| 	{720, 576, 100, ASPECT(4, 3), INTERLACE, false, | ||||
| 		"44 576i50    4:3          720(1440)x576i @ 100Hz"}, | ||||
| 	{720, 576, 100, ASPECT(16, 9), INTERLACE, false, | ||||
| 		"45 576i50H  16:9          720(1440)x576i @ 100Hz"}, | ||||
| 	{1920, 1080, 120, ASPECT(16, 9), INTERLACE, false, | ||||
| 		"46 1080i60  16:9              1920x1080i @ 119.88/120Hz"}, | ||||
| 	{1280, 720, 120, ASPECT(16, 9), PROGRESSIVE, false, | ||||
| 		"47 720p120  16:9               1280x720p @ 119.88/120Hz"}, | ||||
| 	{720, 480, 120, ASPECT(4, 3), PROGRESSIVE, false, | ||||
| 		"48 480p119   4:3                720x480p @ 119.88/120Hz"}, | ||||
| 	{720, 480, 120, ASPECT(16, 9), PROGRESSIVE, false, | ||||
| 		"49 480p119H 16:9                720x480p @ 119.88/120Hz"}, | ||||
| 	{720, 480, 120, ASPECT(4, 3), INTERLACE, false, | ||||
| 		"50 480i59    4:3          720(1440)x480i @ 119.88/120Hz"}, | ||||
| 	{720, 480, 120, ASPECT(16, 9), INTERLACE, false, | ||||
| 		"51 480i59H  16:9          720(1440)x480i @ 119.88/120Hz"}, | ||||
| 	{720, 576, 200, ASPECT(4, 3), PROGRESSIVE, false, | ||||
| 		"52 576p200   4:3                720x576p @ 200Hz"}, | ||||
| 	{720, 576, 200, ASPECT(16, 9), PROGRESSIVE, false, | ||||
| 		"53 576p200H 16:9                720x576p @ 200Hz"}, | ||||
| 	{720, 576, 200, ASPECT(4, 3), INTERLACE, false, | ||||
| 		"54 576i100   4:3          720(1440)x576i @ 200Hz"}, | ||||
| 	{720, 576, 200, ASPECT(16, 9), INTERLACE, false, | ||||
| 		"55 576i100H 16:9          720(1440)x576i @ 200Hz"}, | ||||
| 	{720, 480, 240, ASPECT(4, 3), PROGRESSIVE, false, | ||||
| 		"56 480p239   4:3                720x480p @ 239.76/240Hz"}, | ||||
| 	{720, 480, 240, ASPECT(16, 9), PROGRESSIVE, false, | ||||
| 		"57 480p239H 16:9                720x480p @ 239.76/240Hz"}, | ||||
| 	{720, 480, 240, ASPECT(4, 3), INTERLACE, false, | ||||
| 		"58 480i119   4:3          720(1440)x480i @ 239.76/240Hz"}, | ||||
| 	{720, 480, 240, ASPECT(16, 9), INTERLACE, false, | ||||
| 		"59 480i119H 16:9          720(1440)x480i @ 239.76/240Hz"}, | ||||
| 	{1280, 720, 24, ASPECT(16, 9), PROGRESSIVE, false, | ||||
| 		"60 720p24   16:9               1280x720p @ 23.98/24Hz"}, | ||||
| 	{1280, 720, 25, ASPECT(16, 9), PROGRESSIVE, false, | ||||
| 		"61 720p25   16:9               1280x720p @ 25Hz"}, | ||||
| 	{1280, 720, 30, ASPECT(16, 9), PROGRESSIVE, false, | ||||
| 		"62 720p30   16:9               1280x720p @ 29.97/30Hz"}, | ||||
| 	{1920, 1080, 120, ASPECT(16, 9), PROGRESSIVE, false, | ||||
| 		"63 1080p120 16:9               1920x1080 @ 119.88/120Hz"}, | ||||
| }; | ||||
|  | ||||
| /* device supported modes in CEA */ | ||||
| enum { | ||||
| 	CEA_MODE_640X480P_60HZ_4_3 = 0, | ||||
| 	CEA_MODE_720X480P_60HZ_4_3 = 1, | ||||
| 	CEA_MODE_720X480P_60HZ_16_9 = 2, | ||||
| 	CEA_MODE_1280X720P_60HZ_16_9 = 3, | ||||
| 	CEA_MODE_720X576P_50HZ_4_3 = 16, | ||||
| 	CEA_MODE_720X576P_50HZ_16_9 = 17, | ||||
| }; | ||||
|  | ||||
| /* device supported modes in established timing */ | ||||
| enum { | ||||
| 	ESTABLISHED_MODE_800X600_60HZ = 0, | ||||
| 	ESTABLISHED_MODE_640X480_60HZ = 5, | ||||
| }; | ||||
|  | ||||
| int init_edid_info(struct edid_info_struct *edid_info) | ||||
| { | ||||
| 	edid_info->is_valid = false; | ||||
| 	mutex_init(&edid_info->access_lock);	 | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* Byte 35-37 of block-0 */ | ||||
| static char *established_timing_str[] = { | ||||
|        "800x600 @ 60 Hz", | ||||
|        "800x600 @ 56 Hz", | ||||
|        "640x480 @ 75 Hz", | ||||
|        "640x480 @ 72 Hz", | ||||
|        "640x480 @ 67 Hz", | ||||
|        "640x480 @ 60 Hz", | ||||
|        "720x400 @ 88 Hz", | ||||
|        "720x400 @ 70 Hz", | ||||
|  | ||||
|        "1280x1024@75 Hz", | ||||
|        "1024x768@75 Hz", | ||||
|        "1024x768@70 Hz", | ||||
|        "1024x768@60 Hz", | ||||
|        "1024x768@87 Hz (Interlaced)", | ||||
|        "832x624@75 Hz", | ||||
|        "800x600@75 Hz", | ||||
|        "800x600@72 Hz", | ||||
|  | ||||
|        "", | ||||
|        "", | ||||
|        "", | ||||
|        "", | ||||
|        "", | ||||
|        "", | ||||
|        "", | ||||
|        "1152x870 @ 75 Hz", | ||||
| }; | ||||
|  | ||||
| /* E-EDID Video data block:  */ | ||||
| static char *vdb_modes_str[] = { | ||||
|        " 1 DMT0659   4:3                640x480p @ 59.94/60Hz", | ||||
|        " 2 480p      4:3                720x480p @ 59.94/60Hz", | ||||
|        " 3 480pH    16:9                720x480p @ 59.94/60Hz", | ||||
|        " 4 720p     16:9               1280x720p @ 59.94/60Hz", | ||||
|        " 5 1080i    16:9              1920x1080i @ 59.94/60Hz", | ||||
|        " 6 480i      4:3          720(1440)x480i @ 59.94/60Hz", | ||||
|        " 7 480iH    16:9          720(1440)x480i @ 59.94/60Hz", | ||||
|        " 8 240p      4:3          720(1440)x240p @ 59.94/60Hz", | ||||
|        " 9 240pH    16:9          720(1440)x240p @ 59.94/60Hz", | ||||
|        "10 480i4x    4:3             (2880)x480i @ 59.94/60Hz", | ||||
|        "11 480i4xH  16:9             (2880)x480i @ 59.94/60Hz", | ||||
|        "12 240p4x    4:3             (2880)x240p @ 59.94/60Hz", | ||||
|        "13 240p4xH  16:9             (2880)x240p @ 59.94/60Hz", | ||||
|        "14 480p2x    4:3               1440x480p @ 59.94/60Hz", | ||||
|        "15 480p2xH  16:9               1440x480p @ 59.94/60Hz", | ||||
|        "16 1080p    16:9              1920x1080p @ 59.94/60Hz", | ||||
|        "17 576p      4:3                720x576p @ 50Hz", | ||||
|        "18 576pH    16:9                720x576p @ 50Hz", | ||||
|        "19 720p50   16:9               1280x720p @ 50Hz", | ||||
|        "20 1080i25  16:9              1920x1080i @ 50Hz*", | ||||
|        "21 576i      4:3          720(1440)x576i @ 50Hz", | ||||
|        "22 576iH    16:9          720(1440)x576i @ 50Hz", | ||||
|        "23 288p      4:3          720(1440)x288p @ 50Hz", | ||||
|        "24 288pH    16:9          720(1440)x288p @ 50Hz", | ||||
|        "25 576i4x    4:3             (2880)x576i @ 50Hz", | ||||
|        "26 576i4xH  16:9             (2880)x576i @ 50Hz", | ||||
|        "27 288p4x    4:3             (2880)x288p @ 50Hz", | ||||
|        "28 288p4xH  16:9             (2880)x288p @ 50Hz", | ||||
|        "29 576p2x    4:3               1440x576p @ 50Hz", | ||||
|        "30 576p2xH  16:9               1440x576p @ 50Hz", | ||||
|        "31 1080p50  16:9              1920x1080p @ 50Hz", | ||||
|        "32 1080p24  16:9              1920x1080p @ 23.98/24Hz", | ||||
|        "33 1080p25  16:9              1920x1080p @ 25Hz", | ||||
|        "34 1080p30  16:9              1920x1080p @ 29.97/30Hz", | ||||
|        "35 480p4x    4:3             (2880)x480p @ 59.94/60Hz", | ||||
|        "36 480p4xH  16:9             (2880)x480p @ 59.94/60Hz", | ||||
|        "37 576p4x    4:3             (2880)x576p @ 50Hz", | ||||
|        "38 576p4xH  16:9             (2880)x576p @ 50Hz", | ||||
|        "39 108Oi25  16:9  1920x1080i(1250 Total) @ 50Hz*", | ||||
|        "40 1080i50  16:9              1920x1080i @ 100Hz", | ||||
|        "41 720p100  16:9               1280x720p @ 100Hz", | ||||
|        "42 576p100   4:3                720x576p @ 100Hz", | ||||
|        "43 576p100H 16:9                720x576p @ 100Hz", | ||||
|        "44 576i50    4:3          720(1440)x576i @ 100Hz", | ||||
|        "45 576i50H  16:9          720(1440)x576i @ 100Hz", | ||||
|        "46 1080i60  16:9              1920x1080i @ 119.88/120Hz", | ||||
|        "47 720p120  16:9               1280x720p @ 119.88/120Hz", | ||||
|        "48 480p119   4:3                720x480p @ 119.88/120Hz", | ||||
|        "49 480p119H 16:9                720x480p @ 119.88/120Hz", | ||||
|        "50 480i59    4:3          720(1440)x480i @ 119.88/120Hz", | ||||
|        "51 480i59H  16:9          720(1440)x480i @ 119.88/120Hz", | ||||
|        "52 576p200   4:3                720x576p @ 200Hz", | ||||
|        "53 576p200H 16:9                720x576p @ 200Hz", | ||||
|        "54 576i100   4:3          720(1440)x576i @ 200Hz", | ||||
|        "55 576i100H 16:9          720(1440)x576i @ 200Hz", | ||||
|        "56 480p239   4:3                720x480p @ 239.76/240Hz", | ||||
|        "57 480p239H 16:9                720x480p @ 239.76/240Hz", | ||||
|        "58 480i119   4:3          720(1440)x480i @ 239.76/240Hz", | ||||
|        "59 480i119H 16:9          720(1440)x480i @ 239.76/240Hz", | ||||
|        "60 720p24   16:9               1280x720p @ 23.98/24Hz", | ||||
|        "61 720p25   16:9               1280x720p @ 25Hz", | ||||
|        "62 720p30   16:9               1280x720p @ 29.97/30Hz", | ||||
|        "63 1080p120 16:9               1920x1080 @ 119.88/120Hz", | ||||
| }; | ||||
|  | ||||
| int edid_dump_video_modes(u8 *edid_buf) | ||||
| { | ||||
|        int i, v1, v2, width, height, ret, aspect; | ||||
|        char *str_aspect[] = { "16:10", "4:3", "5:4", "16:9" }; | ||||
|  | ||||
|        switch (edid_buf[0]) { | ||||
|        case 0: | ||||
|                pr_info("block type 0: supported mode:\n"); | ||||
|                v1 = edid_buf[35] | edid_buf[36] << 8 | edid_buf[37] << 16; | ||||
|                /* Established timing */ | ||||
|                pr_info("established timing: {%02x, %02x, %02x}\n", | ||||
|                        edid_buf[35], edid_buf[36], edid_buf[37]); | ||||
|                for (i = 0 ; i < 18; i++ ) { | ||||
|                        v1 >>= 1; | ||||
|                        if (v1 & 1) pr_info("%s\n", established_timing_str[i]); | ||||
|                }; | ||||
|  | ||||
|                pr_info("Standard timing identification:\n"); | ||||
|                /* Standard timing identification */ | ||||
|                for (i = 0; i < 8; i++) { | ||||
|                        v1 = edid_buf[38+i*2]; | ||||
|                        v2 = edid_buf[38+i*2+1]; | ||||
|                        width = v1 * 8 + 248; | ||||
|                        aspect = v2 >> 6; | ||||
|                        switch (aspect) { | ||||
|                        case 0: height = width * 10 / 16; break; | ||||
|                        case 1: height = width * 3 / 4; break; | ||||
|                        case 2: height = width * 4 / 5; break; | ||||
|                        case 3: height = width * 9 / 16; break; | ||||
|                        } | ||||
|                        pr_info("%dx%d, %s, %d Hz\n", width, height, | ||||
|                                str_aspect[aspect], (v2 & ~(3 << 6)) + 60); | ||||
|                } | ||||
|                ret = 0; | ||||
|                break; | ||||
|        case 2: | ||||
|                pr_info("block type 2: supported mode:\n"); | ||||
|                pr_info("edid_buf[4]=%x\n", edid_buf[4]); | ||||
|                for( i = 0; i < (edid_buf[4] & 0x1f); i++) { | ||||
|                        pr_info("%s\n", vdb_modes_str[edid_buf[5+i] & 0x7f]); | ||||
|                } | ||||
|                ret = 0; | ||||
|                break; | ||||
|  | ||||
|        default: | ||||
|                ret = -EINVAL; | ||||
|                break; | ||||
|        } | ||||
|        return ret; | ||||
| } | ||||
|  | ||||
| bool edid_do_checksum(u8 *data) | ||||
| { | ||||
| 	int i; | ||||
| 	u8 sum = 0; | ||||
|  | ||||
| 	for (i = 0; i < EDID_BLOCK_SIZE; i++) | ||||
| 		sum += data[i]; | ||||
| 	EDID_DBG("%s: result=%s\n", __func__, sum ? "fail" : "pass"); | ||||
| 	return sum ? false : true; | ||||
| } | ||||
|  | ||||
| static bool edid_check_header(u8 *data) | ||||
| { | ||||
| 	int ret, i = 0; | ||||
| 	/* EDID 8 bytes header */ | ||||
| 	static u8 header[] = {0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0}; | ||||
|  | ||||
| 	for (i = 0; i < ARRAY_SIZE(header); i++) | ||||
| 		if (data[i] != header[i]) | ||||
| 			break; | ||||
| 	ret = (i == ARRAY_SIZE(header)) ? true : false; | ||||
| 	EDID_DBG("%s: result=%s\n", __func__, ret ? "pass" : "fail"); | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| bool edid_check_hdmi_sink(struct hdmi_info *hdmi, int block) | ||||
| { | ||||
| 	int ret = false, index = 4; | ||||
| 	u8 *data = &hdmi->edid_buf[block * 128]; | ||||
| 	/* block offset where long descriptors start */ | ||||
| 	int long_desc_offset = data[LONG_DESCR_PTR_IDX]; | ||||
| 	int tag_code, data_block_len; | ||||
|  | ||||
| 	while (index < long_desc_offset) { | ||||
| 		tag_code = (data[index] >> 5) & 0x7; | ||||
| 		data_block_len = data[index++] & 0x1f; | ||||
| 		if (tag_code == VENDOR_SPEC_D_BLOCK && | ||||
| 		    data[index] == 0x03 && | ||||
| 		    data[index + 1] == 0x0c && | ||||
| 		    data[index + 2] == 0x00) { | ||||
| 			ret = true; | ||||
| 			break; | ||||
| 		} else | ||||
| 			index += data_block_len; | ||||
| 	} | ||||
| 	hdmi->edid_info.hdmi_sink = ret; | ||||
| 	EDID_DBG("%s: ret=%s\n", __func__, ret ? "yes" : "no"); | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| struct edid_black_list_info { | ||||
| 	u8 mfr_model[4]; | ||||
| 	u8 prefer_modes[3]; | ||||
| }; | ||||
|  | ||||
| struct edid_black_list_info edid_black_list[] = { | ||||
| 	{ {0x4c, 0x2d, 0xa5, 0x02}, {0, 0, 0x40} },	// 720p only | ||||
| 	{ {0x4c, 0x2d, 0x0d, 0x05}, {0, 0, 0x40} },	// 720p only | ||||
|  | ||||
| 	//{ {0x5a, 0x63, 0x20, 0x2b}, {0, 0, 0x40} },	// Viewsonic test | ||||
| }; | ||||
|  | ||||
| /* By comparing the Manufacture(0x8, 0x9) and Model field(0xa, 0xb) of EDID, | ||||
|  * to check if the attached TV is a known less-compatibile one. | ||||
|  */ | ||||
| int edid_fixup_compatibility_list(struct hdmi_info *hdmi) | ||||
| { | ||||
| 	int i, ret = -1; | ||||
|  | ||||
| 	/* FIXME: magic numbers...*/ | ||||
| 	for (i = 0; i < ARRAY_SIZE(edid_black_list); i++) { | ||||
| 		if (!memcmp(hdmi->edid_buf + 8, edid_black_list[i].mfr_model, 4)){ | ||||
| #if 0 | ||||
| 			EDID_DBG("%s: found in blacklist %d\n", __func__, i); | ||||
| 			EDID_DBG("%s: old timing = {%02x, %02x, %02x}\n", | ||||
| 				__func__, | ||||
| 				hdmi->edid_buf[35], hdmi->edid_buf[36], | ||||
| 				hdmi->edid_buf[37]); | ||||
| 			memcpy(hdmi->edid_buf + 35, | ||||
| 				edid_black_list[i].prefer_modes, 3); | ||||
| 			EDID_DBG("%s: new timing = {%02x, %02x, %02x}\n", | ||||
| 				__func__, | ||||
| 				hdmi->edid_buf[35], hdmi->edid_buf[36], | ||||
| 				hdmi->edid_buf[37]); | ||||
| #else | ||||
| 			EDID_DBG("%s: found in compatibility %d\n", __func__, i); | ||||
| 			memcpy(hdmi->edid_buf + 35, | ||||
| 				edid_black_list[i].prefer_modes, 3); | ||||
| #endif | ||||
| 			ret = i; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| u8 edid_simple_parsing(struct hdmi_info *hdmi) | ||||
| { | ||||
| 	u8 *edid_buf = hdmi->edid_buf; | ||||
| 	int i, index, ret = -EINVAL; | ||||
| 	struct edid_info_struct *edid_info = &hdmi->edid_info; | ||||
| 	unsigned v1, width, height, aspect; | ||||
| 	unsigned extensions; | ||||
|  | ||||
| 	EDID_DBG("%s\n", __func__); | ||||
| 	// FIXME: integrate with edid_check() | ||||
| 	if (!edid_do_checksum(edid_buf)) { | ||||
| 		pr_err("%s: checksum error\n", __func__); | ||||
| 		//return EDID_CHECKSUM_ERROR; | ||||
| 	} | ||||
| 	if (!edid_check_header(edid_buf)) { | ||||
| 		pr_err("%s: incorrect header\n", __func__); | ||||
| 		return INCORRECT_EDID_HEADER; | ||||
| 	} | ||||
|         edid_info->under_scan = ((edid_buf[MISC_SUPPORT_IDX]) >> 7) & 0x1; | ||||
|         edid_info->basic_audio = ((edid_buf[MISC_SUPPORT_IDX]) >> 6) & 0x1; | ||||
|         edid_info->ycbcr_4_4_4 = ((edid_buf[MISC_SUPPORT_IDX]) >> 5) & 0x1; | ||||
|         edid_info->ycbcr_4_2_2 = ((edid_buf[MISC_SUPPORT_IDX]) >> 4) & 0x1; | ||||
|  | ||||
| 	// FIXME: 0x7e | ||||
| 	extensions = edid_buf[0x7e]; | ||||
| 	EDID_DBG("%s: extensions=%d\n", __func__, extensions); | ||||
| 	if (!extensions) { | ||||
| 		hdmi->edid_info.hdmi_sink = false; | ||||
| 		return NO_861_EXTENSIONS; | ||||
| 	} | ||||
| 	//return; | ||||
|  | ||||
| 	/* reset all supported */ | ||||
| 	for (i = 0 ; i < ARRAY_SIZE(additional_timing_db); i++) | ||||
| 		additional_timing_db[i].supported = false; | ||||
| 	for (i = 0 ; i < ARRAY_SIZE(established_timing_db); i++) | ||||
| 		established_timing_db[i].supported = false; | ||||
|  | ||||
| 	/* Block 0: established timing */ | ||||
| 	pr_info("established timing: {%02x, %02x, %02x}\n", | ||||
| 			edid_buf[35], edid_buf[36], edid_buf[37]); | ||||
|  | ||||
| 	v1 = edid_buf[35] | edid_buf[36] << 8 | (!!edid_buf[37]) << 16; | ||||
|  | ||||
| 	for (i = 0 ; i < 17; i++ )	// 17 bits defined in established timing | ||||
| 		established_timing_db[i].supported = ((v1 >>= 1) & 1) ; | ||||
|  | ||||
| #if 0 | ||||
| 	/* standard timing identification */ | ||||
| 	for (i = 0; i < 8; i++) { | ||||
| 		width = edid_buf[38 + i * 2] * 8 + 248; | ||||
| 		v1 = edid_buf[38 + i * 2 + 1]; | ||||
| 		switch (v1 >> 6) { | ||||
| 		case 0: height = width * 10 / 16; aspect = ASPECT(16, 10); break; | ||||
| 		case 1: height = width * 3 / 4; aspect = ASPECT(4, 3); break; | ||||
| 		case 2: height = width * 4 / 5; aspect = ASPECT(5, 4); break; | ||||
| 		case 3: height = width * 9 / 16; aspect = ASPECT(16, 9); break; | ||||
| 		} | ||||
| 		standard_timing_db[i].width = width; | ||||
| 		standard_timing_db[i].height = height; | ||||
| 		standard_timing_db[i].aspect = aspect; | ||||
| 		standard_timing_db[i].refresh_rate = (v1 & ~(3 << 6)) + 60; | ||||
| 		standard_timing_db[i].supported = true; | ||||
| 	} | ||||
| #endif | ||||
|  | ||||
| 	if (extensions == 1) { | ||||
| 		EDID_DBG("Block-1: additional timing\n"); | ||||
| 		/* Block-1: additional timing */ | ||||
| 		for( i = 0; i < (edid_buf[128 + 4] & 0x1f); i++) { | ||||
| 			index = edid_buf[128 + 5 + i] & 0x7f; | ||||
| 			additional_timing_db[index-1].supported = true; | ||||
| 			EDID_DBG("%s\n", additional_timing_db[index-1].descrption); | ||||
| 			} | ||||
| 		edid_check_hdmi_sink(hdmi, 1); | ||||
| 	} else { | ||||
| 		EDID_DBG("Block-2: additional timing\n"); | ||||
| 		for( i = 0; i < (edid_buf[384 + 4] & 0x1f); i++) { | ||||
| 			index = edid_buf[384 + 5 + i] & 0x7f; | ||||
| 			additional_timing_db[index-1].supported = true; | ||||
| 			EDID_DBG("%s\n", additional_timing_db[index-1].descrption); | ||||
| 			} | ||||
| 		edid_check_hdmi_sink(hdmi, 3); | ||||
| 	} | ||||
|  | ||||
| 	edid_buf[35] = 0; | ||||
| 	edid_buf[36] = 0; | ||||
| 	edid_buf[37] = 0; | ||||
|  | ||||
| 	/* edid_buf[37] bit4: 480p, bit5: 576p, bit6: 720p */ | ||||
| 	if (additional_timing_db[CEA_MODE_720X480P_60HZ_4_3].supported || | ||||
| 	    additional_timing_db[CEA_MODE_720X480P_60HZ_16_9].supported) { | ||||
| 		EDID_DBG("decide to support 480P\n"); | ||||
| 		edid_buf[37] |= (1<<4); | ||||
| 	} | ||||
|  | ||||
| 	if (additional_timing_db[CEA_MODE_720X576P_50HZ_4_3].supported || | ||||
| 	    additional_timing_db[CEA_MODE_720X576P_50HZ_16_9].supported) { | ||||
| 		EDID_DBG("decide to support 576P\n"); | ||||
| 		edid_buf[37] |= (1<<5); | ||||
| 	} | ||||
|  | ||||
| 	if (additional_timing_db[CEA_MODE_1280X720P_60HZ_16_9].supported) { | ||||
| 		EDID_DBG("decide to support 720P\n"); | ||||
| 		edid_buf[37] |= (1<<6); | ||||
| 	} | ||||
|  | ||||
| 	if (established_timing_db[ESTABLISHED_MODE_800X600_60HZ].supported) { | ||||
| 		EDID_DBG("decide to support 800x600\n"); | ||||
| 		edid_buf[36] |= (1<<6); | ||||
| 	} | ||||
|  | ||||
| 	if (established_timing_db[ESTABLISHED_MODE_640X480_60HZ].supported) { | ||||
| 		EDID_DBG("decide to support 640x480\n"); | ||||
| 		edid_buf[35] |= (1<<5); | ||||
| 	} | ||||
| 	edid_fixup_compatibility_list(hdmi); | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| // FIXME: modify the checking routines into inline function. | ||||
| bool edid_is_video_mode_supported(struct video_mode *vmode) | ||||
| { | ||||
| 	int i; | ||||
| 	struct video_mode *vmode_db; | ||||
|  | ||||
| 	vmode_db = established_timing_db; | ||||
| 	for (i = 0, vmode_db = established_timing_db; | ||||
| 			i < ARRAY_SIZE(established_timing_db); i++) | ||||
| 		if ( (vmode->width == vmode_db[i].width) &&  | ||||
| 		     (vmode->height == vmode_db[i].height) && | ||||
| 		     (vmode->refresh_rate == vmode_db[i].refresh_rate ) && | ||||
| 		     (vmode_db[i].interlaced == PROGRESSIVE) && | ||||
| 		     (vmode_db[i].supported == true )) | ||||
| 			return true; | ||||
| 	for (i = 0, vmode_db = standard_timing_db; | ||||
| 			i < ARRAY_SIZE(standard_timing_db); i++) | ||||
| 		if ( (vmode->width == vmode_db[i].width) && | ||||
| 		     (vmode->height == vmode_db[i].height) && | ||||
| 		     (vmode->refresh_rate == vmode_db[i].refresh_rate ) && | ||||
| 		     (vmode_db[i].interlaced == PROGRESSIVE) && | ||||
| 		     (vmode_db[i].supported == true )) | ||||
| 			return true; | ||||
|         for (i = 0, vmode_db = additional_timing_db; | ||||
|                         i < ARRAY_SIZE(additional_timing_db); i++) | ||||
|                 if ( (vmode->width == vmode_db[i].width) && | ||||
|                      (vmode->height == vmode_db[i].height) && | ||||
|                      (vmode->refresh_rate == vmode_db[i].refresh_rate ) && | ||||
|                      (vmode_db[i].interlaced == PROGRESSIVE) && | ||||
| 		     (vmode_db[i].supported == true )) | ||||
|                         return true; | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| bool edid_check_sink_type(struct hdmi_info *hdmi) | ||||
| { | ||||
|         EDID_DBG("%s: ret=%d\n", __func__, hdmi->edid_info.hdmi_sink); | ||||
|         return hdmi->edid_info.hdmi_sink; | ||||
| } | ||||
|  | ||||
| int edid_dump_hex(u8 *src, int src_size, char *output, int output_size) | ||||
| { | ||||
|         char line[80]; | ||||
|         static char hextab[] = "0123456789abcdef"; | ||||
|         int i, j, n = 0, v, len, offset, line_size; | ||||
|  | ||||
|         len = src_size; | ||||
|         memset(line, ' ', 79); | ||||
|         line[79] = '\0'; | ||||
|         offset = strlen("0000 | "); | ||||
|         line_size = offset + 3 * 16 + 1; | ||||
|         for (i = 0; i < len / 16 ; i++) { | ||||
|                 scnprintf(line, offset + 1, "%04x | ", (i << 4)); | ||||
|                 for (j = 0; j < 16 ; j++) { | ||||
|                         v = src[i * 16 + j]; | ||||
|                         line[offset + j * 3] = hextab[v / 16]; | ||||
|                         line[offset + j * 3 + 1] = hextab[v % 16]; | ||||
|                 } | ||||
|                 line[line_size - 1] = '\n'; | ||||
|                 strncpy(output+ n, line, line_size); | ||||
| 		if ((n + line_size) > output_size) | ||||
| 			break; | ||||
| 		else | ||||
|                 	n += line_size; | ||||
|         } | ||||
|  | ||||
|         return n; | ||||
| } | ||||
|  | ||||
| /*============================================================================*/ | ||||
| static ssize_t edid_dbg_open(struct inode *inode, struct file *file) | ||||
| { | ||||
|         file->private_data = inode->i_private; | ||||
|         return 0; | ||||
| } | ||||
|  | ||||
| static char hex_buff[2048]; | ||||
| static ssize_t edid_buffered_read(struct file *filp, char __user *buf, | ||||
|                 size_t count, loff_t *ppos) | ||||
| { | ||||
| 	int n; | ||||
| 	int extensions; | ||||
|         struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; | ||||
|  | ||||
| 	extensions = hdmi->edid_buf[0x7e] + 1; | ||||
| 	edid_dump_video_modes(hdmi->edid_buf);// fixme: crashed | ||||
| 	if (extensions == 1) | ||||
| 		edid_dump_video_modes(hdmi->edid_buf + 128); | ||||
| 	else | ||||
| 		edid_dump_video_modes(hdmi->edid_buf + 384); | ||||
| 	//edid_simple_parsing(hdmi); // FIXME: crashed... | ||||
| 	n = edid_dump_hex(hdmi->edid_buf, (hdmi->edid_buf[0x7e] + 1) * 128, | ||||
| 		hex_buff, 2048); | ||||
|         return simple_read_from_buffer(buf, count, ppos, hex_buff, n); | ||||
| } | ||||
|  | ||||
| static struct file_operations edid_debugfs_fops[] = { | ||||
|         { | ||||
|                 .open  = edid_dbg_open, | ||||
|                 .read  = edid_buffered_read, | ||||
|                 .write = NULL, | ||||
|         } | ||||
| }; | ||||
|  | ||||
| // TODO: error handling | ||||
| int edid_debugfs_init(struct hdmi_info *hdmi) | ||||
| { | ||||
|         //int ret; | ||||
| 	struct dentry *edid_dent; | ||||
|  | ||||
|         edid_dent = debugfs_create_dir("edid", hdmi->debug_dir); | ||||
|         if (IS_ERR(edid_dent)) | ||||
|                 return PTR_ERR(edid_dent); | ||||
|  | ||||
|         debugfs_create_file("hex_dump", 0444, edid_dent, hdmi, | ||||
|                 &edid_debugfs_fops[0]); | ||||
|  | ||||
|         return 0; | ||||
| } | ||||
|  | ||||
							
								
								
									
										716
									
								
								drivers/video/msm/hdmi/fb-hdmi.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										716
									
								
								drivers/video/msm/hdmi/fb-hdmi.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,716 @@ | ||||
| /* | ||||
|  * Copyright (C) 2009 HTC | ||||
|  * | ||||
|  * This software is licensed under the terms of the GNU General Public | ||||
|  * License version 2, as published by the Free Software Foundation, and | ||||
|  * may be copied, distributed, and modified under those terms. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * Referenced from drivers/video/msm/msm_fb.c, Google Incorporated. | ||||
|  * | ||||
|  */ | ||||
| #define DEBUG | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/fb.h> | ||||
| #include <linux/delay.h> | ||||
|  | ||||
| #include <linux/freezer.h> | ||||
| #include <linux/wait.h> | ||||
| #include <linux/wakelock.h> | ||||
| #include <linux/earlysuspend.h> | ||||
| #include <linux/msm_mdp.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/uaccess.h> | ||||
| #include <mach/msm_fb.h> | ||||
| #include <mach/board.h> | ||||
| #include <linux/workqueue.h> | ||||
| #include <linux/clk.h> | ||||
| #include <linux/debugfs.h> | ||||
| #include <linux/dma-mapping.h> | ||||
| #include <linux/htc_hdmi.h> | ||||
| #ifdef CONFIG_HTC_HEADSET_MGR | ||||
| #include <mach/htc_headset_mgr.h> | ||||
| #endif | ||||
|  | ||||
| #include "include/fb-hdmi.h" | ||||
| #include "include/sil902x.h" | ||||
|  | ||||
| #if 1 | ||||
| #define HDMI_DBG(s...) printk("[hdmi/fb]" s) | ||||
| #else | ||||
| #define HDMI_DBG(s...) do {} while (0) | ||||
| #endif | ||||
|  | ||||
| struct update_info_t { | ||||
| 	int left; | ||||
| 	int top; | ||||
| 	int eright; /* exclusive */ | ||||
| 	int ebottom; /* exclusive */ | ||||
| 	unsigned yoffset; | ||||
| }; | ||||
|  | ||||
| struct hdmifb_info { | ||||
| 	struct fb_info *fb; | ||||
| 	struct msm_panel_data *panel; | ||||
| 	struct notifier_block fb_hdmi_event; | ||||
| 	struct msmfb_callback dma_callback; | ||||
| 	struct msmfb_callback vsync_callback; | ||||
| 	struct update_info_t update_info; | ||||
| 	struct early_suspend earlier_suspend; | ||||
| 	struct early_suspend early_suspend; | ||||
| 	spinlock_t update_lock; | ||||
| 	int xres; | ||||
| 	int yres; | ||||
| 	unsigned long state; | ||||
| 	atomic_t use_count; | ||||
| }; | ||||
|  | ||||
| static struct mdp_device *mdp; | ||||
| static struct hdmi_device *hdmi; | ||||
|  | ||||
| static unsigned PP[16]; | ||||
|  | ||||
| void hdmi_pre_change(struct hdmi_info *hdmi); | ||||
| void hdmi_post_change(struct hdmi_info *info, struct fb_var_screeninfo *var); | ||||
|  | ||||
| static int hdmifb_open(struct fb_info *info, int user) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int hdmifb_release(struct fb_info *info, int user) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int hdmifb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) | ||||
| { | ||||
| 	u32 size; | ||||
|  | ||||
| 	if (mdp->check_output_format(mdp, var->bits_per_pixel)) | ||||
| 		return -EINVAL; | ||||
| 	if (hdmi->check_res(hdmi, var)) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	size = var->xres_virtual * var->yres_virtual * | ||||
| 		(var->bits_per_pixel >> 3); | ||||
| 	if (size > info->fix.smem_len) | ||||
| 		return -EINVAL; | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int hdmifb_set_par(struct fb_info *info) | ||||
| { | ||||
| 	struct fb_var_screeninfo *var = &info->var; | ||||
| 	struct fb_fix_screeninfo *fix = &info->fix; | ||||
| 	struct hdmifb_info *hdmi_fb = info->par; | ||||
| 	struct msm_panel_data *panel = hdmi_fb->panel; | ||||
| 	struct msm_lcdc_timing *timing; | ||||
|         struct hdmi_info *hinfo = container_of(hdmi, struct hdmi_info, | ||||
|                                         hdmi_dev); | ||||
|  | ||||
| 	HDMI_DBG("%s\n", __func__); | ||||
| 	/* we only support RGB ordering for now */ | ||||
| 	if (var->bits_per_pixel == 32 || var->bits_per_pixel == 24) { | ||||
| 		var->red.offset = 0; | ||||
| 		var->red.length = 8; | ||||
| 		var->green.offset = 8; | ||||
| 		var->green.length = 8; | ||||
| 		var->blue.offset = 16; | ||||
| 		var->blue.length = 8; | ||||
| 	} else if (var->bits_per_pixel == 16) { | ||||
| 		var->red.offset = 11; | ||||
| 		var->red.length = 5; | ||||
| 		var->green.offset = 5; | ||||
| 		var->green.length = 6; | ||||
| 		var->blue.offset = 0; | ||||
| 		var->blue.length = 5; | ||||
| 	} else | ||||
| 		return -1; | ||||
|  | ||||
| 	HDMI_DBG("set res (%d, %d)\n", var->xres, var->yres); | ||||
| 	timing = hdmi->set_res(hdmi, var); | ||||
| 	panel->adjust_timing(panel, timing, var->xres, var->yres); | ||||
| 	hdmi_post_change(hinfo, var); | ||||
|  | ||||
| 	mdp->set_output_format(mdp, var->bits_per_pixel); | ||||
|  | ||||
| 	hdmi_fb->xres = var->xres; | ||||
| 	hdmi_fb->yres = var->yres; | ||||
| 	fix->line_length = var->xres * var->bits_per_pixel / 8; | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* core update function */ | ||||
| static void | ||||
| hdmifb_pan_update(struct fb_info *info, uint32_t left, uint32_t top, | ||||
| 		uint32_t eright, uint32_t ebottom, uint32_t yoffset) | ||||
| { | ||||
| 	struct hdmifb_info *hdmi_fb = info->par; | ||||
| 	struct msm_panel_data *panel = hdmi_fb->panel; | ||||
| 	unsigned long irq_flags; | ||||
|  | ||||
| 	/* printk(KERN_DEBUG "%s\n", __func__); */ | ||||
| 	if ((test_bit(fb_enabled, &hdmi_fb->state) == 0) || | ||||
| 	    (test_bit(hdmi_enabled, &hdmi_fb->state) == 0)) | ||||
| 		return; | ||||
|  | ||||
| 	spin_lock_irqsave(&hdmi_fb->update_lock, irq_flags); | ||||
| 	hdmi_fb->update_info.left = left; | ||||
| 	hdmi_fb->update_info.top = top; | ||||
| 	hdmi_fb->update_info.eright = eright; | ||||
| 	hdmi_fb->update_info.ebottom = ebottom; | ||||
| 	hdmi_fb->update_info.yoffset = yoffset; | ||||
| 	spin_unlock_irqrestore(&hdmi_fb->update_lock, irq_flags); | ||||
| 	panel->request_vsync(panel, &hdmi_fb->vsync_callback); | ||||
| } | ||||
|  | ||||
| /* fb ops, fb_pan_display */ | ||||
| static int | ||||
| hdmifb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) | ||||
| { | ||||
| 	/* full update */ | ||||
| 	hdmifb_pan_update(info, 0, 0, info->var.xres, info->var.yres, | ||||
| 			var->yoffset); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static void hdmifb_fillrect(struct fb_info *p, const struct fb_fillrect *rect) | ||||
| { | ||||
| 	cfb_fillrect(p, rect); | ||||
| 	hdmifb_pan_update(p, rect->dx, rect->dy, rect->dx + rect->width, | ||||
| 			rect->dy + rect->height, 0); | ||||
| } | ||||
|  | ||||
| static void hdmifb_copyarea(struct fb_info *p, const struct fb_copyarea *area) | ||||
| { | ||||
| 	cfb_copyarea(p, area); | ||||
| 	hdmifb_pan_update(p, area->dx, area->dy, area->dx + area->width, | ||||
| 			area->dy + area->height, 0); | ||||
| } | ||||
|  | ||||
| static void hdmifb_imageblit(struct fb_info *p, const struct fb_image *image) | ||||
| { | ||||
| 	cfb_imageblit(p, image); | ||||
| 	hdmifb_pan_update(p, image->dx, image->dy, image->dx + image->width, | ||||
| 			image->dy + image->height, 0); | ||||
| } | ||||
|  | ||||
| static int hdmifb_change_mode(struct fb_info *info, unsigned int mode) | ||||
| { | ||||
| 	struct hdmifb_info *hdmi_fb = info->par; | ||||
|  | ||||
| 	/* printk(KERN_DEBUG "%s mode = %d\n", __func__, mode); */ | ||||
|  | ||||
| 	if (mode) | ||||
| 		set_bit(hdmi_mode, &hdmi_fb->state); | ||||
| 	else | ||||
| 		clear_bit(hdmi_mode, &hdmi_fb->state); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| struct hdmifb_info *_hdmi_fb; | ||||
| int hdmifb_get_mode(void) | ||||
| { | ||||
|         return test_bit(hdmi_mode, &_hdmi_fb->state); | ||||
| } | ||||
|  | ||||
| bool hdmifb_suspending = false; | ||||
|  | ||||
| static int hdmifb_pause(struct fb_info *fb, unsigned int mode) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 	struct hdmifb_info *hdmi_fb = fb->par; | ||||
| 	struct msm_panel_data *panel = hdmi_fb->panel; | ||||
| 	struct hdmi_info *info = container_of(hdmi, struct hdmi_info, | ||||
| 			hdmi_dev); | ||||
|  | ||||
| 	pr_info("%s: %d %s\n", __func__, atomic_read(&hdmi_fb->use_count), | ||||
| 		mode == 1 ? "pause" : "resume"); | ||||
|  | ||||
| 	if (mode == 1) { | ||||
| 		hdmifb_suspending = false; | ||||
| 		HDMI_DBG("%s: hdmifb_suspending = false\n", __func__); | ||||
| 		/* pause */ | ||||
| 		if (atomic_read(&hdmi_fb->use_count) == 0) | ||||
| 			goto done; | ||||
| 		if (atomic_dec_return(&hdmi_fb->use_count) == 0) { | ||||
| 			hdmi_pre_change(info); | ||||
| 			ret = panel->blank(panel); | ||||
| 			clear_bit(hdmi_enabled, &hdmi_fb->state); | ||||
| #ifdef CONFIG_HTC_HEADSET_MGR | ||||
| 			switch_send_event(BIT_HDMI_AUDIO, 0); | ||||
| #endif | ||||
| 		} | ||||
| 	} else if (mode == 0) { | ||||
| 		/* resume */ | ||||
| 		if (atomic_inc_return(&hdmi_fb->use_count) == 1) { | ||||
| 			hdmi_pre_change(info); | ||||
| 			ret = panel->unblank(panel); | ||||
| /* | ||||
| 			// set timing again to prevent TV been out of range | ||||
| 			var = &fb->var; | ||||
| 			timing = hdmi->set_res(hdmi, var); | ||||
| 			panel->adjust_timing(panel, timing, var->xres, var->yres); | ||||
| 			hdmi_post_change(info, var); | ||||
| */ | ||||
| 			set_bit(hdmi_enabled, &hdmi_fb->state); | ||||
| #ifdef CONFIG_HTC_HEADSET_MGR | ||||
| 			switch_send_event(BIT_HDMI_AUDIO, 1); | ||||
| #endif | ||||
| 		} | ||||
| 	} else | ||||
| 		ret = -EINVAL; | ||||
| done: | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| static int hdmifb_blit(struct fb_info *info, void __user *p) | ||||
| { | ||||
| 	struct mdp_blit_req req; | ||||
| 	struct mdp_blit_req_list req_list; | ||||
| 	int i; | ||||
| 	int ret; | ||||
|  | ||||
| 	if (copy_from_user(&req_list, p, sizeof(req_list))) | ||||
| 		return -EFAULT; | ||||
|  | ||||
| 	for (i = 0; i < req_list.count; i++) { | ||||
| 		struct mdp_blit_req_list *list = | ||||
| 			(struct mdp_blit_req_list *)p; | ||||
| 		if (copy_from_user(&req, &list->req[i], sizeof(req))) | ||||
| 			return -EFAULT; | ||||
| 		req.flags |= MDP_DITHER; | ||||
| 		ret = mdp->blit(mdp, info, &req); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| enum ioctl_cmd_index { | ||||
|        CMD_SET_MODE, | ||||
|        CMD_GET_MODE, | ||||
|        CMD_DISABLE, | ||||
|        CMD_ENABLE, | ||||
|        CMD_GET_STATE, | ||||
|        CMD_BLIT, | ||||
|        CMD_CABLE_STAT, | ||||
|        CMD_ESTABLISH_TIMING, | ||||
| }; | ||||
|  | ||||
| static char *cmd_str[] = { | ||||
|        "HDMI_SET_MODE", | ||||
|        "HDMI_GET_MODE", | ||||
|        "HDMI_DISABLE", | ||||
|        "HDMI_ENABLE", | ||||
|        "HDMI_GET_STATE", | ||||
|        "HDMI_BLIT", | ||||
|        "HDMI_CABLE_STAT", | ||||
|        "HDMI_ESTABLISH_TIMING", | ||||
| }; | ||||
|  | ||||
| static int hdmifb_ioctl(struct fb_info *p, unsigned int cmd, unsigned long arg) | ||||
| { | ||||
| 	struct hdmifb_info *hdmi_fb = p->par; | ||||
| 	void __user *argp = (void __user *)arg; | ||||
| 	unsigned int val; | ||||
| 	int ret = -EINVAL; | ||||
|         struct hdmi_info *hinfo = container_of(hdmi, struct hdmi_info, | ||||
|                                         hdmi_dev); | ||||
|  | ||||
| /* | ||||
| 	if (cmd != HDMI_BLIT) | ||||
| 		HDMI_DBG("%s, cmd=%d=%s\n", __func__, cmd - HDMI_SET_MODE, | ||||
| 				cmd_str[cmd-HDMI_SET_MODE]); | ||||
| */ | ||||
|  | ||||
| 	switch (cmd) { | ||||
| 	case HDMI_SET_MODE: | ||||
| 		get_user(val, (unsigned __user *) arg); | ||||
| 		//pr_info("[hdmi] SET_MODE: %d\n", val); | ||||
| 		ret = hdmifb_change_mode(p, val); | ||||
| 		break; | ||||
| 	case HDMI_GET_MODE: | ||||
| /* | ||||
| 		pr_info("[hdmi] GET_MODE: %d\n", | ||||
| 			test_bit(hdmi_mode, &hdmi_fb->state)); | ||||
| */ | ||||
| 		ret = put_user(test_bit(hdmi_mode, &hdmi_fb->state), | ||||
| 			(unsigned __user *) arg); | ||||
| 		break; | ||||
| 	case HDMI_DISABLE: | ||||
| 		get_user(val, (unsigned __user *) arg); | ||||
| 		ret = hdmifb_pause(p, 1); | ||||
| 		break; | ||||
| 	case HDMI_ENABLE: | ||||
| 		get_user(val, (unsigned __user *) arg); | ||||
| 		ret = hdmifb_pause(p, 0); | ||||
| 		break; | ||||
| 	case HDMI_GET_STATE: | ||||
| 		ret = put_user(test_bit(hdmi_enabled, &hdmi_fb->state), | ||||
| 			(unsigned __user *) arg); | ||||
| 		break; | ||||
| 	case HDMI_BLIT: | ||||
| 		if (test_bit(hdmi_enabled, &hdmi_fb->state)) | ||||
| 			ret = hdmifb_blit(p, argp); | ||||
| 		else | ||||
| 			ret = -EPERM; | ||||
| 		break; | ||||
| 	case HDMI_CABLE_STAT: { | ||||
| 		int connect; | ||||
| 		ret = hdmi->get_cable_state(hdmi, &connect); | ||||
| 		ret = put_user(connect, (unsigned __user *) arg); | ||||
| 		break; | ||||
| 	} | ||||
| 	case HDMI_ESTABLISH_TIMING: { | ||||
| 		u8 tmp[3]; | ||||
| 		hdmi->get_establish_timing(hdmi, tmp); | ||||
| 		ret = copy_to_user((unsigned __user *) arg, tmp, 3); | ||||
| 		if (ret) | ||||
| 			ret = -EFAULT; | ||||
| 		break; | ||||
| 	} | ||||
| 	case HDMI_GET_EDID: | ||||
| 		ret = copy_to_user((unsigned __user *) arg, | ||||
| 			hinfo->edid_buf, 512); | ||||
| 		break; | ||||
| 	case HDMI_GET_DISPLAY_INFO: { | ||||
| 		struct display_info dinfo; | ||||
| 		u8 *ptr = hinfo->edid_buf; | ||||
| 		dinfo.visible_width = | ||||
| 			(((u32)ptr[68] & 0xf0) << 4) | ptr[66]; | ||||
| 		dinfo.visible_height = | ||||
| 			(((u32)ptr[68] & 0x0f) << 8) | ptr[67]; | ||||
| 		dinfo.resolution_width = | ||||
| 			(((u32)ptr[58] & 0xf0) << 4) | ptr[56]; | ||||
| 		dinfo.resolution_height = | ||||
| 			(((u32)ptr[61] & 0xf0) << 4) | ptr[59]; | ||||
| 		ret = copy_to_user((unsigned __user *) arg, | ||||
| 			&dinfo, sizeof(dinfo)); | ||||
| 		break; | ||||
| 	} | ||||
| 	default: | ||||
| 		printk(KERN_ERR "hdmi: unknown cmd, cmd = %d\n", cmd); | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| static struct fb_ops hdmi_fb_ops = { | ||||
| 	.owner = THIS_MODULE, | ||||
| 	.fb_open = hdmifb_open, | ||||
| 	.fb_release = hdmifb_release, | ||||
| 	.fb_check_var = hdmifb_check_var, | ||||
| 	.fb_set_par = hdmifb_set_par, | ||||
| 	.fb_pan_display = hdmifb_pan_display, | ||||
| 	.fb_fillrect = hdmifb_fillrect, | ||||
| 	.fb_copyarea = hdmifb_copyarea, | ||||
| 	.fb_imageblit = hdmifb_imageblit, | ||||
| 	.fb_ioctl = hdmifb_ioctl, | ||||
| }; | ||||
|  | ||||
| #ifdef CONFIG_HAS_EARLYSUSPEND | ||||
| static void hdmifb_suspend(struct early_suspend *h) | ||||
| { | ||||
| 	struct hdmifb_info *hdmi_fb = container_of(h, struct hdmifb_info, | ||||
| 						early_suspend); | ||||
| 	struct msm_panel_data *panel = hdmi_fb->panel; | ||||
|  | ||||
| 	HDMI_DBG("%s, use_count=%d\n", __func__, | ||||
| 		atomic_read(&hdmi_fb->use_count)); | ||||
| 	hdmifb_suspending = true; | ||||
| 	HDMI_DBG("%s: hdmifb_suspending = true\n", __func__); | ||||
| 	if (atomic_read(&hdmi_fb->use_count) && | ||||
| 		false == test_bit(hdmi_enabled, &hdmi_fb->state) | ||||
| 		) { | ||||
| 		if (panel->blank) | ||||
| 			panel->blank(panel); | ||||
| 	} | ||||
|  | ||||
| 	if (panel->suspend) | ||||
| 		panel->suspend(panel); | ||||
|  | ||||
| 	clear_bit(hdmi_enabled, &hdmi_fb->state); | ||||
| 	clear_bit(fb_enabled, &hdmi_fb->state); | ||||
| } | ||||
|  | ||||
| static void hdmifb_resume(struct early_suspend *h) | ||||
| { | ||||
| 	struct hdmifb_info *hdmi_fb = container_of(h, struct hdmifb_info, | ||||
| 						early_suspend); | ||||
| 	struct msm_panel_data *panel = hdmi_fb->panel; | ||||
|  | ||||
| 	HDMI_DBG("%s\n", __func__); | ||||
| 	if (panel->resume) | ||||
| 		panel->resume(panel); | ||||
|  | ||||
| 	atomic_set(&hdmi_fb->use_count, 0); | ||||
| 	set_bit(fb_enabled, &hdmi_fb->state); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #define BITS_PER_PIXEL 16 | ||||
|  | ||||
| static void setup_fb_info(struct hdmifb_info *hdmi_fb) | ||||
| { | ||||
| 	struct fb_info *fb_info = hdmi_fb->fb; | ||||
| 	int r; | ||||
|  | ||||
| 	/* finish setting up the fb_info struct */ | ||||
| 	strncpy(fb_info->fix.id, "hdmi_fb", 16); | ||||
| 	fb_info->fix.ypanstep = 1; | ||||
|  | ||||
| 	fb_info->fbops = &hdmi_fb_ops; | ||||
| 	fb_info->flags = FBINFO_DEFAULT; | ||||
|  | ||||
| 	fb_info->fix.type = FB_TYPE_PACKED_PIXELS; | ||||
| 	fb_info->fix.visual = FB_VISUAL_TRUECOLOR; | ||||
| 	fb_info->fix.line_length = hdmi_fb->xres * 2; | ||||
|  | ||||
| 	fb_info->var.xres = hdmi_fb->xres; | ||||
| 	fb_info->var.yres = hdmi_fb->yres; | ||||
| 	fb_info->var.width = hdmi_fb->panel->fb_data->width; | ||||
| 	fb_info->var.height = hdmi_fb->panel->fb_data->height; | ||||
| 	fb_info->var.xres_virtual = hdmi_fb->xres; | ||||
| 	fb_info->var.yres_virtual = hdmi_fb->yres * 2; | ||||
| 	fb_info->var.bits_per_pixel = BITS_PER_PIXEL; | ||||
| 	fb_info->var.accel_flags = 0; | ||||
| 	fb_info->var.yoffset = 0; | ||||
|  | ||||
| 	fb_info->var.red.offset = 11; | ||||
| 	fb_info->var.red.length = 5; | ||||
| 	fb_info->var.red.msb_right = 0; | ||||
| 	fb_info->var.green.offset = 5; | ||||
| 	fb_info->var.green.length = 6; | ||||
| 	fb_info->var.green.msb_right = 0; | ||||
| 	fb_info->var.blue.offset = 0; | ||||
| 	fb_info->var.blue.length = 5; | ||||
| 	fb_info->var.blue.msb_right = 0; | ||||
|  | ||||
| 	r = fb_alloc_cmap(&fb_info->cmap, 16, 0); | ||||
| 	fb_info->pseudo_palette = PP; | ||||
|  | ||||
| 	PP[0] = 0; | ||||
| 	for (r = 1; r < 16; r++) | ||||
| 		PP[r] = 0xffffffff; | ||||
| } | ||||
|  | ||||
| static int | ||||
| setup_fbmem(struct hdmifb_info *hdmi_fb, struct platform_device *pdev) | ||||
| { | ||||
| 	struct fb_info *fb = hdmi_fb->fb; | ||||
| 	struct resource *res; | ||||
| 	unsigned long size = hdmi_fb->xres * hdmi_fb->yres * | ||||
| 			     (BITS_PER_PIXEL >> 3) * 2; | ||||
| 	unsigned char *fbram; | ||||
|  | ||||
| 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||||
| 	if (!res) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	/* check the resource is large enough to fit the fb */ | ||||
| 	if (resource_size(res) < size) { | ||||
| 		printk(KERN_ERR "allocated resource(%d) is too small(%lu)" | ||||
| 				"for fb\n", resource_size(res), size); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
|  | ||||
| 	fb->fix.smem_start = res->start; | ||||
| 	fb->fix.smem_len = resource_size(res); | ||||
|  | ||||
| 	fbram = ioremap(res->start, resource_size(res)); | ||||
| 	if (fbram == 0) { | ||||
| 		printk(KERN_ERR "hdmi_fb: cannot allocate fbram!\n"); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
|  | ||||
| 	fb->screen_base = fbram; | ||||
| 	memset(fbram, 0, resource_size(res)); | ||||
|  | ||||
| 	printk(KERN_DEBUG "HDMI FB: 0x%x 0x%x\n", res->start, res->end); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* Called from dma interrupt handler, must not sleep */ | ||||
| static void hdmi_handle_dma(struct msmfb_callback *callback) | ||||
| { | ||||
| 	/* printk(KERN_DEBUG "%s\n", __func__); */ | ||||
| } | ||||
|  | ||||
| /* Called from vsync interrupt handler, must not sleep */ | ||||
| static void hdmi_handle_vsync(struct msmfb_callback *callback) | ||||
| { | ||||
| 	uint32_t x, y, w, h; | ||||
| 	unsigned yoffset; | ||||
| 	unsigned addr; | ||||
| 	unsigned long irq_flags; | ||||
| 	struct fb_info *mirror_fb = registered_fb[0], *fb_hdmi; | ||||
| 	struct hdmifb_info *hdmi = container_of(callback, struct hdmifb_info, | ||||
| 						vsync_callback); | ||||
| 	struct msm_panel_data *panel = hdmi->panel; | ||||
|  | ||||
| 	spin_lock_irqsave(&hdmi->update_lock, irq_flags); | ||||
| 	x = hdmi->update_info.left; | ||||
| 	y = hdmi->update_info.top; | ||||
| 	w = hdmi->update_info.eright - x; | ||||
| 	h = hdmi->update_info.ebottom - y; | ||||
| 	yoffset = hdmi->update_info.yoffset; | ||||
| 	hdmi->update_info.left = hdmi->xres + 1; | ||||
| 	hdmi->update_info.top = hdmi->yres + 1; | ||||
| 	hdmi->update_info.eright = 0; | ||||
| 	hdmi->update_info.ebottom = 0; | ||||
| 	if (unlikely(w > hdmi->xres || h > hdmi->yres || | ||||
| 		w == 0 || h == 0)) { | ||||
| 		printk(KERN_INFO "invalid update: %d %d %d " | ||||
| 				"%d\n", x, y, w, h); | ||||
| 		goto error; | ||||
| 	} | ||||
| 	spin_unlock_irqrestore(&hdmi->update_lock, irq_flags); | ||||
|  | ||||
| 	addr = ((hdmi->xres * (yoffset + y) + x) * 2); | ||||
| 	if (test_bit(hdmi_mode, &hdmi->state) == 0) { | ||||
| 		mdp->dma(mdp, addr + mirror_fb->fix.smem_start, | ||||
| 			hdmi->xres * 2, w, h, x, y, &hdmi->dma_callback, | ||||
| 			panel->interface_type); | ||||
| 	} else { | ||||
| 		fb_hdmi = hdmi->fb; | ||||
| 		mdp->dma(mdp, addr + fb_hdmi->fix.smem_start, | ||||
| 			hdmi->xres * 2, w, h, x, y, &hdmi->dma_callback, | ||||
| 			panel->interface_type); | ||||
| 	} | ||||
| 	return; | ||||
| error: | ||||
| 	spin_unlock_irqrestore(&hdmi->update_lock, irq_flags); | ||||
| } | ||||
|  | ||||
| static int hdmifb_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	struct fb_info *info; | ||||
| 	struct hdmifb_info *hdmi_fb; | ||||
| 	struct msm_panel_data *panel = pdev->dev.platform_data; | ||||
| 	int ret; | ||||
|  | ||||
| 	printk(KERN_DEBUG "%s\n", __func__); | ||||
|  | ||||
| 	if (!panel) { | ||||
| 		pr_err("hdmi_fb_probe: no platform data\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	if (!panel->fb_data) { | ||||
| 		pr_err("hdmi_fb_probe: no fb_data\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	info = framebuffer_alloc(sizeof(struct hdmifb_info), &pdev->dev); | ||||
| 	if (!info) | ||||
| 		return -ENOMEM; | ||||
|  | ||||
| 	hdmi_fb = info->par; | ||||
| 	_hdmi_fb = hdmi_fb; | ||||
| 	hdmi_fb->fb = info; | ||||
| 	hdmi_fb->panel = panel; | ||||
| 	set_bit(hdmi_mode, &hdmi_fb->state); | ||||
| 	hdmi_fb->dma_callback.func = hdmi_handle_dma; | ||||
| 	hdmi_fb->vsync_callback.func = hdmi_handle_vsync; | ||||
| 	hdmi_fb->xres = panel->fb_data->xres; | ||||
| 	hdmi_fb->yres = panel->fb_data->yres; | ||||
| 	spin_lock_init(&hdmi_fb->update_lock); | ||||
|  | ||||
| 	ret = setup_fbmem(hdmi_fb, pdev); | ||||
| 	if (ret) | ||||
| 		goto error_setup_fbmem; | ||||
|  | ||||
| 	setup_fb_info(hdmi_fb); | ||||
|  | ||||
| 	ret = register_framebuffer(info); | ||||
| 	if (ret) | ||||
| 		goto error_register_fb; | ||||
|  | ||||
| 	printk(KERN_INFO "hdmi_fb %d * %d initialed\n", | ||||
| 			hdmi_fb->xres, hdmi_fb->yres); | ||||
|  | ||||
| #ifdef CONFIG_HAS_EARLYSUSPEND | ||||
| 	hdmi_fb->early_suspend.suspend = hdmifb_suspend; | ||||
| 	hdmi_fb->early_suspend.resume = hdmifb_resume; | ||||
| 	hdmi_fb->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; | ||||
| 	register_early_suspend(&hdmi_fb->early_suspend); | ||||
| #endif | ||||
| 	/* blank panel explicitly because we turn on clk on initial */ | ||||
| 	if (panel->blank) | ||||
| 		panel->blank(panel); | ||||
| 	set_bit(fb_enabled, &hdmi_fb->state); | ||||
| 	return 0; | ||||
|  | ||||
| error_register_fb: | ||||
| error_setup_fbmem: | ||||
| 	framebuffer_release(hdmi_fb->fb); | ||||
| 	printk(KERN_ERR "msm probe fail with %d\n", ret); | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| static struct platform_driver hdmi_frame_buffer = { | ||||
| 	.probe = hdmifb_probe, | ||||
| 	.driver = {.name = "msm_hdmi"}, | ||||
| }; | ||||
|  | ||||
| static int hdmifb_add_mdp_device(struct device *dev, | ||||
| 				struct class_interface *class_intf) | ||||
| { | ||||
| 	/* might need locking if mulitple mdp devices */ | ||||
| 	if (mdp) | ||||
| 		return 0; | ||||
| 	mdp = container_of(dev, struct mdp_device, dev); | ||||
| 	return platform_driver_register(&hdmi_frame_buffer); | ||||
| } | ||||
|  | ||||
| static void hdmifb_remove_mdp_device(struct device *dev, | ||||
| 				struct class_interface *class_intf) | ||||
| { | ||||
| 	/* might need locking if mulitple mdp devices */ | ||||
| 	if (dev != &mdp->dev) | ||||
| 		return; | ||||
| 	platform_driver_unregister(&hdmi_frame_buffer); | ||||
| 	mdp = NULL; | ||||
| } | ||||
|  | ||||
| static struct class_interface hdmi_fb_interface = { | ||||
| 	.add_dev = &hdmifb_add_mdp_device, | ||||
| 	.remove_dev = &hdmifb_remove_mdp_device, | ||||
| }; | ||||
|  | ||||
| static int hdmifb_add_hdmi_device(struct device *dev, | ||||
| 				struct class_interface *class_intf) | ||||
| { | ||||
| 	dev_dbg(dev, "%s\n", __func__); | ||||
|  | ||||
| 	if (hdmi) | ||||
| 		return 0; | ||||
| 	hdmi = container_of(dev, struct hdmi_device, dev); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static struct class_interface hdmi_interface = { | ||||
| 	.add_dev = hdmifb_add_hdmi_device, | ||||
| }; | ||||
|  | ||||
| static int __init hdmifb_init(void) | ||||
| { | ||||
| 	int rc; | ||||
|  | ||||
| 	rc = register_mdp_client(&hdmi_fb_interface); | ||||
| 	if (rc) | ||||
| 		return rc; | ||||
|  | ||||
| 	rc = register_hdmi_client(&hdmi_interface); | ||||
| 	if (rc) | ||||
| 		return rc; | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| module_init(hdmifb_init); | ||||
							
								
								
									
										633
									
								
								drivers/video/msm/hdmi/hdmi_lcdc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										633
									
								
								drivers/video/msm/hdmi/hdmi_lcdc.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,633 @@ | ||||
| /* drivers/video/msm/hdmi_lcdc.c | ||||
|  * | ||||
|  * Copyright (c) 2009 Google Inc. | ||||
|  * Copyright (c) 2009 HTC | ||||
|  * | ||||
|  * This software is licensed under the terms of the GNU General Public | ||||
|  * License version 2, as published by the Free Software Foundation, and | ||||
|  * may be copied, distributed, and modified under those terms. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <linux/clk.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/delay.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/sched.h> | ||||
| #include <linux/wait.h> | ||||
|  | ||||
| #include <linux/io.h> | ||||
| #include <asm/mach-types.h> | ||||
|  | ||||
| #include <mach/msm_fb.h> | ||||
|  | ||||
| #include "../mdp_hw.h" | ||||
| #include "../../../../arch/arm/mach-msm/proc_comm.h" | ||||
| #include "../../../../arch/arm/mach-msm/clock.h" | ||||
|  | ||||
|  | ||||
| #if 1 | ||||
| #define HDMI_DBG(s...) printk("[hdmi/lcdc]" s) | ||||
| #else | ||||
| #define HDMI_DBG(s...) do {} while (0) | ||||
| #endif | ||||
|  | ||||
| struct mdp_lcdc_info { | ||||
| 	struct mdp_info	*mdp; | ||||
| 	struct clk *mdp_clk; | ||||
| 	struct clk *pclk; | ||||
| 	struct clk *pad_pclk; | ||||
| 	struct msm_panel_data fb_panel_data; | ||||
| 	struct platform_device fb_pdev; | ||||
| 	struct msm_lcdc_platform_data *pdata; | ||||
| 	uint32_t fb_start; | ||||
|  | ||||
| 	struct msmfb_callback frame_start_cb; | ||||
| 	wait_queue_head_t vsync_waitq; | ||||
| 	int got_vsync; | ||||
|  | ||||
| 	struct { | ||||
| 		uint32_t clk_rate; | ||||
| 		uint32_t hsync_ctl; | ||||
| 		uint32_t vsync_period; | ||||
| 		uint32_t vsync_pulse_width; | ||||
| 		uint32_t display_hctl; | ||||
| 		uint32_t display_vstart; | ||||
| 		uint32_t display_vend; | ||||
| 		uint32_t hsync_skew; | ||||
| 		uint32_t polarity; | ||||
| 	} parms; | ||||
| 	atomic_t	blank_count; | ||||
| 	struct mutex	blank_lock; | ||||
| }; | ||||
| struct mdp_lcdc_info *_lcdc; | ||||
|  | ||||
| static struct mdp_device *mdp_dev; | ||||
|  | ||||
| #define panel_to_lcdc(p) container_of((p), struct mdp_lcdc_info, fb_panel_data) | ||||
|  | ||||
| /* FIXME: arrange the clock manipulating to proper place, | ||||
| 	  integrate with the counter of fb_hdmi | ||||
| */ | ||||
| int lcdc_enable_video(void) | ||||
| { | ||||
|         //struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel); | ||||
|         struct mdp_lcdc_info *lcdc = _lcdc; | ||||
|         struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops; | ||||
|  | ||||
| 	mutex_lock(&lcdc->blank_lock); | ||||
| 	if (atomic_read(&lcdc->blank_count)) | ||||
| 		goto end_enable_video; | ||||
|         HDMI_DBG("%s: enable clocks\n", __func__); | ||||
|         clk_enable(lcdc->mdp_clk); | ||||
|         clk_enable(lcdc->pclk); | ||||
|         clk_enable(lcdc->pad_pclk); | ||||
|  | ||||
| 	/* TODO: need pre-test to see if it make any influence to HDCP, | ||||
| 	 * if ebi1_clk doesn't enabled here. | ||||
| 	 */ | ||||
| 	//panel_ops->unblank(panel_ops); | ||||
|  | ||||
|         mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN); | ||||
|         atomic_inc(&lcdc->blank_count); | ||||
|         HDMI_DBG("%s, blank_count=%d\n", __func__, | ||||
| 		atomic_read(&lcdc->blank_count)); | ||||
| end_enable_video: | ||||
| 	mutex_unlock(&lcdc->blank_lock); | ||||
|  | ||||
|         return 0; | ||||
| } | ||||
|  | ||||
| int lcdc_disable_video(void) | ||||
| { | ||||
|         struct mdp_lcdc_info *lcdc = _lcdc; | ||||
|         struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops; | ||||
|  | ||||
| 	mutex_lock(&lcdc->blank_lock); | ||||
| 	if (atomic_read(&lcdc->blank_count) == 0) | ||||
| 		goto disable_video_done; | ||||
| 	if (atomic_dec_return(&lcdc->blank_count) == 0) { | ||||
| 		HDMI_DBG("%s: disable clocks\n", __func__); | ||||
| 		panel_ops->blank(panel_ops); | ||||
| 		mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN); | ||||
| 		clk_disable(lcdc->pclk); | ||||
| 		clk_disable(lcdc->pad_pclk); | ||||
| 		clk_disable(lcdc->mdp_clk); | ||||
| 	} | ||||
| disable_video_done: | ||||
| 	mutex_unlock(&lcdc->blank_lock); | ||||
| 	HDMI_DBG("%s, blank_count=%d\n", __func__, | ||||
| 			atomic_read(&lcdc->blank_count)); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int lcdc_unblank(struct msm_panel_data *fb_panel) | ||||
| { | ||||
| 	struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel); | ||||
| 	struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops; | ||||
|  | ||||
| 	HDMI_DBG("%s\n", __func__); | ||||
|  | ||||
| #if 0 | ||||
| 	HDMI_DBG("%s: enable clocks\n", __func__); | ||||
| 	clk_enable(lcdc->mdp_clk); | ||||
| 	clk_enable(lcdc->pclk); | ||||
| 	clk_enable(lcdc->pad_pclk); | ||||
|  | ||||
| 	panel_ops->unblank(panel_ops); | ||||
|  | ||||
| 	mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN); | ||||
| 	atomic_set(&lcdc->blank_count, 1); | ||||
| #else | ||||
| 	lcdc_enable_video(); | ||||
| 	/* TODO: need pre-test to see if it make any influence to HDCP, | ||||
| 	 * if ebi1_clk enabled here. | ||||
| 	 */ | ||||
|         panel_ops->unblank(panel_ops); | ||||
| #endif | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int lcdc_blank(struct msm_panel_data *fb_panel) | ||||
| { | ||||
| 	struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel); | ||||
| 	//struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops; | ||||
|  | ||||
| #if 0 | ||||
| 	mutex_lock(&lcdc->blank_lock); | ||||
| 	if (atomic_read(&lcdc->blank_count) == 0) | ||||
| 		goto blank_done; | ||||
| 	if (atomic_dec_return(&lcdc->blank_count) == 0) { | ||||
| 		HDMI_DBG("%s: disable clocks\n", __func__); | ||||
| 		panel_ops->blank(panel_ops); | ||||
| 		mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN); | ||||
| 		clk_disable(lcdc->pclk); | ||||
| 		clk_disable(lcdc->pad_pclk); | ||||
| 		clk_disable(lcdc->mdp_clk); | ||||
| 	} | ||||
| blank_done: | ||||
| 	mutex_unlock(&lcdc->blank_lock); | ||||
| 	HDMI_DBG("%s, blank_count=%d\n", __func__, | ||||
| 		atomic_read(&lcdc->blank_count)); | ||||
| #else | ||||
| 	lcdc_disable_video(); | ||||
| #endif | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int lcdc_suspend(struct msm_panel_data *fb_panel) | ||||
| { | ||||
| 	struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel); | ||||
| 	struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops; | ||||
|  | ||||
| 	//pr_info("%s: suspending\n", __func__); | ||||
| 	HDMI_DBG("%s\n", __func__); | ||||
|  | ||||
| 	if (panel_ops->uninit) | ||||
| 		panel_ops->uninit(panel_ops); | ||||
| 	lcdc_disable_video(); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int lcdc_resume(struct msm_panel_data *fb_panel) | ||||
| { | ||||
| 	struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel); | ||||
| 	struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops; | ||||
|  | ||||
| 	//pr_info("%s: resuming\n", __func__); | ||||
| 	HDMI_DBG("%s\n", __func__); | ||||
|  | ||||
| 	if (panel_ops->init) { | ||||
| 		if (panel_ops->init(panel_ops) < 0) | ||||
| 			printk(KERN_ERR "LCD init fail!\n"); | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int | ||||
| lcdc_adjust_timing(struct msm_panel_data *fb_panel, | ||||
| 		struct msm_lcdc_timing *timing, u32 xres, u32 yres) | ||||
| { | ||||
| 	struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel); | ||||
| 	unsigned int hsync_period; | ||||
| 	unsigned int hsync_start_x; | ||||
| 	unsigned int hsync_end_x; | ||||
| 	unsigned int vsync_period; | ||||
| 	unsigned int display_vstart; | ||||
| 	unsigned int display_vend; | ||||
| 	uint32_t dma_cfg; | ||||
|  | ||||
| 	clk_set_rate(lcdc->pclk, timing->clk_rate); | ||||
| 	clk_set_rate(lcdc->pad_pclk, timing->clk_rate); | ||||
| 	HDMI_DBG("%s, clk=%d, xres=%d, yres=%d,\n", __func__, | ||||
| 		clk_get_rate(lcdc->pclk), xres, yres); | ||||
|  | ||||
| 	hsync_period = (timing->hsync_pulse_width + timing->hsync_back_porch + | ||||
| 			xres + timing->hsync_front_porch); | ||||
| 	hsync_start_x = (timing->hsync_pulse_width + timing->hsync_back_porch); | ||||
| 	hsync_end_x = hsync_period - timing->hsync_front_porch - 1; | ||||
|  | ||||
| 	vsync_period = (timing->vsync_pulse_width + timing->vsync_back_porch + | ||||
| 			yres + timing->vsync_front_porch); | ||||
| 	vsync_period *= hsync_period; | ||||
|  | ||||
| 	display_vstart = timing->vsync_pulse_width + timing->vsync_back_porch; | ||||
| 	display_vstart *= hsync_period; | ||||
| 	display_vstart += timing->hsync_skew; | ||||
|  | ||||
| 	display_vend = timing->vsync_front_porch * hsync_period; | ||||
| 	display_vend = vsync_period - display_vend + timing->hsync_skew - 1; | ||||
|  | ||||
| 	/* register values we pre-compute at init time from the timing | ||||
| 	 * information in the panel info */ | ||||
| 	lcdc->parms.hsync_ctl = (((hsync_period & 0xfff) << 16) | | ||||
| 				 (timing->hsync_pulse_width & 0xfff)); | ||||
| 	lcdc->parms.vsync_period = vsync_period & 0xffffff; | ||||
| 	lcdc->parms.vsync_pulse_width = (timing->vsync_pulse_width * | ||||
| 					 hsync_period) & 0xffffff; | ||||
|  | ||||
| 	lcdc->parms.display_hctl = (((hsync_end_x & 0xfff) << 16) | | ||||
| 				    (hsync_start_x & 0xfff)); | ||||
| 	lcdc->parms.display_vstart = display_vstart & 0xffffff; | ||||
| 	lcdc->parms.display_vend = display_vend & 0xffffff; | ||||
| 	lcdc->parms.hsync_skew = timing->hsync_skew & 0xfff; | ||||
| 	lcdc->parms.polarity = ((timing->hsync_act_low << 0) | | ||||
| 				(timing->vsync_act_low << 1) | | ||||
| 				(timing->den_act_low << 2)); | ||||
| 	lcdc->parms.clk_rate = timing->clk_rate; | ||||
|  | ||||
| 	mdp_writel(lcdc->mdp, lcdc->parms.hsync_ctl, MDP_LCDC_HSYNC_CTL); | ||||
| 	mdp_writel(lcdc->mdp, lcdc->parms.vsync_period, MDP_LCDC_VSYNC_PERIOD); | ||||
| 	mdp_writel(lcdc->mdp, lcdc->parms.vsync_pulse_width, | ||||
| 		   MDP_LCDC_VSYNC_PULSE_WIDTH); | ||||
| 	mdp_writel(lcdc->mdp, lcdc->parms.display_hctl, MDP_LCDC_DISPLAY_HCTL); | ||||
| 	mdp_writel(lcdc->mdp, lcdc->parms.display_vstart, | ||||
| 		   MDP_LCDC_DISPLAY_V_START); | ||||
| 	mdp_writel(lcdc->mdp, lcdc->parms.display_vend, MDP_LCDC_DISPLAY_V_END); | ||||
| 	mdp_writel(lcdc->mdp, lcdc->parms.hsync_skew, MDP_LCDC_HSYNC_SKEW); | ||||
|  | ||||
| 	mdp_writel(lcdc->mdp, 0, MDP_LCDC_BORDER_CLR); | ||||
| 	mdp_writel(lcdc->mdp, 0x0, MDP_LCDC_UNDERFLOW_CTL); | ||||
| 	mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_HCTL); | ||||
| 	mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_START); | ||||
| 	mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_END); | ||||
| 	mdp_writel(lcdc->mdp, lcdc->parms.polarity, MDP_LCDC_CTL_POLARITY); | ||||
| 	printk("solomon: polarity=%04x\n", mdp_readl(lcdc->mdp, MDP_LCDC_CTL_POLARITY)); | ||||
|  | ||||
| 	/* config the dma_p block that drives the lcdc data */ | ||||
| 	mdp_writel(lcdc->mdp, lcdc->fb_start, MDP_DMA_P_IBUF_ADDR); | ||||
| 	mdp_writel(lcdc->mdp, (((yres & 0x7ff) << 16) | | ||||
| 			       (xres & 0x7ff)), | ||||
| 		   MDP_DMA_P_SIZE); | ||||
| 	/* TODO: pull in the bpp info from somewhere else? */ | ||||
| 	mdp_writel(lcdc->mdp, xres * 2, | ||||
| 		   MDP_DMA_P_IBUF_Y_STRIDE); | ||||
| 	mdp_writel(lcdc->mdp, 0, MDP_DMA_P_OUT_XY); | ||||
|  | ||||
| 	dma_cfg = (DMA_PACK_ALIGN_LSB | | ||||
| 		   DMA_PACK_PATTERN_RGB | | ||||
| 		   DMA_DITHER_EN); | ||||
| 	dma_cfg |= DMA_OUT_SEL_LCDC; | ||||
| 	dma_cfg |= DMA_IBUF_FORMAT_RGB565; | ||||
| 	dma_cfg |= DMA_DSTC0G_8BITS | DMA_DSTC1B_8BITS | DMA_DSTC2R_8BITS; | ||||
|  | ||||
| 	mdp_writel(lcdc->mdp, dma_cfg, MDP_DMA_P_CONFIG); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int lcdc_hw_init(struct mdp_lcdc_info *lcdc) | ||||
| { | ||||
| 	struct msm_panel_data *fb_panel = &lcdc->fb_panel_data; | ||||
| 	uint32_t dma_cfg; | ||||
|         unsigned int clk_id, clk_rate; | ||||
|  | ||||
| 	clk_enable(lcdc->mdp_clk); | ||||
| 	clk_enable(lcdc->pclk); | ||||
| 	clk_enable(lcdc->pad_pclk); | ||||
|  | ||||
| 	clk_set_rate(lcdc->pclk, lcdc->parms.clk_rate); | ||||
| 	clk_set_rate(lcdc->pad_pclk, lcdc->parms.clk_rate); | ||||
| 	printk(KERN_DEBUG "pclk = %ld, pad_pclk = %ld\n", | ||||
| 			clk_get_rate(lcdc->pclk), | ||||
| 			clk_get_rate(lcdc->pad_pclk)); | ||||
|  | ||||
| 	/* write the lcdc params */ | ||||
| 	mdp_writel(lcdc->mdp, lcdc->parms.hsync_ctl, MDP_LCDC_HSYNC_CTL); | ||||
| 	mdp_writel(lcdc->mdp, lcdc->parms.vsync_period, MDP_LCDC_VSYNC_PERIOD); | ||||
| 	mdp_writel(lcdc->mdp, lcdc->parms.vsync_pulse_width, | ||||
| 		   MDP_LCDC_VSYNC_PULSE_WIDTH); | ||||
| 	mdp_writel(lcdc->mdp, lcdc->parms.display_hctl, MDP_LCDC_DISPLAY_HCTL); | ||||
| 	mdp_writel(lcdc->mdp, lcdc->parms.display_vstart, | ||||
| 		   MDP_LCDC_DISPLAY_V_START); | ||||
| 	mdp_writel(lcdc->mdp, lcdc->parms.display_vend, MDP_LCDC_DISPLAY_V_END); | ||||
| 	mdp_writel(lcdc->mdp, lcdc->parms.hsync_skew, MDP_LCDC_HSYNC_SKEW); | ||||
|  | ||||
| 	mdp_writel(lcdc->mdp, 0, MDP_LCDC_BORDER_CLR); | ||||
| 	mdp_writel(lcdc->mdp, 0, MDP_LCDC_UNDERFLOW_CTL); | ||||
| 	mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_HCTL); | ||||
| 	mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_START); | ||||
| 	mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_END); | ||||
| 	mdp_writel(lcdc->mdp, lcdc->parms.polarity, MDP_LCDC_CTL_POLARITY); | ||||
| 	printk("solomon: polarity=%04x\n", mdp_readl(lcdc->mdp, MDP_LCDC_CTL_POLARITY)); | ||||
|  | ||||
| 	/* config the dma_p block that drives the lcdc data */ | ||||
| 	mdp_writel(lcdc->mdp, lcdc->fb_start, MDP_DMA_P_IBUF_ADDR); | ||||
| 	mdp_writel(lcdc->mdp, (((fb_panel->fb_data->yres & 0x7ff) << 16) | | ||||
| 			       (fb_panel->fb_data->xres & 0x7ff)), | ||||
| 		   MDP_DMA_P_SIZE); | ||||
| 	/* TODO: pull in the bpp info from somewhere else? */ | ||||
| 	mdp_writel(lcdc->mdp, fb_panel->fb_data->xres * 2, | ||||
| 		   MDP_DMA_P_IBUF_Y_STRIDE); | ||||
| 	mdp_writel(lcdc->mdp, 0, MDP_DMA_P_OUT_XY); | ||||
|  | ||||
| 	dma_cfg = (DMA_PACK_ALIGN_LSB | | ||||
| 		   DMA_PACK_PATTERN_RGB | | ||||
| 		   DMA_DITHER_EN); | ||||
| 	dma_cfg |= DMA_OUT_SEL_LCDC; | ||||
| 	dma_cfg |= DMA_IBUF_FORMAT_RGB565; | ||||
| 	dma_cfg |= DMA_DSTC0G_8BITS | DMA_DSTC1B_8BITS | DMA_DSTC2R_8BITS; | ||||
|  | ||||
| 	mdp_writel(lcdc->mdp, dma_cfg, MDP_DMA_P_CONFIG); | ||||
|  | ||||
| 	/* Send customized command to ARM9 for escalating DMA_P as tier-1 | ||||
| 	 * of AXI bus. | ||||
| 	 * Ref: SR#272509 | ||||
| 	 */ | ||||
| 	clk_id = USB_PHY_CLK; | ||||
| 	clk_rate = 0x1; | ||||
| 	msm_proc_comm(PCOM_CLKCTL_RPC_MIN_RATE, &clk_id, &clk_rate); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static void lcdc_wait_vsync(struct msm_panel_data *panel) | ||||
| { | ||||
| 	struct mdp_lcdc_info *lcdc = panel_to_lcdc(panel); | ||||
| 	int ret; | ||||
|  | ||||
| 	ret = wait_event_timeout(lcdc->vsync_waitq, lcdc->got_vsync, HZ / 2); | ||||
| 	if (ret == 0) | ||||
| 		pr_err("%s: timeout waiting for VSYNC\n", __func__); | ||||
| 	lcdc->got_vsync = 0; | ||||
| } | ||||
|  | ||||
| static void lcdc_request_vsync(struct msm_panel_data *fb_panel, | ||||
| 			       struct msmfb_callback *vsync_cb) | ||||
| { | ||||
| 	struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel); | ||||
|  | ||||
| 	/* the vsync callback will start the dma */ | ||||
| 	vsync_cb->func(vsync_cb); | ||||
| 	lcdc->got_vsync = 0; | ||||
| 	mdp_out_if_req_irq(mdp_dev, MSM_LCDC_INTERFACE, MDP_LCDC_FRAME_START, | ||||
| 			   &lcdc->frame_start_cb); | ||||
| 	lcdc_wait_vsync(fb_panel); | ||||
| } | ||||
|  | ||||
| static void lcdc_clear_vsync(struct msm_panel_data *fb_panel) | ||||
| { | ||||
| 	struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel); | ||||
| 	lcdc->got_vsync = 0; | ||||
| 	mdp_out_if_req_irq(mdp_dev, MSM_LCDC_INTERFACE, 0, NULL); | ||||
| } | ||||
|  | ||||
| /* called in irq context with mdp lock held, when mdp gets the | ||||
|  * MDP_LCDC_FRAME_START interrupt */ | ||||
| static void lcdc_frame_start(struct msmfb_callback *cb) | ||||
| { | ||||
| 	struct mdp_lcdc_info *lcdc; | ||||
|  | ||||
| 	lcdc = container_of(cb, struct mdp_lcdc_info, frame_start_cb); | ||||
|  | ||||
| 	lcdc->got_vsync = 1; | ||||
| 	wake_up(&lcdc->vsync_waitq); | ||||
| } | ||||
|  | ||||
| static void lcdc_dma_start(void *priv, uint32_t addr, uint32_t stride, | ||||
| 			   uint32_t width, uint32_t height, uint32_t x, | ||||
| 			   uint32_t y) | ||||
| { | ||||
| 	struct mdp_lcdc_info *lcdc = priv; | ||||
| 	struct mdp_info *mdp = lcdc->mdp; | ||||
|  | ||||
| #if 0 | ||||
| 	if (mdp->dma_config_dirty) { | ||||
| 		mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN); | ||||
| 		mdelay(20); | ||||
| 		mdp_dev->configure_dma(mdp_dev); | ||||
| 		mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN); | ||||
| 	} | ||||
| #endif | ||||
| 	mdp_writel(lcdc->mdp, stride, MDP_DMA_P_IBUF_Y_STRIDE); | ||||
| 	mdp_writel(lcdc->mdp, addr, MDP_DMA_P_IBUF_ADDR); | ||||
| } | ||||
|  | ||||
| static void precompute_timing_parms(struct mdp_lcdc_info *lcdc) | ||||
| { | ||||
| 	struct msm_lcdc_timing *timing = lcdc->pdata->timing; | ||||
| 	struct msm_fb_data *fb_data = lcdc->pdata->fb_data; | ||||
| 	unsigned int hsync_period; | ||||
| 	unsigned int hsync_start_x; | ||||
| 	unsigned int hsync_end_x; | ||||
| 	unsigned int vsync_period; | ||||
| 	unsigned int display_vstart; | ||||
| 	unsigned int display_vend; | ||||
|  | ||||
| 	hsync_period = (timing->hsync_pulse_width + timing->hsync_back_porch + | ||||
| 			fb_data->xres + timing->hsync_front_porch); | ||||
| 	hsync_start_x = (timing->hsync_pulse_width + timing->hsync_back_porch); | ||||
| 	hsync_end_x = hsync_period - timing->hsync_front_porch - 1; | ||||
|  | ||||
| 	vsync_period = (timing->vsync_pulse_width + timing->vsync_back_porch + | ||||
| 			fb_data->yres + timing->vsync_front_porch); | ||||
| 	vsync_period *= hsync_period; | ||||
|  | ||||
| 	display_vstart = timing->vsync_pulse_width + timing->vsync_back_porch; | ||||
| 	display_vstart *= hsync_period; | ||||
| 	display_vstart += timing->hsync_skew; | ||||
|  | ||||
| 	display_vend = timing->vsync_front_porch * hsync_period; | ||||
| 	display_vend = vsync_period - display_vend + timing->hsync_skew - 1; | ||||
|  | ||||
| 	/* register values we pre-compute at init time from the timing | ||||
| 	 * information in the panel info */ | ||||
| 	lcdc->parms.hsync_ctl = (((hsync_period & 0xfff) << 16) | | ||||
| 				 (timing->hsync_pulse_width & 0xfff)); | ||||
| 	lcdc->parms.vsync_period = vsync_period & 0xffffff; | ||||
| 	lcdc->parms.vsync_pulse_width = (timing->vsync_pulse_width * | ||||
| 					 hsync_period) & 0xffffff; | ||||
|  | ||||
| 	lcdc->parms.display_hctl = (((hsync_end_x & 0xfff) << 16) | | ||||
| 				    (hsync_start_x & 0xfff)); | ||||
| 	lcdc->parms.display_vstart = display_vstart & 0xffffff; | ||||
| 	lcdc->parms.display_vend = display_vend & 0xffffff; | ||||
| 	lcdc->parms.hsync_skew = timing->hsync_skew & 0xfff; | ||||
| 	lcdc->parms.polarity = ((timing->hsync_act_low << 0) | | ||||
| 				(timing->vsync_act_low << 1) | | ||||
| 				(timing->den_act_low << 2)); | ||||
| 	lcdc->parms.clk_rate = timing->clk_rate; | ||||
| } | ||||
|  | ||||
| static int hdmi_lcdc_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	struct msm_lcdc_platform_data *pdata = pdev->dev.platform_data; | ||||
| 	struct mdp_lcdc_info *lcdc; | ||||
| 	int ret = 0; | ||||
|  | ||||
| 	printk(KERN_DEBUG "%s\n", __func__); | ||||
|  | ||||
| 	if (!pdata) { | ||||
| 		pr_err("%s: no LCDC platform data found\n", __func__); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	_lcdc = lcdc = kzalloc(sizeof(struct mdp_lcdc_info), GFP_KERNEL); | ||||
| 	if (!lcdc) | ||||
| 		return -ENOMEM; | ||||
|  | ||||
| 	/* We don't actually own the clocks, the mdp does. */ | ||||
| 	lcdc->mdp_clk = clk_get(mdp_dev->dev.parent, "mdp_clk"); | ||||
| 	if (IS_ERR(lcdc->mdp_clk)) { | ||||
| 		pr_err("%s: failed to get mdp_clk\n", __func__); | ||||
| 		ret = PTR_ERR(lcdc->mdp_clk); | ||||
| 		goto err_get_mdp_clk; | ||||
| 	} | ||||
|  | ||||
| 	lcdc->pclk = clk_get(mdp_dev->dev.parent, "lcdc_pclk_clk"); | ||||
| 	if (IS_ERR(lcdc->pclk)) { | ||||
| 		pr_err("%s: failed to get lcdc_pclk\n", __func__); | ||||
| 		ret = PTR_ERR(lcdc->pclk); | ||||
| 		goto err_get_pclk; | ||||
| 	} | ||||
|  | ||||
| 	lcdc->pad_pclk = clk_get(mdp_dev->dev.parent, "lcdc_pad_pclk_clk"); | ||||
| 	if (IS_ERR(lcdc->pad_pclk)) { | ||||
| 		pr_err("%s: failed to get lcdc_pad_pclk\n", __func__); | ||||
| 		ret = PTR_ERR(lcdc->pad_pclk); | ||||
| 		goto err_get_pad_pclk; | ||||
| 	} | ||||
|  | ||||
| 	init_waitqueue_head(&lcdc->vsync_waitq); | ||||
| 	mutex_init(&lcdc->blank_lock); | ||||
| 	lcdc->pdata = pdata; | ||||
| 	lcdc->frame_start_cb.func = lcdc_frame_start; | ||||
|  | ||||
| 	platform_set_drvdata(pdev, lcdc); | ||||
|  | ||||
| 	mdp_out_if_register(mdp_dev, MSM_LCDC_INTERFACE, lcdc, MDP_DMA_P_DONE, | ||||
| 			    lcdc_dma_start); | ||||
|  | ||||
| 	precompute_timing_parms(lcdc); | ||||
|  | ||||
| 	lcdc->fb_start = pdata->fb_resource->start; | ||||
| 	lcdc->mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); | ||||
|  | ||||
| 	lcdc->fb_panel_data.suspend = lcdc_suspend; | ||||
| 	lcdc->fb_panel_data.resume = lcdc_resume; | ||||
| 	lcdc->fb_panel_data.wait_vsync = lcdc_wait_vsync; | ||||
| 	lcdc->fb_panel_data.request_vsync = lcdc_request_vsync; | ||||
| 	lcdc->fb_panel_data.clear_vsync = lcdc_clear_vsync; | ||||
| 	lcdc->fb_panel_data.blank = lcdc_blank; | ||||
| 	lcdc->fb_panel_data.unblank = lcdc_unblank; | ||||
| 	lcdc->fb_panel_data.adjust_timing = lcdc_adjust_timing; | ||||
| 	lcdc->fb_panel_data.fb_data = pdata->fb_data; | ||||
| 	lcdc->fb_panel_data.interface_type = MSM_LCDC_INTERFACE; | ||||
|  | ||||
| 	ret = lcdc_hw_init(lcdc); | ||||
| 	atomic_set(&lcdc->blank_count, 1); | ||||
| 	if (ret) { | ||||
| 		pr_err("%s: Cannot initialize the mdp_lcdc\n", __func__); | ||||
| 		goto err_hw_init; | ||||
| 	} | ||||
|  | ||||
| 	lcdc->fb_pdev.name = "msm_hdmi"; | ||||
| 	lcdc->fb_pdev.id = pdata->fb_id; | ||||
| 	lcdc->fb_pdev.resource = pdata->fb_resource; | ||||
| 	lcdc->fb_pdev.num_resources = 1; | ||||
| 	lcdc->fb_pdev.dev.platform_data = &lcdc->fb_panel_data; | ||||
|  | ||||
| 	ret = platform_device_register(&lcdc->fb_pdev); | ||||
| 	if (ret) { | ||||
| 		pr_err("%s: Cannot register msm_panel pdev\n", __func__); | ||||
| 		goto err_plat_dev_reg; | ||||
| 	} | ||||
|  | ||||
| 	pr_info("%s: initialized\n", __func__); | ||||
|  | ||||
| 	return 0; | ||||
|  | ||||
| err_plat_dev_reg: | ||||
| err_hw_init: | ||||
| 	platform_set_drvdata(pdev, NULL); | ||||
| 	clk_put(lcdc->pad_pclk); | ||||
| err_get_pad_pclk: | ||||
| 	clk_put(lcdc->pclk); | ||||
| err_get_pclk: | ||||
| 	clk_put(lcdc->mdp_clk); | ||||
| err_get_mdp_clk: | ||||
| 	kfree(lcdc); | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| static int hdmi_lcdc_remove(struct platform_device *pdev) | ||||
| { | ||||
| 	struct mdp_lcdc_info *lcdc = platform_get_drvdata(pdev); | ||||
|  | ||||
| 	platform_set_drvdata(pdev, NULL); | ||||
|  | ||||
| 	clk_put(lcdc->pclk); | ||||
| 	clk_put(lcdc->pad_pclk); | ||||
| 	kfree(lcdc); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static struct platform_driver mdp_lcdc_driver = { | ||||
| 	.probe = hdmi_lcdc_probe, | ||||
| 	.remove = hdmi_lcdc_remove, | ||||
| 	.driver = { | ||||
| 		.name	= "msm_mdp_hdmi", | ||||
| 		.owner	= THIS_MODULE, | ||||
| 	}, | ||||
| }; | ||||
|  | ||||
| static int mdp_lcdc_add_mdp_device(struct device *dev, | ||||
| 				   struct class_interface *class_intf) | ||||
| { | ||||
| 	/* might need locking if mulitple mdp devices */ | ||||
| 	if (mdp_dev) | ||||
| 		return 0; | ||||
| 	mdp_dev = container_of(dev, struct mdp_device, dev); | ||||
| 	return platform_driver_register(&mdp_lcdc_driver); | ||||
| } | ||||
|  | ||||
| static void mdp_lcdc_remove_mdp_device(struct device *dev, | ||||
| 				       struct class_interface *class_intf) | ||||
| { | ||||
| 	/* might need locking if mulitple mdp devices */ | ||||
| 	if (dev != &mdp_dev->dev) | ||||
| 		return; | ||||
| 	platform_driver_unregister(&mdp_lcdc_driver); | ||||
| 	mdp_dev = NULL; | ||||
| } | ||||
|  | ||||
| static struct class_interface mdp_lcdc_interface = { | ||||
| 	.add_dev = &mdp_lcdc_add_mdp_device, | ||||
| 	.remove_dev = &mdp_lcdc_remove_mdp_device, | ||||
| }; | ||||
|  | ||||
| static int __init mdp_lcdc_init(void) | ||||
| { | ||||
| 	return register_mdp_client(&mdp_lcdc_interface); | ||||
| } | ||||
|  | ||||
| module_init(mdp_lcdc_init); | ||||
							
								
								
									
										82
									
								
								drivers/video/msm/hdmi/include/edid.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								drivers/video/msm/hdmi/include/edid.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,82 @@ | ||||
| #ifndef _EDID_H_ | ||||
| #define _EDID_H_ | ||||
|  | ||||
| #define MAX_VIDEO_MODES			32 | ||||
| struct edid_info_struct { | ||||
|         bool            is_valid; | ||||
|         struct          mutex access_lock; | ||||
|         bool under_scan; | ||||
|         bool basic_audio; | ||||
|         bool ycbcr_4_4_4; | ||||
|         bool ycbcr_4_2_2; | ||||
|         bool hdmi_sink; | ||||
| }; | ||||
|  | ||||
| #define EDID_BLOCK_SIZE			128 | ||||
| #define EDID_HDR_NO_OF_FF   		0x06 | ||||
|  | ||||
| #define EDID_BLOCK_0_OFFSET		0x00 | ||||
| #define EDID_BLOCK_1_OFFSET		0x80 | ||||
| #define EDID_BLOCK_SIZE			128 | ||||
| #define EDID_HDR_NO_OF_FF		0x06 | ||||
| #define NUM_OF_EXTEN_ADDR		0x7E | ||||
|  | ||||
| #define EDID_TAG_ADDR       		0x00 | ||||
| #define EDID_REV_ADDR       		0x01 | ||||
| #define EDID_TAG_IDX        		0x02 | ||||
| #define LONG_DESCR_PTR_IDX  		0x02 | ||||
| #define MISC_SUPPORT_IDX    		0x03 | ||||
|  | ||||
| #define ESTABLISHED_TIMING_INDEX        35 | ||||
| #define NUM_OF_STANDARD_TIMINGS		8 | ||||
| #define STANDARD_TIMING_OFFSET		38 | ||||
| #define LONG_DESCR_LENi			18 | ||||
| #define NUM_OF_DETAILED_DESCRIPTORS	4 | ||||
|  | ||||
| #define DETAILED_TIMING_OFFSET		0x36 | ||||
|  | ||||
| /* Offsets within a Long Descriptors Block */ | ||||
| #define PIX_CLK_OFFSET			0 | ||||
| #define H_ACTIVE_OFFSET			2 | ||||
| #define H_BLANKING_OFFSET		3 | ||||
| #define V_ACTIVE_OFFSET			5 | ||||
| #define V_BLANKING_OFFSET		6 | ||||
| #define H_SYNC_OFFSET			8 | ||||
| #define H_SYNC_PW_OFFSET		9 | ||||
| #define V_SYNC_OFFSET			10 | ||||
| #define V_SYNC_PW_OFFSET		10 | ||||
| #define H_IMAGE_SIZE_OFFSET		12 | ||||
| #define V_IMAGE_SIZE_OFFSET		13 | ||||
| #define H_BORDER_OFFSET			15 | ||||
| #define V_BORDER_OFFSET			16 | ||||
| #define FLAGS_OFFSET			17 | ||||
|  | ||||
| #define AR16_10				0 | ||||
| #define AR4_3				1 | ||||
| #define AR5_4				2 | ||||
| #define AR16_9				3 | ||||
|  | ||||
| #define EDID_EXTENSION_TAG		0x02 | ||||
| #define EDID_REV_THREE			0x03 | ||||
| #define EDID_DATA_START			0x04 | ||||
|  | ||||
| #define EDID_BLOCK_0			0x00 | ||||
| #define EDID_BLOCK_2_3			0x01 | ||||
|  | ||||
| #define AUDIO_DESCR_SIZE		3 | ||||
|  | ||||
| /* Data Block Tag Codes */ | ||||
| #define AUDIO_D_BLOCK			0x01 | ||||
| #define VIDEO_D_BLOCK			0x02 | ||||
| #define VENDOR_SPEC_D_BLOCK		0x03 | ||||
| #define SPKR_ALLOC_D_BLOCK		0x04 | ||||
| #define USE_EXTENDED_TAG		0x07 | ||||
|  | ||||
| /* Extended Data Block Tag Codes */ | ||||
| #define COLORIMETRY_D_BLOCK		0x05 | ||||
|  | ||||
| #define VIDEO_CAPABILITY_D_BLOCK	0x00 | ||||
| #define HDMI_SIGNATURE_LEN		0x03 | ||||
|  | ||||
| #define CEC_PHYS_ADDR_LEN		0x02 | ||||
| #endif | ||||
							
								
								
									
										27
									
								
								drivers/video/msm/hdmi/include/fb-hdmi.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								drivers/video/msm/hdmi/include/fb-hdmi.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| #ifndef _FB_HDMI_H_ | ||||
| #define _FB_HDMI_H_ | ||||
|  | ||||
| #include <linux/device.h> | ||||
| #include <linux/fb.h> | ||||
|  | ||||
| enum hd_res { | ||||
| 	hd_720p = 0,	/* 1280 * 720 */ | ||||
| 	svga,		/* 800 * 600 */ | ||||
| 	pal,		/* 720 * 576 */ | ||||
| 	edtv,		/* 720 * 480 */ | ||||
| 	vga,		/* 640 * 480 */ | ||||
| }; | ||||
|  | ||||
| struct msm_lcdc_timing; | ||||
| struct hdmi_device { | ||||
| 	struct device dev; | ||||
| 	int (*check_res)(struct hdmi_device *, struct fb_var_screeninfo *); | ||||
| 	struct msm_lcdc_timing *(*set_res)(struct hdmi_device *, | ||||
| 					struct fb_var_screeninfo *); | ||||
| 	int (*get_cable_state)(struct hdmi_device *, int *); | ||||
| 	int (*get_establish_timing)(struct hdmi_device *, u8 *); | ||||
| }; | ||||
|  | ||||
| struct class_interface; | ||||
| int register_hdmi_client(struct class_interface *class_intf); | ||||
| #endif | ||||
							
								
								
									
										399
									
								
								drivers/video/msm/hdmi/include/sil902x.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										399
									
								
								drivers/video/msm/hdmi/include/sil902x.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,399 @@ | ||||
| #ifndef __SIL902X_H_ | ||||
| #define __SIL902X_H_ | ||||
|  | ||||
| #include <mach/msm_fb.h> | ||||
| #include "edid.h" | ||||
|  | ||||
| struct hdmi_info { | ||||
|         struct hdmi_device hdmi_dev; | ||||
|         struct i2c_client *client; | ||||
|         struct msm_lcdc_panel_ops hdmi_lcdc_ops; | ||||
|         struct work_struct work; | ||||
|         struct delayed_work hdmi_delay_work; | ||||
|         struct mutex lock; | ||||
|         struct mutex lock2; | ||||
|         struct clk *ebi1_clk; | ||||
|         int (*power)(int on); | ||||
|         void (*hdmi_gpio_on)(void); | ||||
|         void (*hdmi_gpio_off)(void); | ||||
|         enum hd_res res; | ||||
| 	// FIXME: move to edid_info_struct | ||||
|         u8 edid_buf[128 * 4]; | ||||
|         enum { | ||||
|                 SLEEP, | ||||
|                 AWAKE, | ||||
|         } sleeping; | ||||
|         bool                    polling; | ||||
|         bool                    cable_connected; | ||||
|         bool                    isr_enabled; | ||||
|         bool                    first; | ||||
| 	struct completion	hotplug_completion; | ||||
|         struct timer_list       timer; | ||||
|         struct work_struct      polling_work; | ||||
| 	struct dentry           *debug_dir; | ||||
| 	struct edid_info_struct	edid_info; | ||||
|         struct mutex polling_lock; | ||||
| 	bool			suspending; | ||||
|         bool                    user_playing; | ||||
|         bool                    video_streaming; | ||||
| }; | ||||
|  | ||||
| enum { | ||||
| 	HDMI_PIXEL_DATA		= 0x08, | ||||
| 	HDMI_AVI_INFO_FRAME	= 0x0c, | ||||
| 	HDMI_AUDIO_INFO_FRAME	= 0xbf, | ||||
| 	HDMI_SYS_CTL 		= 0x1a, | ||||
| 	HDMI_POWER 		= 0x1e, | ||||
| 	HDMI_IDENTIFY 		= 0x1b, | ||||
| 	HDMI_INT_EN 		= 0x3c, | ||||
| 	HDMI_INT_STAT 		= 0x3d, | ||||
| 	HDMI_EN_REG		= 0xc7, | ||||
| }; | ||||
|  | ||||
| /* flag bitmap for register HDMI_INT_STAT */ | ||||
| enum { | ||||
| 	HOT_PLUG_PENDING 	= (1U << 0), | ||||
| 	RX_PENDING 		= (1U << 1), | ||||
| 	HOT_PLUG_STATE 		= (1U << 2), | ||||
| 	RX_STATE 		= (1U << 3), | ||||
| 	AUDIO_ERR 		= (1U << 4), | ||||
| 	SECURITY_STATE 		= (1U << 5), | ||||
| 	HDCP_VALUE 		= (1U << 6), | ||||
| 	HDCP_AUTH 		= (1U << 7), | ||||
| }; | ||||
|  | ||||
| enum ErrorMessages { | ||||
| 	INIT_SYSTEM_SUCCESSFUL,                     // 0 | ||||
|  | ||||
| 	BLACK_BOX_OPEN_FAILURE, | ||||
| 	BLACK_BOX_OPENED_SUCCESSFULLY, | ||||
| 	HW_RESET_FAILURE, | ||||
| 	TPI_ENABLE_FAILURE, | ||||
| 	INTERRUPT_EN_FAILURE, | ||||
| 	INTERRUPT_POLLING_FAILED, | ||||
|  | ||||
| 	NO_SINK_CONNECTED, | ||||
| 	DDC_BUS_REQ_FAILURE, | ||||
| 	HDCP_FAILURE, | ||||
| 	HDCP_OK,                                    // 10 | ||||
| 	RX_AUTHENTICATED, | ||||
| 	SINK_DOES_NOT_SUPPORT_HDCP, | ||||
| 	TX_DOES_NOT_SUPPORT_HDCP, | ||||
|  | ||||
| 	ILLEGAL_AKSV, | ||||
| 	SET_PROTECTION_FAILURE, | ||||
| 	REVOKED_KEYS_FOUND, | ||||
| 	REPEATER_AUTHENTICATED, | ||||
| 	INT_STATUS_READ_FAILURE, | ||||
|  | ||||
| 	PROTECTION_OFF_FAILED, | ||||
| 	PROTECTION_ON_FAILED,                       // 20 | ||||
| 	INTERRUPT_POLLING_OK, | ||||
|  | ||||
| 	EDID_PARSING_FAILURE, | ||||
| 	VIDEO_SETUP_FAILURE, | ||||
| 	TPI_READ_FAILURE, | ||||
| 	TPI_WRITE_FAILURE, | ||||
|  | ||||
| 	INIT_VIDEO_FAILURE, | ||||
| 	DE_CANNOT_BE_SET_WITH_EMBEDDED_SYNC, | ||||
| 	SET_EMBEDDED_SYC_FAILURE, | ||||
| 	V_MODE_NOT_SUPPORTED, | ||||
|  | ||||
| 	AUD_MODE_NOT_SUPPORTED,                     // 30 | ||||
| 	I2S_NOT_SET, | ||||
|  | ||||
| 	EDID_READ_FAILURE, | ||||
| 	EDID_CHECKSUM_ERROR, | ||||
| 	INCORRECT_EDID_HEADER, | ||||
| 	EDID_EXT_TAG_ERROR, | ||||
|  | ||||
| 	EDID_REV_ADDR_ERROR, | ||||
| 	EDID_V_DESCR_OVERFLOW, | ||||
| 	INCORRECT_EDID_FILE, | ||||
| 	UNKNOWN_EDID_TAG_CODE, | ||||
| 	NO_DETAILED_DESCRIPTORS_IN_THIS_EDID_BLOCK, // 40 | ||||
| 	CONFIG_DATA_VALID, | ||||
| 	CONFIG_DATA_INVALID, | ||||
|  | ||||
| 	GPIO_ACCESS_FAILED, | ||||
| 	GPIO_CONFIG_ERROR, | ||||
|  | ||||
| 	HP_EVENT_GOING_TO_SERVICE_LOOP, | ||||
| 	EDID_PARSED_OK, | ||||
| 	VIDEO_MODE_SET_OK, | ||||
| 	AUDIO_MODE_SET_OK, | ||||
|  | ||||
| 	I2S_MAPPING_SUCCESSFUL, | ||||
| 	I2S_INPUT_CONFIG_SUCCESSFUL,                // 50 | ||||
| 	I2S_HEADER_SET_SUCCESSFUL, | ||||
| 	INTERRUPT_POLLING_SUCCESSFUL, | ||||
|  | ||||
| 	HPD_LOOP_EXITED_SUCCESSFULY, | ||||
| 	HPD_LOOP_FAILED, | ||||
| 	SINK_CONNECTED, | ||||
| 	HP_EVENT_RETURNING_FROM_SERVICE_LOOP, | ||||
| 	AVI_INFOFRAMES_SETTING_FAILED, | ||||
|  | ||||
| 	TMDS_ENABLING_FAILED, | ||||
| 	DE_SET_OK, | ||||
| 	DE_SET_FAILED, | ||||
| 	NO_861_EXTENSIONS, | ||||
|  | ||||
| 	GO_OK, | ||||
| 	IMAGE_PKTS_UPDATED_OK, | ||||
| 	MONITORING_BLOCKED, | ||||
| 	LINK_NORMAL, | ||||
| 	LINK_LOST, | ||||
| 	RENEGOTIATION_REQUIRED, | ||||
|  | ||||
| 	LINK_SUSPENDED, | ||||
| 	EDID_SHORT_DESCRIPTORS_PARSED_OK, | ||||
| 	EDID_LONG_DESCRIPTORS_PARSED_OK, | ||||
| 	DDC_BUS_RELEASE_FAILURE, | ||||
| 	FAILED_GETTING_BKSV, | ||||
|  | ||||
| 	PLL_SETUP_FAILUE, | ||||
| 	ERR_RX_QUEUE_FULL, | ||||
| 	ERR_TX_QUEUE_FULL, | ||||
| 	GBD_SET_SUCCESSFULLY, | ||||
| 	BACKDOOR_SETTING_FAILED, | ||||
| 	ERR_TX_QUEUE_EMPTY | ||||
| }; | ||||
|  | ||||
| #define BIT_0                   0x01 | ||||
| #define BIT_1                   0x02 | ||||
| #define BIT_2                   0x04 | ||||
| #define BIT_3                   0x08 | ||||
| #define BIT_4                   0x10 | ||||
| #define BIT_5                   0x20 | ||||
| #define BIT_6                   0x40 | ||||
| #define BIT_7                   0x80 | ||||
|  | ||||
| #define INTERVAL_HDCP_POLLING           (HZ / 25) | ||||
|  | ||||
| #define REQUEST_RELEASE_DDC_BEFORE_HDCP | ||||
| #define T_HDCP_ACTIVATION		500 | ||||
| #define T_HDCP_DEACTIVATION		200 | ||||
|  | ||||
| #define T_HPD_DELAY		10 | ||||
| #define TPI_INTERRUPT_EN	0x3c | ||||
| #define ALL			0xff | ||||
| #define DUMMY                   0xFD | ||||
|  | ||||
| #define SiI_DEVICE_ID           0xB0 | ||||
|  | ||||
| #define T_DDC_ACCESS    50 | ||||
| // TPI Control Masks | ||||
| // ================= | ||||
| #define BIT_OUTPUT_MODE     0x01 | ||||
| #define BIT_DDC_BUS_GRANT   0x02 | ||||
| #define BIT_DDC_BUS_REQ     0x04 | ||||
| #define BIT_TMDS_OUTPUT     0x10 | ||||
|  | ||||
| #define TPI_INTERNAL_PAGE_REG		0xBC | ||||
| #define TPI_REGISTER_OFFSET_REG		0xBD | ||||
| #define TPI_REGISTER_VALUE_REG		0xBE | ||||
|  | ||||
| /* HDCP Control Masks */ | ||||
| #define BIT_PROTECT_LEVEL		0x01 | ||||
| #define BIT_PROTECT_TYPE		0x02 | ||||
| #define BIT_REPEATER			0x08 | ||||
| #define BIT_LOCAL_PROTECT		0x40 | ||||
| #define BIT_EXT_PROTECT			0x80 | ||||
|  | ||||
| #define BITS_LINK_LOST			0x10 | ||||
| #define BITS_RENEGOTIATION		0x20 | ||||
|  | ||||
| #define BIT_TMDS_OUTPUT			0x10 | ||||
|  | ||||
| #define BIT_AUDIO_MUTE      0x10 | ||||
|  | ||||
| #define TPI_HDCP_REVISION_DATA_REG			(0x30) | ||||
| #define HDCP_MAJOR_REVISION_MASK			(BIT_7 | BIT_6 | BIT_5 | BIT_4) | ||||
| #define HDCP_MAJOR_REVISION_VALUE			(0x10) | ||||
|  | ||||
| #define HDCP_MINOR_REVISION_MASK			(BIT_3 | BIT_2 | BIT_1 | BIT_0) | ||||
| #define HDCP_MINOR_REVISION_VALUE			(0x02) | ||||
|  | ||||
| #define HDCP_REVISION       0x12 | ||||
| #define SET_PROT_ATTEMPTS   0x05 | ||||
|  | ||||
| #define AKSV_SIZE			5 | ||||
| #define BYTE_SIZE			8 | ||||
| #define NUM_OF_ONES_IN_KSV    		20 | ||||
|  | ||||
| // Interrupt Masks | ||||
| //================ | ||||
| #define HOT_PLUG_EVENT          0x01 | ||||
| #define RX_SENSE_EVENT          0x02 | ||||
| #define TPI_HOT_PLUG_STATE          0x04 | ||||
| #define RX_SENSE_STATE          0x08 | ||||
|  | ||||
| #define AUDIO_ERROR_EVENT       0x10 | ||||
| #define SECURITY_CHANGE_EVENT   0x20 | ||||
| #define V_READY_EVENT           0x40 | ||||
| #define HDCP_CHANGE_EVENT       0x80 | ||||
|  | ||||
| #define NON_MASKABLE_INT		0xFF | ||||
|  | ||||
| /* Protection Levels */ | ||||
| #define NO_PROTECTION			0x00 | ||||
| #define LOCAL_PROTECTION		0x01 | ||||
| #define EXTENDED_PROTECTION		0x03 | ||||
|  | ||||
| #define LINK_NORMAL			0 | ||||
| #define MAX_V_DESCRIPTORS		20 | ||||
| #define MAX_A_DESCRIPTORS		10 | ||||
| #define MAX_SPEAKER_CONFIGURATIONS	 4 | ||||
|  | ||||
| #define HDMI_DEBUGFS_ROOT		"hdmi" | ||||
|  | ||||
| /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// | ||||
| /*\ | ||||
| | | HDCP Implementation | ||||
| | | | ||||
| | | HDCP link security logic is implemented in certain transmitters; unique | ||||
| | |   keys are embedded in each chip as part of the solution. The security | ||||
| | |   scheme is fully automatic and handled completely by the hardware. | ||||
| \*/ | ||||
| /// HDCP Query Data Register ============================================== /// | ||||
|  | ||||
| #define TPI_HDCP_QUERY_DATA_REG                         (0x29) | ||||
|  | ||||
| #define EXTENDED_LINK_PROTECTION_MASK           (BIT_7) | ||||
| #define EXTENDED_LINK_PROTECTION_NONE           (0x00) | ||||
| #define EXTENDED_LINK_PROTECTION_SECURE         (0x80) | ||||
|  | ||||
| #define LOCAL_LINK_PROTECTION_MASK                      (BIT_6) | ||||
| #define LOCAL_LINK_PROTECTION_NONE                      (0x00) | ||||
| #define LOCAL_LINK_PROTECTION_SECURE            (0x40) | ||||
|  | ||||
| #define LINK_STATUS_MASK                                        (BIT_5 | BIT_4) | ||||
| #define LINK_STATUS_NORMAL                                      (0x00) | ||||
| #define LINK_STATUS_LINK_LOST                           (0x10) | ||||
| #define LINK_STATUS_RENEGOTIATION_REQ           (0x20) | ||||
| #define LINK_STATUS_LINK_SUSPENDED                      (0x30) | ||||
|  | ||||
| #define HDCP_REPEATER_MASK                                      (BIT_3) | ||||
| #define HDCP_REPEATER_NO                                        (0x00) | ||||
| #define HDCP_REPEATER_YES                                       (0x08) | ||||
|  | ||||
| #define CONNECTOR_TYPE_MASK                                     (BIT_2 | BIT_0) | ||||
| #define CONNECTOR_TYPE_DVI                                      (0x00) | ||||
| #define CONNECTOR_TYPE_RSVD                                     (0x01) | ||||
| #define CONNECTOR_TYPE_HDMI                                     (0x04) | ||||
| #define CONNECTOR_TYPE_FUTURE                           (0x05) | ||||
|  | ||||
| #define PROTECTION_TYPE_MASK                            (BIT_1) | ||||
| #define PROTECTION_TYPE_NONE                            (0x00) | ||||
| #define PROTECTION_TYPE_HDCP                            (0x02) | ||||
|  | ||||
| /// HDCP Control Data Register ============================================ /// | ||||
|  | ||||
| #define TPI_HDCP_CONTROL_DATA_REG                       (0x2A) | ||||
|  | ||||
| #define PROTECTION_LEVEL_MASK                           (BIT_0) | ||||
| #define PROTECTION_LEVEL_MIN                            (0x00) | ||||
| #define PROTECTION_LEVEL_MAX                            (0x01) | ||||
|  | ||||
| /*---------------------------------------------------------------------------*/ | ||||
|  | ||||
| #if 0 | ||||
| /* Caller: ChangeVideoMode(), HDCP_Poll(), HotPlugServiceLoop(), RestartHDCP() | ||||
|  */ | ||||
| #define EnableTMDS(hdmi)	ReadClearWriteTPI(hdmi, TPI_SYSTEM_CONTROL, BIT_TMDS_OUTPUT)  // 0x1A[4] = 0 | ||||
|  | ||||
| /* Caller: ChangeVideoMode(), HDCP_Poll(), TPI_Poll(), RestartHDCP(), | ||||
|  * 	   OnHdmiCableDisconnected() | ||||
|  */ | ||||
| #define DisableTMDS(hdmi)	ReadSetWriteTPI(hdmi, TPI_SYSTEM_CONTROL, BIT_TMDS_OUTPUT)    // 0x1A[4] = 1 | ||||
| #else | ||||
|  | ||||
| void EnableTMDS(struct hdmi_info *hdmi); | ||||
| void DisableTMDS(struct hdmi_info *hdmi); | ||||
|  | ||||
| #endif | ||||
|  | ||||
| // FIXME: fix the global variables | ||||
| extern u8 pvid_mode, vid_mode; | ||||
| extern u8 LinkProtectionLevel; | ||||
| extern u8 systemInitialized; | ||||
| /*---------------------------------------------------------------------------*/ | ||||
| int hdmi_read(struct i2c_client *client, u8 cmd); | ||||
| int hdmi_write_byte(struct i2c_client *client, u8 reg, u8 val); | ||||
| int hdmi_enable_int(struct i2c_client *client); | ||||
| int hdmi_disable_int(struct i2c_client *client); | ||||
| int hdmi_read_edid(struct hdmi_info *info, struct i2c_client *client); | ||||
| int hdmi_standby(struct hdmi_info *hdmi); | ||||
| int hdmi_wakeup(struct hdmi_info *hdmi); | ||||
| int read_backdoor_register(struct hdmi_info *hdmi, u8 PageNum, u8 RegOffset); | ||||
| void ReadSetWriteTPI(struct hdmi_info *hdmi, u8 Offset, u8 Pattern); | ||||
| void ReadModifyWriteTPI(struct hdmi_info *hdmi, u8 Offset, u8 Mask, u8 Value); | ||||
| void tpi_clear_interrupt(struct hdmi_info *hdmi, u8 pattern); | ||||
| bool tpi_init(struct hdmi_info *hdmi); | ||||
| void ReadClearWriteTPI(struct hdmi_info *hdmi, u8 Offset, u8 Pattern); | ||||
| s32 ReadBlockTPI(struct hdmi_info *hdmi, u8 TPI_Offset, u16 NBytes, u8 *pData); | ||||
| bool IsRepeater(struct hdmi_info *hdmi); | ||||
| bool GetDDC_Access(struct hdmi_info *hdmi, u8* SysCtrlRegVal); | ||||
| bool ReleaseDDC(struct hdmi_info *hdmi, u8 SysCtrlRegVal); | ||||
| int tpi_read_backdoor_register(struct hdmi_info *hdmi, u8 PageNum, u8 RegOffset) | ||||
| ; | ||||
| void tpi_write_backdoor_register(struct hdmi_info *hdmi, u8 PageNum, u8 RegOffset, u8 RegValue); | ||||
|  | ||||
| int HotPlugServiceLoop(struct hdmi_info *hdmi); | ||||
|  | ||||
| int hdmi_active9022(struct i2c_client *client); | ||||
| int hdmi_active9022_dup(struct i2c_client *client); | ||||
| bool avc_send_avi_info_frames(struct hdmi_info *hdmi); | ||||
| bool avc_init_video(struct hdmi_info *hdmi, u8 mode, u8 TclkSel, bool Init); | ||||
| //void hdcp_on(struct hdmi_info *hdmi); | ||||
| void hdcp_off(struct hdmi_info *hdmi); | ||||
| void hdcp_check_status(struct hdmi_info *hdmi, u8 InterruptStatusImage); | ||||
| void hdcp_init(struct hdmi_info *hdmi); | ||||
| int hdcp_debugfs_init(struct hdmi_info *hdmi); | ||||
|  | ||||
| extern u8	EDID_TempData[]; | ||||
| u8 edid_simple_parsing(struct hdmi_info *hdmi); | ||||
|  | ||||
| int edid_dump_hex(u8 *src, int src_size, char *output, int output_size); | ||||
| bool edid_is_video_mode_supported(struct video_mode *vmode); | ||||
| int edid_debugfs_init(struct hdmi_info *hdmi); | ||||
| bool edid_check_sink_type(struct hdmi_info *hdmi); | ||||
| int HotPlugServiceLoop(struct hdmi_info *hdmi); | ||||
| int tpi_prepare(struct hdmi_info *hdmi); | ||||
|  | ||||
| //bool InitVideo(struct hdmi_info *hdmi, u8 Mode, u8 TclkSel, bool Init); | ||||
| void avc_set_basic_audio(struct hdmi_info *hdmi); | ||||
|  | ||||
| int hdmi_debugfs_init(struct hdmi_info *hdmi); | ||||
| int tpi_debugfs_init(struct hdmi_info *hdmi); | ||||
| ssize_t hdmi_dbgfs_open(struct inode *inode, struct file *file); | ||||
|  | ||||
| void SetAudioMute(struct hdmi_info *hdmi, u8 audioMute); | ||||
| void SetInputColorSpace(struct hdmi_info *hdmi, u8 inputColorSpace); | ||||
|  | ||||
| void WriteBackDoorRegister(struct hdmi_info *hdmi, u8 PageNum, u8 RegOffset, u8 RegValue); | ||||
|  | ||||
| #define	TPI_INPUT_FORMAT		0x09 | ||||
| #define	TPI_OUTPUT_FORMAT		0x0A | ||||
|  | ||||
| #define	TPI_SYSTEM_CONTROL		0x1A | ||||
|  | ||||
| #define	TPI_DEVICE_POWER_STATE_CTRL_REG		(0x1E) | ||||
|  | ||||
| #define CTRL_PIN_CONTROL_MASK				(BIT_4) | ||||
| #define CTRL_PIN_TRISTATE					(0x00) | ||||
| #define CTRL_PIN_DRIVEN_TX_BRIDGE			(0x10) | ||||
|  | ||||
| #define TX_POWER_STATE_D0					(0x00) | ||||
| #define TX_POWER_STATE_D1					(0x01) | ||||
| #define TX_POWER_STATE_D2					(0x02) | ||||
| #define TX_POWER_STATE_D3					(0x03) | ||||
|  | ||||
| #define	TPI_AUDIO_INTERFACE		0x26 | ||||
|  | ||||
| #define	TPI_HDCP_QUERY_DATA		0x29 | ||||
| #define	TPI_HDCP_CTRL			0x2A | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										411
									
								
								drivers/video/msm/hdmi/include/tpi.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										411
									
								
								drivers/video/msm/hdmi/include/tpi.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,411 @@ | ||||
| #ifndef _TPI_H_ | ||||
| #define _TPI_H_ | ||||
|  | ||||
| #define TPI_PIX_CLK_LSB						(0x00) | ||||
| #define TPI_PIX_CLK_MSB						(0x01) | ||||
|  | ||||
| #define TPI_VERT_FREQ_LSB					(0x02) | ||||
| #define TPI_VERT_FREQ_MSB					(0x03) | ||||
|  | ||||
| #define TPI_TOTAL_PIX_LSB					(0x04) | ||||
| #define TPI_TOTAL_PIX_MSB					(0x05) | ||||
|  | ||||
| #define TPI_TOTAL_LINES_LSB					(0x06) | ||||
| #define TPI_TOTAL_LINES_MSB					(0x07) | ||||
|  | ||||
| // Pixel Repetition Data | ||||
| //====================== | ||||
|  | ||||
| #define TPI_PIX_REPETITION					(0x08) | ||||
|  | ||||
| // TPI AVI Input and Output Format Data | ||||
| //===================================== | ||||
|  | ||||
| /// AVI Input Format Data ================================================= /// | ||||
|  | ||||
| #define TPI_INPUT_FORMAT_REG				(0x09) | ||||
|  | ||||
| //Finish this... | ||||
|  | ||||
| #define INPUT_COLOR_SPACE_MASK				(BIT_1 | BIT_0) | ||||
| #define INPUT_COLOR_SPACE_RGB				(0x00) | ||||
| #define INPUT_COLOR_SPACE_YCBCR444			(0x01) | ||||
| #define INPUT_COLOR_SPACE_YCBCR422			(0x02) | ||||
| #define INPUT_COLOR_SPACE_BLACK_MODE		(0x03) | ||||
|  | ||||
| /// AVI Output Format Data ================================================ /// | ||||
|  | ||||
| #define TPI_OUTPUT_FORMAT_REG				(0x0A) | ||||
|  | ||||
| #define TPI_YC_Input_Mode					(0x0B) | ||||
|  | ||||
| // TPI AVI InfoFrame Data | ||||
| //=======================  | ||||
|  | ||||
| #define TPI_AVI_BYTE_0						(0x0C) | ||||
| #define TPI_AVI_BYTE_1						(0x0D) | ||||
| #define TPI_AVI_BYTE_2						(0x0E) | ||||
| #define TPI_AVI_BYTE_3						(0x0F) | ||||
| #define TPI_AVI_BYTE_4						(0x10) | ||||
| #define TPI_AVI_BYTE_5						(0x11) | ||||
|  | ||||
| #define TPI_AUDIO_BYTE_0					(0xBF) | ||||
|  | ||||
| #define TPI_INFO_FRM_DBYTE5					0xC8 | ||||
| #define TPI_INFO_FRM_DBYTE6					0xC9 | ||||
|  | ||||
| #define TPI_END_TOP_BAR_LSB					(0x12) | ||||
| #define TPI_END_TOP_BAR_MSB					(0x13) | ||||
|  | ||||
| #define TPI_START_BTM_BAR_LSB				(0x14) | ||||
| #define TPI_START_BTM_BAR_MSB				(0x15) | ||||
|  | ||||
| #define TPI_END_LEFT_BAR_LSB				(0x16) | ||||
| #define TPI_END_LEFT_BAR_MSB				(0x17) | ||||
|  | ||||
| #define TPI_END_RIGHT_BAR_LSB				(0x18) | ||||
| #define TPI_END_RIGHT_BAR_MSB				(0x19) | ||||
|  | ||||
| // Colorimetry | ||||
| //============ | ||||
| #define SET_EX_COLORIMETRY	0x0C	// Set TPI_AVI_BYTE_2 to extended colorimetry and use  | ||||
| 									//TPI_AVI_BYTE_3 | ||||
|  | ||||
| // ===================================================== // | ||||
|  | ||||
| #define TPI_SYSTEM_CONTROL_DATA_REG			(0x1A) | ||||
|  | ||||
| #define LINK_INTEGRITY_MODE_MASK			(BIT_6) | ||||
| #define LINK_INTEGRITY_STATIC				(0x00) | ||||
| #define LINK_INTEGRITY_DYNAMIC				(0x40) | ||||
|  | ||||
| #define TMDS_OUTPUT_CONTROL_MASK			(BIT_4) | ||||
| #define TMDS_OUTPUT_CONTROL_ACTIVE			(0x00) | ||||
| #define TMDS_OUTPUT_CONTROL_POWER_DOWN		(0x10) | ||||
|  | ||||
| #define AV_MUTE_MASK						(BIT_3) | ||||
| #define AV_MUTE_NORMAL						(0x00) | ||||
| #define AV_MUTE_MUTED						(0x08) | ||||
|  | ||||
| #define DDC_BUS_REQUEST_MASK				(BIT_2) | ||||
| #define DDC_BUS_REQUEST_NOT_USING			(0x00) | ||||
| #define DDC_BUS_REQUEST_REQUESTED			(0x04) | ||||
|  | ||||
| #define DDC_BUS_GRANT_MASK					(BIT_1) | ||||
| #define DDC_BUS_GRANT_NOT_AVAILABLE			(0x00) | ||||
| #define DDC_BUS_GRANT_GRANTED				(0x02) | ||||
|  | ||||
| #define OUTPUT_MODE_MASK					(BIT_0) | ||||
| #define OUTPUT_MODE_DVI						(0x00) | ||||
| #define OUTPUT_MODE_HDMI					(0x01) | ||||
|  | ||||
|  | ||||
| // TPI Identification Registers | ||||
| //============================= | ||||
|  | ||||
| #define TPI_DEVICE_ID						(0x1B) | ||||
| #define TPI_DEVICE_REV_ID					(0x1C) | ||||
|  | ||||
| #define TPI_RESERVED2						(0x1D) | ||||
|  | ||||
| // ===================================================== // | ||||
|  | ||||
| #define TPI_DEVICE_POWER_STATE_CTRL_REG		(0x1E) | ||||
|  | ||||
| #define CTRL_PIN_CONTROL_MASK				(BIT_4) | ||||
| #define CTRL_PIN_TRISTATE					(0x00) | ||||
| #define CTRL_PIN_DRIVEN_TX_BRIDGE			(0x10) | ||||
|  | ||||
| #define TX_POWER_STATE_MASK					(BIT_1 | BIT_0) | ||||
| #define TX_POWER_STATE_D0					(0x00) | ||||
| #define TX_POWER_STATE_D1					(0x01) | ||||
| #define TX_POWER_STATE_D2					(0x02) | ||||
| #define TX_POWER_STATE_D3					(0x03) | ||||
|  | ||||
| // Configuration of I2S Interface | ||||
| //=============================== | ||||
|  | ||||
| #define TPI_I2S_EN							(0x1F) | ||||
| #define TPI_I2S_IN_CFG						(0x20) | ||||
|  | ||||
| // Available only when TPI 0x26[7:6]=10 to select I2S input | ||||
| //========================================================= | ||||
| #define TPI_I2S_CHST_0						(0x21) | ||||
| #define TPI_I2S_CHST_1						(0x22) | ||||
| #define TPI_I2S_CHST_2						(0x23) | ||||
| #define TPI_I2S_CHST_3						(0x24) | ||||
| #define TPI_I2S_CHST_4						(0x25) | ||||
|  | ||||
|  | ||||
| // Available only when 0x26[7:6]=01 | ||||
| //================================= | ||||
| #define TPI_SPDIF_HEADER					(0x24) | ||||
| #define TPI_AUDIO_HANDLING					(0x25) | ||||
|  | ||||
|  | ||||
| // Audio Configuration Regiaters | ||||
| //============================== | ||||
| #define TPI_AUDIO_INTERFACE_REG				(0x26) | ||||
|  | ||||
| // Finish this... | ||||
|  | ||||
| #define AUDIO_MUTE_MASK						(BIT_4) | ||||
| #define AUDIO_MUTE_NORMAL					(0x00) | ||||
| #define AUDIO_MUTE_MUTED					(0x10) | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| #define TPI_AUDIO_SAMPLE_CTRL				(0x27) | ||||
|  | ||||
| #define TPI_SPEAKER_CFG						(0xC7) | ||||
| #define TPI_CHANNEL_COUNT					(0xC4) | ||||
|  | ||||
| /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// | ||||
|  | ||||
| /*\ | ||||
| | | HDCP Implementation | ||||
| | | | ||||
| | | HDCP link security logic is implemented in certain transmitters; unique | ||||
| | |   keys are embedded in each chip as part of the solution. The security  | ||||
| | |   scheme is fully automatic and handled completely by the hardware. | ||||
| \*/ | ||||
|  | ||||
| /// HDCP Query Data Register ============================================== /// | ||||
|  | ||||
| #define TPI_HDCP_QUERY_DATA_REG				(0x29) | ||||
|  | ||||
| #define EXTENDED_LINK_PROTECTION_MASK		(BIT_7) | ||||
| #define EXTENDED_LINK_PROTECTION_NONE		(0x00) | ||||
| #define EXTENDED_LINK_PROTECTION_SECURE		(0x80) | ||||
|  | ||||
| #define LOCAL_LINK_PROTECTION_MASK			(BIT_6) | ||||
| #define LOCAL_LINK_PROTECTION_NONE			(0x00) | ||||
| #define LOCAL_LINK_PROTECTION_SECURE		(0x40) | ||||
|  | ||||
| #define LINK_STATUS_MASK					(BIT_5 | BIT_4) | ||||
| #define LINK_STATUS_NORMAL					(0x00) | ||||
| #define LINK_STATUS_LINK_LOST				(0x10) | ||||
| #define LINK_STATUS_RENEGOTIATION_REQ		(0x20) | ||||
| #define LINK_STATUS_LINK_SUSPENDED			(0x30) | ||||
|  | ||||
| #define HDCP_REPEATER_MASK					(BIT_3) | ||||
| #define HDCP_REPEATER_NO					(0x00) | ||||
| #define HDCP_REPEATER_YES					(0x08) | ||||
|  | ||||
| #define CONNECTOR_TYPE_MASK					(BIT_2 | BIT_0) | ||||
| #define CONNECTOR_TYPE_DVI					(0x00) | ||||
| #define CONNECTOR_TYPE_RSVD					(0x01) | ||||
| #define CONNECTOR_TYPE_HDMI					(0x04) | ||||
| #define CONNECTOR_TYPE_FUTURE				(0x05) | ||||
|  | ||||
| #define PROTECTION_TYPE_MASK				(BIT_1) | ||||
| #define PROTECTION_TYPE_NONE				(0x00) | ||||
| #define PROTECTION_TYPE_HDCP				(0x02) | ||||
|  | ||||
| /// HDCP Control Data Register ============================================ /// | ||||
|  | ||||
| #define TPI_HDCP_CONTROL_DATA_REG			(0x2A) | ||||
|  | ||||
| #define PROTECTION_LEVEL_MASK				(BIT_0) | ||||
| #define PROTECTION_LEVEL_MIN				(0x00) | ||||
| #define PROTECTION_LEVEL_MAX				(0x01) | ||||
|  | ||||
| /// HDCP BKSV Registers =================================================== /// | ||||
|  | ||||
| #define TPI_BKSV_1_REG						(0x2B) | ||||
| #define TPI_BKSV_2_REG						(0x2C) | ||||
| #define TPI_BKSV_3_REG						(0x2D) | ||||
| #define TPI_BKSV_4_REG						(0x2E) | ||||
| #define TPI_BKSV_5_REG						(0x2F) | ||||
|  | ||||
| /// HDCP Revision Data Register =========================================== /// | ||||
|  | ||||
| #define TPI_HDCP_REVISION_DATA_REG			(0x30) | ||||
|  | ||||
| #define HDCP_MAJOR_REVISION_MASK			(BIT_7 | BIT_6 | BIT_5 | BIT_4) | ||||
| #define HDCP_MAJOR_REVISION_VALUE			(0x10) | ||||
|  | ||||
| #define HDCP_MINOR_REVISION_MASK			(BIT_3 | BIT_2 | BIT_1 | BIT_0) | ||||
| #define HDCP_MINOR_REVISION_VALUE			(0x02) | ||||
|  | ||||
| /// HDCP KSV and V' Value Data Register =================================== /// | ||||
|  | ||||
| #define TPI_V_PRIME_SELECTOR_REG			(0x31) | ||||
|  | ||||
| /// V' Value Readback Registers =========================================== /// | ||||
|  | ||||
| #define TPI_V_PRIME_7_0_REG					(0x32) | ||||
| #define TPI_V_PRIME_15_9_REG				(0x33) | ||||
| #define TPI_V_PRIME_23_16_REG				(0x34) | ||||
| #define TPI_V_PRIME_31_24_REG				(0x35) | ||||
|  | ||||
| /// HDCP AKSV Registers =================================================== /// | ||||
|  | ||||
| #define TPI_AKSV_1_REG						(0x36) | ||||
| #define TPI_AKSV_2_REG						(0x37) | ||||
| #define TPI_AKSV_3_REG						(0x38) | ||||
| #define TPI_AKSV_4_REG						(0x39) | ||||
| #define TPI_AKSV_5_REG						(0x3A) | ||||
|  | ||||
| /*\ | ||||
| | | Interrupt Service | ||||
| | | | ||||
| | | TPI can be configured to generate an interrupt to the host to notify it of | ||||
| | |   various events. The host can either poll for activity or use an interrupt | ||||
| | |   handler routine. TPI generates on a single interrupt (INT) to the host. | ||||
| \*/ | ||||
|  | ||||
| /// Interrupt Enable Register ============================================= /// | ||||
|  | ||||
| #define TPI_INTERRUPT_ENABLE_REG			(0x3C) | ||||
|  | ||||
| #define HDCP_AUTH_STATUS_CHANGE_EN_MASK		(BIT_7) | ||||
| #define HDCP_AUTH_STATUS_CHANGE_DISABLE		(0x00) | ||||
| #define HDCP_AUTH_STATUS_CHANGE_ENABLE		(0x80) | ||||
|  | ||||
| #define HDCP_VPRIME_VALUE_READY_EN_MASK		(BIT_6) | ||||
| #define HDCP_VPRIME_VALUE_READY_DISABLE		(0x00) | ||||
| #define HDCP_VPRIME_VALUE_READY_ENABLE		(0x40) | ||||
|  | ||||
| #define HDCP_SECURITY_CHANGE_EN_MASK		(BIT_5) | ||||
| #define HDCP_SECURITY_CHANGE_DISABLE		(0x00) | ||||
| #define HDCP_SECURITY_CHANGE_ENABLE			(0x20) | ||||
|  | ||||
| #define AUDIO_ERROR_EVENT_EN_MASK			(BIT_4) | ||||
| #define AUDIO_ERROR_EVENT_DISABLE			(0x00) | ||||
| #define AUDIO_ERROR_EVENT_ENABLE			(0x10) | ||||
|  | ||||
| #define CPI_EVENT_NO_RX_SENSE_MASK			(BIT_3) | ||||
| #define CPI_EVENT_NO_RX_SENSE_DISABLE		(0x00) | ||||
| #define CPI_EVENT_NO_RX_SENSE_ENABLE		(0x08) | ||||
|  | ||||
| #define RECEIVER_SENSE_EVENT_EN_MASK		(BIT_1) | ||||
| #define RECEIVER_SENSE_EVENT_DISABLE		(0x00) | ||||
| #define RECEIVER_SENSE_EVENT_ENABLE			(0x02) | ||||
|  | ||||
| #define HOT_PLUG_EVENT_EN_MASK				(BIT_0) | ||||
| #define HOT_PLUG_EVENT_DISABLE				(0x00) | ||||
| #define HOT_PLUG_EVENT_ENABLE				(0x01) | ||||
|  | ||||
| /// Interrupt Status Register ============================================= /// | ||||
|  | ||||
| #define TPI_INTERRUPT_STATUS_REG			(0x3D) | ||||
|  | ||||
| #define HDCP_AUTH_STATUS_CHANGE_EVENT_MASK	(BIT_7) | ||||
| #define HDCP_AUTH_STATUS_CHANGE_EVENT_NO	(0x00) | ||||
| #define HDCP_AUTH_STATUS_CHANGE_EVENT_YES	(0x80) | ||||
|  | ||||
| #define HDCP_VPRIME_VALUE_READY_EVENT_MASK	(BIT_6) | ||||
| #define HDCP_VPRIME_VALUE_READY_EVENT_NO	(0x00) | ||||
| #define HDCP_VPRIME_VALUE_READY_EVENT_YES	(0x40) | ||||
|  | ||||
| #define HDCP_SECURITY_CHANGE_EVENT_MASK		(BIT_5) | ||||
| #define HDCP_SECURITY_CHANGE_EVENT_NO		(0x00) | ||||
| #define HDCP_SECURITY_CHANGE_EVENT_YES		(0x20) | ||||
|  | ||||
| #define AUDIO_ERROR_EVENT_MASK				(BIT_4) | ||||
| #define AUDIO_ERROR_EVENT_NO				(0x00) | ||||
| #define AUDIO_ERROR_EVENT_YES				(0x10) | ||||
|  | ||||
| #define CPI_EVENT_MASK						(BIT_3) | ||||
| #define CPI_EVENT_NO						(0x00) | ||||
| #define CPI_EVENT_YES						(0x08) | ||||
| #define RX_SENSE_MASK						(BIT_3)		// This bit is dual purpose depending on the value of 0x3C[3] | ||||
| #define RX_SENSE_NOT_ATTACHED				(0x00) | ||||
| #define RX_SENSE_ATTACHED					(0x08) | ||||
|  | ||||
| #define HOT_PLUG_PIN_STATE_MASK				(BIT_2) | ||||
| #define HOT_PLUG_PIN_STATE_LOW				(0x00) | ||||
| #define HOT_PLUG_PIN_STATE_HIGH				(0x04) | ||||
|  | ||||
| #define RECEIVER_SENSE_EVENT_MASK			(BIT_1) | ||||
| #define RECEIVER_SENSE_EVENT_NO				(0x00) | ||||
| #define RECEIVER_SENSE_EVENT_YES			(0x02) | ||||
|  | ||||
| #define HOT_PLUG_EVENT_MASK					(BIT_0) | ||||
| #define HOT_PLUG_EVENT_NO					(0x00) | ||||
| #define HOT_PLUG_EVENT_YES					(0x01) | ||||
|  | ||||
| /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// | ||||
|  | ||||
| // Sync Register Configuration and Sync Monitoring Registers | ||||
| //========================================================== | ||||
|  | ||||
| #define TPI_SYNC_GEN_CTRL					(0x60) | ||||
| #define TPI_SYNC_POLAR_DETECT				(0x61) | ||||
|  | ||||
| // Explicit Sync DE Generator Registers (TPI 0x60[7]=0) | ||||
| //===================================================== | ||||
|  | ||||
| #define TPI_DE_DLY							(0x62) | ||||
| #define TPI_DE_CTRL							(0x63) | ||||
| #define TPI_DE_TOP							(0x64) | ||||
|  | ||||
| #define TPI_RESERVED4						(0x65) | ||||
|  | ||||
| #define TPI_DE_CNT_7_0						(0x66) | ||||
| #define TPI_DE_CNT_11_8						(0x67) | ||||
|  | ||||
| #define TPI_DE_LIN_7_0						(0x68) | ||||
| #define TPI_DE_LIN_10_8						(0x69) | ||||
|  | ||||
| #define TPI_DE_H_RES_7_0					(0x6A) | ||||
| #define TPI_DE_H_RES_10_8					(0x6B) | ||||
|  | ||||
| #define TPI_DE_V_RES_7_0					(0x6C) | ||||
| #define TPI_DE_V_RES_10_8					(0x6D) | ||||
|  | ||||
| // Embedded Sync Register Set (TPI 0x60[7]=1) | ||||
| //=========================================== | ||||
|  | ||||
| #define TPI_HBIT_TO_HSYNC_7_0				(0x62) | ||||
| #define TPI_HBIT_TO_HSYNC_9_8				(0x63) | ||||
| #define TPI_FIELD_2_OFFSET_7_0				(0x64) | ||||
| #define TPI_FIELD_2_OFFSET_11_8				(0x65) | ||||
| #define TPI_HWIDTH_7_0						(0x66) | ||||
| #define TPI_HWIDTH_8_9						(0x67) | ||||
| #define TPI_VBIT_TO_VSYNC					(0x68) | ||||
| #define TPI_VWIDTH							(0x69) | ||||
|  | ||||
| // TPI Enable Register | ||||
| //==================== | ||||
|  | ||||
| #define TPI_ENABLE							(0xC7) | ||||
|  | ||||
| // Misc InfoFrames | ||||
| //================ | ||||
| #define MISC_INFO_FRAMES_CTRL				(0xBF) | ||||
| #define MISC_INFO_FRAMES_TYPE				(0xC0) | ||||
| #define MISC_INFO_FRAMES_VER				(0xC1) | ||||
| #define MISC_INFO_FRAMES_LEN				(0xC2) | ||||
| #define MISC_INFO_FRAMES_CHKSUM				(0xC3) | ||||
|  | ||||
| // Backdoor Register Offsets | ||||
| //========================== | ||||
| #define INTERNAL_PAGE_0         0x00 | ||||
| #define INTERNAL_PAGE_1         0x01 | ||||
| #define DEVICE_ID_LOW_BYTE      0x02 | ||||
| #define DEVICE_ID_HI_BYTE       0x03 | ||||
| #define AUDIO_INPUT_LENGTH		0x24 | ||||
|  | ||||
|  | ||||
| #define SW_RESET                0x05 | ||||
| #define POWER_DOWN              0x6F | ||||
|  | ||||
|  | ||||
| // Backdoor constants | ||||
| //=================== | ||||
|  | ||||
| #define DIV_BY_2                0x00 | ||||
| #define MULT_BY_1               0x01 | ||||
| #define MULT_BY_2               0x02 | ||||
| #define MULT_BY_4               0x03 | ||||
|  | ||||
| #define INTERNAL_PAGE_1         0x01 | ||||
| #define INTERNAL_PAGE_2         0x02 | ||||
| #define TMDS_CONT_REG           0x82 | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										7
									
								
								drivers/video/msm/hdmi/silicon-image/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								drivers/video/msm/hdmi/silicon-image/Makefile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| sil-objs =			\ | ||||
| 	tpi.o			\ | ||||
| 	hdcp.o			\ | ||||
| 	av_config.o		\ | ||||
| 	debug-sil902x.o | ||||
|  | ||||
| obj-$(CONFIG_MSM_HDMI) += sil.o | ||||
							
								
								
									
										365
									
								
								drivers/video/msm/hdmi/silicon-image/av_config.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										365
									
								
								drivers/video/msm/hdmi/silicon-image/av_config.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,365 @@ | ||||
| /* | ||||
|  * Copyright (C) 2009 HTC | ||||
|  * | ||||
|  * This software is licensed under the terms of the GNU General Public | ||||
|  * License version 2, as published by the Free Software Foundation, and | ||||
|  * may be copied, distributed, and modified under those terms. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  */ | ||||
|  | ||||
| /* TODO: | ||||
|  * 1. Mutex while active | ||||
|  */ | ||||
| #include <linux/delay.h> | ||||
| #include <mach/msm_fb.h> | ||||
| #include "../include/fb-hdmi.h" | ||||
| #include "../include/sil902x.h" | ||||
| #include "../include/tpi.h" | ||||
|  | ||||
| #if 1 | ||||
| #define HDMI_DBG(s...) printk("[hdmi/avc]" s) | ||||
| #else | ||||
| #define HDMI_DBG(s...) do {} while (0) | ||||
| #endif | ||||
|  | ||||
| static u8 video_param[][8] = { | ||||
| 	[hd_720p] = {0x01, 0x1d, 0x70, 0x17, 0x72, 0x06, 0xee, 0x02}, | ||||
| 	[svga] = {0xa0, 0x0f, 0x70, 0x17, 0x20, 0x04, 0x74, 0x02}, | ||||
| 	[pal] = {0x8c, 0x0a, 0x88, 0x13, 0x60, 0x03, 0x71, 0x02}, /* 576p50 */ | ||||
| 	[edtv] = {0x8c, 0x0a, 0x70, 0x17, 0x5a, 0x03, 0x0d, 0x02},/* 480p60 */ | ||||
| 	[vga] = {0x8c, 0x0a, 0x70, 0x17, 0x20, 0x03, 0x0d, 0x02}, | ||||
| }; | ||||
|  | ||||
| #if 0 | ||||
| static u8 avi_info_frame[][14] = { | ||||
| 	[hd_720p] = {0x32, 0x0d, 0xa8, 0x84, 0x04, 0x00, 0x00, 0x00, 0x00, | ||||
| 		0x00, 0x00, 0x00, 0x00, 0x00}, | ||||
| 	[svga] = {0xd1, 0x0e, 0x08, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||
| 		0x00, 0x00, 0x00, 0x00}, | ||||
| 	[pal] = {0x64, 0x0d, 0x68, 0x84, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||
| 		0x00, 0x00, 0x00, 0x00}, /* 576p50 */ | ||||
| 	[edtv] = {0x73, 0x0d, 0x68, 0x84, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||
| 		0x00, 0x00, 0x00, 0x00}, | ||||
| 	[vga] = {0x5E, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||
| 		0x00, 0x00, 0x00, 0x00}, | ||||
| }; | ||||
| #else | ||||
| static u8 avi_info_frame[][14] = { | ||||
| 	[hd_720p] = {0x43, 0x00, 0x28, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, | ||||
| 		     0x00, 0x00, 0x00, 0x00, 0x00}, | ||||
| 	[svga] = {0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||
| 		  0x00, 0x00, 0x00, 0x00}, | ||||
| 	[pal] = {0x46, 0x00, 0x18, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||
| 		 0x00, 0x00, 0x00, 0x00}, /* 576p50 */ | ||||
| 	[edtv] = {0x55, 0x00, 0x18, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||
| 		  0x00, 0x00, 0x00, 0x00}, | ||||
| 	[vga] = {0x5E, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||
| 		 0x00, 0x00, 0x00, 0x00}, | ||||
| }; | ||||
| #endif | ||||
|  | ||||
| static u8 audio_info_frame[] = | ||||
| 	{ 0xc2, 0x84, 0x01, 0x0a, 0x71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; | ||||
|  | ||||
| #if 0 | ||||
| int hdmi_active9022(struct i2c_client *client) | ||||
| { | ||||
| 	int i, ret = -EIO; | ||||
| 	struct hdmi_info *info = i2c_get_clientdata(client); | ||||
| 	u8 *video_parm = &video_param[info->res][0]; | ||||
|  | ||||
| 	HDMI_DBG("%s+\n", __func__); | ||||
|         mutex_lock(&info->polling_lock); | ||||
| 	mutex_lock(&info->lock); | ||||
|  | ||||
| 	//hdcp_off(info); | ||||
| 	DisableTMDS(info); | ||||
| 	msleep(128); | ||||
|  | ||||
|         /* choose video mode */ | ||||
| 	ret = i2c_smbus_write_i2c_block_data(client, 0, 8, video_parm); | ||||
|  | ||||
| 	/* wakeup Sil9022 to D0 state */ | ||||
| 	ret = hdmi_write_byte(client, HDMI_POWER, 0); | ||||
|  | ||||
| 	if (edid_check_sink_type(info)) { | ||||
| 		/* HDMI Output */ | ||||
| 		ReadModifyWriteTPI(info, TPI_SYSTEM_CONTROL, | ||||
| 			OUTPUT_MODE_MASK, OUTPUT_MODE_HDMI); | ||||
| 		/* audio configuration */ | ||||
| 		ret = hdmi_write_byte(client, 0x26, 0x91); | ||||
| 		ret = hdmi_write_byte(client, 0x25, 0x03); | ||||
| 		ret = hdmi_write_byte(client, 0x27, 0x59); | ||||
| 		ret = hdmi_write_byte(client, 0x28, 0x00); | ||||
| 		ret = hdmi_write_byte(client, 0x1f, 0x80); | ||||
| 		ret = hdmi_write_byte(client, 0x20, 0x90); | ||||
| 		ret = hdmi_write_byte(client, 0x21, 0x00); | ||||
| 		//ret = hdmi_write_byte(client, 0x24, 0x00);//0x00 for 44.1k | ||||
| 		ret = hdmi_write_byte(client, 0x24, 0x02);//0x02 for 48k | ||||
| 		ret = hdmi_write_byte(client, 0x25, 0x00); | ||||
| 		ret = hdmi_write_byte(client, 0xbc, 0x02); | ||||
| 		ret = hdmi_write_byte(client, 0xbd, 0x24); | ||||
| 		ret = hdmi_write_byte(client, 0xbe, 0x92); | ||||
| 		ret = hdmi_write_byte(client, 0xbc, 0x02); | ||||
| 		ret = hdmi_write_byte(client, 0xbd, 0x2f); | ||||
| 		ret = hdmi_write_byte(client, 0xbe, 0); | ||||
| 		ret = hdmi_write_byte(client, 0x26, 0x81); | ||||
| 	} else { | ||||
| 		ReadModifyWriteTPI(info, TPI_SYSTEM_CONTROL, | ||||
| 			OUTPUT_MODE_MASK, OUTPUT_MODE_DVI); | ||||
| 		SetAudioMute(info, AUDIO_MUTE_MUTED); | ||||
| 	} | ||||
| 	ret = hdmi_write_byte(client, HDMI_PIXEL_DATA, 0x60); | ||||
|  | ||||
| 	ret = i2c_smbus_write_i2c_block_data(client, HDMI_AUDIO_INFO_FRAME, | ||||
| 			15, audio_info_frame); | ||||
| 	hdmi_write_byte(client, 0x09, 0); | ||||
| 	hdmi_write_byte(client, 0x0a, 0); | ||||
| 	for (i = 0; i < 14 ;i++) | ||||
| 		hdmi_write_byte(client, 0xc + i, avi_info_frame[info->res][i]); | ||||
| 	 | ||||
| 	EnableTMDS(info); | ||||
| 	HDMI_DBG("%s-\n", __func__); | ||||
| 	mutex_unlock(&info->lock); | ||||
| 	mutex_unlock(&info->polling_lock); | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
| #endif | ||||
|  | ||||
| int avc_set_video_parm(struct hdmi_info *hdmi) | ||||
| { | ||||
| 	int ret; | ||||
| 	u8 *video_parm = video_param[hdmi->res]; | ||||
|  | ||||
| 	ret = i2c_smbus_write_i2c_block_data(hdmi->client, 0, 8, video_parm); | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| int avc_set_blank_screen(struct hdmi_info *hdmi) | ||||
| { | ||||
| 	HDMI_DBG("%s+\n", __func__); | ||||
| 	hdmi_write_byte(hdmi->client, 0x09, 0x03); | ||||
| 	hdmi_write_byte(hdmi->client, 0x19, 0x00); | ||||
| 	hdmi_write_byte(hdmi->client, 0x26, | ||||
| 		hdmi_read(hdmi->client, 0x26) | 0x10); | ||||
| } | ||||
|  | ||||
| int hdmi_active9022_dup(struct i2c_client *client) | ||||
| { | ||||
| 	int i, ret = -EIO; | ||||
| 	struct hdmi_info *info = i2c_get_clientdata(client); | ||||
| 	u8 *video_parm = &video_param[info->res][0]; | ||||
|  | ||||
| 	HDMI_DBG("%s+\n", __func__); | ||||
|  | ||||
| 	//hdcp_off(info); | ||||
| 	DisableTMDS(info); | ||||
| 	msleep(128); | ||||
|  | ||||
| 	/* choose video mode */ | ||||
| 	ret = i2c_smbus_write_i2c_block_data(client, 0, 8, video_parm); | ||||
|  | ||||
| 	/* wakeup Sil9022 to D0 state */ | ||||
| 	ret = hdmi_write_byte(client, HDMI_POWER, 0); | ||||
|  | ||||
| 	if (edid_check_sink_type(info)) { | ||||
| 		/* HDMI Output */ | ||||
| 		ReadModifyWriteTPI(info, TPI_SYSTEM_CONTROL, | ||||
| 			OUTPUT_MODE_MASK, OUTPUT_MODE_HDMI); | ||||
| 		/* audio configuration */ | ||||
| 		ret = hdmi_write_byte(client, 0x26, 0x91); | ||||
| 		ret = hdmi_write_byte(client, 0x25, 0x03); | ||||
| 		ret = hdmi_write_byte(client, 0x27, 0x59); | ||||
| 		ret = hdmi_write_byte(client, 0x28, 0x00); | ||||
| 		ret = hdmi_write_byte(client, 0x1f, 0x80); | ||||
| 		ret = hdmi_write_byte(client, 0x20, 0x90); | ||||
| 		ret = hdmi_write_byte(client, 0x21, 0x00); | ||||
| 		//ret = hdmi_write_byte(client, 0x24, 0x00);//0x00 for 44.1k | ||||
| 		ret = hdmi_write_byte(client, 0x24, 0x02);//0x02 for 48k | ||||
| 		ret = hdmi_write_byte(client, 0x25, 0x00); | ||||
| 		ret = hdmi_write_byte(client, 0xbc, 0x02); | ||||
| 		ret = hdmi_write_byte(client, 0xbd, 0x24); | ||||
| 		ret = hdmi_write_byte(client, 0xbe, 0x92); | ||||
| 		ret = hdmi_write_byte(client, 0xbc, 0x02); | ||||
| 		ret = hdmi_write_byte(client, 0xbd, 0x2f); | ||||
| 		ret = hdmi_write_byte(client, 0xbe, 0); | ||||
| 		ret = hdmi_write_byte(client, 0x26, 0x81); | ||||
| 	} else { | ||||
| 		ReadModifyWriteTPI(info, TPI_SYSTEM_CONTROL, | ||||
| 			OUTPUT_MODE_MASK, OUTPUT_MODE_DVI); | ||||
| 		SetAudioMute(info, AUDIO_MUTE_MUTED); | ||||
| 	} | ||||
| 	ret = hdmi_write_byte(client, HDMI_PIXEL_DATA, 0x60); | ||||
|  | ||||
| 	ret = i2c_smbus_write_i2c_block_data(client, HDMI_AUDIO_INFO_FRAME, | ||||
| 			15, audio_info_frame); | ||||
| 	hdmi_write_byte(client, 0x09, 0x03); | ||||
| 	hdmi_write_byte(client, 0x0a, 0); | ||||
| 	for (i = 0; i < 14 ;i++) | ||||
| 		hdmi_write_byte(client, 0xc + i, avi_info_frame[info->res][i]); | ||||
|  | ||||
| 	EnableTMDS(info); | ||||
|  | ||||
| 	HDMI_DBG("%s-\n", __func__); | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| bool avc_send_avi_info_frames(struct hdmi_info *hdmi) | ||||
| { | ||||
|         int i; | ||||
|  | ||||
| 	HDMI_DBG("%s res=%d\n", __func__, hdmi->res); | ||||
| 	for (i = 0; i < 14 ;i++) | ||||
| 		hdmi_write_byte(hdmi->client, 0xc + i, | ||||
| 				avi_info_frame[hdmi->res][i]); | ||||
|  | ||||
|         return true; | ||||
| } | ||||
|  | ||||
| #if 0 | ||||
| /* FIXME: intergrate with active9022 */ | ||||
| bool InitVideo(struct hdmi_info *hdmi, u8 Mode, u8 TclkSel, bool Init) | ||||
| { | ||||
| 	int Pattern, ret; | ||||
| 	u8 *video_parm = &video_param[hdmi->res][0]; | ||||
|  | ||||
| 	/* Use TPI 0x08[7:6] for 9022A/24A video clock multiplier */ | ||||
| 	Pattern = (1 << 6) & 0xc0; | ||||
| 	ReadSetWriteTPI(hdmi, TPI_PIX_REPETITION, Pattern); | ||||
|  | ||||
| 	ret = i2c_smbus_write_block_data(hdmi->client, 0, 8, video_parm); | ||||
|  | ||||
| 	/* input format */ | ||||
| 	hdmi_write_byte(hdmi->client, 0x09, 0); | ||||
| 	hdmi_write_byte(hdmi->client, 0x0a, 0); | ||||
| 	if(Init) { | ||||
| 		hdmi_write_byte(hdmi->client, 0x08, 0x60); | ||||
| 		hdmi_write_byte(hdmi->client, 0x09, 0); | ||||
| 		hdmi_write_byte(hdmi->client, 0x0a, 0); | ||||
| 		hdmi_write_byte(hdmi->client, 0x60, 0x4); | ||||
| 	} | ||||
|  | ||||
| 	// termination ??? | ||||
| 	//ret = (read_back_door_register(0x1, 0x82) & 0x3f ) | 0x25; | ||||
| 	//write_back_door_register(); | ||||
| #if 0 | ||||
| 	for (i = 0; i < 14 ;i++) { | ||||
| 		hdmi_write_byte(hdmi->client, 0xc + i, avi_info_frame[info->res][i]); | ||||
| 	} | ||||
| #endif | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
| #endif | ||||
|  | ||||
| bool avc_init_video(struct hdmi_info *hdmi, u8 mode, u8 TclkSel, bool Init) | ||||
| { | ||||
|         int ret; | ||||
|         u8 *video_parm = &video_param[hdmi->res][0]; | ||||
|  | ||||
| 	HDMI_DBG("%s\n", __func__); | ||||
|         /* Use TPI 0x08[7:6] for 9022A/24A video clock multiplier */ | ||||
| 	hdmi_write_byte(hdmi->client, HDMI_PIXEL_DATA, 0x60); | ||||
|  | ||||
|         ret = i2c_smbus_write_i2c_block_data(hdmi->client, 0, 8, video_parm); | ||||
|  | ||||
|         /* input format */ | ||||
|         hdmi_write_byte(hdmi->client, TPI_INPUT_FORMAT_REG, 0x00); | ||||
|         hdmi_write_byte(hdmi->client, TPI_OUTPUT_FORMAT_REG, 0x00); | ||||
| #if 0 | ||||
|         if (Init) { | ||||
|                 hdmi_write_byte(hdmi->client, 0x08, 0x60); | ||||
|                 hdmi_write_byte(hdmi->client, 0x09, 0); | ||||
|         	hdmi_write_byte(hdmi->client, 0x0a, 0); | ||||
| 		/* Default to External Sync mode + disable VSync adjustment */ | ||||
|                 hdmi_write_byte(hdmi->client, 0x60, 0x4); | ||||
| 		/* Disable DE generator by default */ | ||||
|                 hdmi_write_byte(hdmi->client, 0x63, 0x0); | ||||
|         } | ||||
|  | ||||
| #endif | ||||
|         ret = (tpi_read_backdoor_register(hdmi, INTERNAL_PAGE_1, TMDS_CONT_REG) | ||||
| 		& 0x3f ) | 0x25; | ||||
|         tpi_write_backdoor_register(hdmi, INTERNAL_PAGE_1, TMDS_CONT_REG, ret); | ||||
|  | ||||
|         return true; | ||||
| } | ||||
|  | ||||
| void avc_set_basic_audio(struct hdmi_info *hdmi) | ||||
| { | ||||
| 	int ret; | ||||
| 	struct i2c_client *client = hdmi->client; | ||||
| 	HDMI_DBG("%s\n", __func__); | ||||
|  | ||||
|         ret = hdmi_write_byte(client, 0x26, 0x91); | ||||
|         ret = hdmi_write_byte(client, 0x25, 0x03); | ||||
|         ret = hdmi_write_byte(client, 0x27, 0x59); | ||||
|         ret = hdmi_write_byte(client, 0x28, 0x00); | ||||
|         ret = hdmi_write_byte(client, 0x1f, 0x80); | ||||
|         ret = hdmi_write_byte(client, 0x20, 0x90); | ||||
|         ret = hdmi_write_byte(client, 0x21, 0x00); | ||||
|         //ret = hdmi_write_byte(client, 0x24, 0x00);//0x00 for 44.1k | ||||
|         ret = hdmi_write_byte(client, 0x24, 0x02);//0x02 for 48k | ||||
|         ret = hdmi_write_byte(client, 0x25, 0x00); | ||||
|         ret = hdmi_write_byte(client, 0xbc, 0x02); | ||||
|         ret = hdmi_write_byte(client, 0xbd, 0x24); | ||||
|         ret = hdmi_write_byte(client, 0xbe, 0x92); | ||||
|         ret = hdmi_write_byte(client, 0xbc, 0x02); | ||||
|         ret = hdmi_write_byte(client, 0xbd, 0x2f); | ||||
|         ret = hdmi_write_byte(client, 0xbe, 0); | ||||
|         ret = hdmi_write_byte(client, 0x26, 0x81); | ||||
|  | ||||
| 	ret = i2c_smbus_write_i2c_block_data(client, HDMI_AUDIO_INFO_FRAME, | ||||
| 			15, audio_info_frame); | ||||
| } | ||||
|  | ||||
| /* simplifier version of ChangeVideoMode() */ | ||||
| u8 avc_change_video_mode(struct hdmi_info *hdmi, int *resolution) | ||||
| { | ||||
| 	HDMI_DBG("%s\n", __func__); | ||||
|  | ||||
| 	hdcp_off(hdmi); | ||||
| 	DisableTMDS(hdmi); | ||||
| 	/* allow control InfoFrames to pass through to the sink device. */ | ||||
| 	mdelay(128); | ||||
|  | ||||
| 	// FIXME: video mode | ||||
| 	avc_init_video(hdmi, vid_mode, 0, 0); | ||||
|  | ||||
| 	hdmi_write_byte(hdmi->client, TPI_PIX_REPETITION, 0x60); | ||||
| 	hdmi_write_byte(hdmi->client, TPI_INPUT_FORMAT, 0); | ||||
|  | ||||
| 	if (edid_check_sink_type(hdmi)) | ||||
| 		ReadSetWriteTPI(hdmi, 0x1a, 0x01); | ||||
| 	else | ||||
| 		ReadSetWriteTPI(hdmi, 0x1a, 0x00); | ||||
|  | ||||
| 	/* FIXME: 720p/480p ?*/ | ||||
| 	hdmi_write_byte(hdmi->client, TPI_OUTPUT_FORMAT, 0); | ||||
|  | ||||
| 	/* set 0x60[7] = 0 for External Sync */ | ||||
| 	ReadClearWriteTPI(hdmi, TPI_SYNC_GEN_CTRL, 0x80); | ||||
| 	/* clear 0x63[6] = 0 to disable internal DE */ | ||||
| 	ReadClearWriteTPI(hdmi, 0x63, 1 << 6); | ||||
|  | ||||
| 	/* InfoFrames - only if output mode is HDMI */ | ||||
| 	if (edid_check_sink_type(hdmi)) | ||||
| 		avc_send_avi_info_frames(hdmi); | ||||
|  | ||||
| 	/* SETTING UP AVI InfoFrames CLEARS 0x63 and 0x60[5] */ | ||||
| 	hdmi_write_byte(hdmi->client, TPI_SYNC_GEN_CTRL, 1 << 2); | ||||
|         /* SETTING UP AVI InfoFrames CLEARS 0x63 */ | ||||
| 	hdmi_write_byte(hdmi->client, 0x63, 0); | ||||
|  | ||||
| 	/* YC Input Mode Select */ | ||||
| 	hdmi_write_byte(hdmi->client, 0x0b, 0);	// 0x0b | ||||
| 	EnableTMDS(hdmi); | ||||
|  | ||||
| 	return VIDEO_MODE_SET_OK; | ||||
| } | ||||
							
								
								
									
										109
									
								
								drivers/video/msm/hdmi/silicon-image/debug-sil902x.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								drivers/video/msm/hdmi/silicon-image/debug-sil902x.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,109 @@ | ||||
| #include <linux/delay.h> | ||||
| #include <linux/debugfs.h> | ||||
|  | ||||
| #include <mach/msm_fb.h> | ||||
| #include <mach/msm_hdmi.h> | ||||
|  | ||||
| #include "../include/fb-hdmi.h" | ||||
| #include "../include/sil902x.h" | ||||
|  | ||||
| //#define HDMI_DEBUGFS | ||||
|  | ||||
| #if defined(HDMI_DEBUGFS) | ||||
| static spinlock_t hdmi_dbgfs_lock; | ||||
| ssize_t hdmi_dbgfs_open(struct inode *inode, struct file *file) | ||||
| { | ||||
| 	file->private_data = inode->i_private; | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static ssize_t hdmi_dbgfs_read(struct file *filp, char __user *buf, | ||||
| 		size_t count, loff_t *ppos) | ||||
| { | ||||
| 	static char line[80], buffer[80*8*4]; | ||||
| 	static char hextab[] = "0123456789abcdefg"; | ||||
| 	int i, j, n = 0, v, len, offset, line_size; | ||||
| 	unsigned long irq_flags; | ||||
| 	struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; | ||||
|  | ||||
| 	len = ((int)hdmi->edid_buf[0x7e]+1) * 128; | ||||
| 	spin_lock_irqsave(&hdmi_dbgfs_lock, irq_flags); | ||||
| 	memset(line, ' ', 79); | ||||
| 	line[79] = '\0'; | ||||
| 	offset = strlen("0000 | "); | ||||
| 	line_size = offset + 3 * 16 + 1; | ||||
|  | ||||
| 	for (i = 0; i < len / 16 ; i++) { | ||||
| 		scnprintf(line, offset + 1, "%04x | ", (i << 4)); | ||||
| 		for (j = 0; j < 16 ; j++) { | ||||
| 			v = hdmi->edid_buf[i * 16 + j]; | ||||
| 			line[offset + j * 3] = hextab[v / 16]; | ||||
| 			line[offset + j * 3 + 1] = hextab[v % 16]; | ||||
| 		} | ||||
| 		line[line_size - 1] = '\n'; | ||||
| 		strncpy(buffer + i * line_size, line, line_size); | ||||
| 		n += line_size; | ||||
| 	} | ||||
| 	spin_unlock_irqrestore(&hdmi_dbgfs_lock, irq_flags); | ||||
| 	return simple_read_from_buffer(buf, count, ppos, buffer, n); | ||||
| } | ||||
|  | ||||
| #if 0 | ||||
| static ssize_t hdmi_dbgfs_write(struct file *filp, const char __user *buf, | ||||
|                 size_t count, loff_t *ppos) | ||||
| { | ||||
|         unsigned long v; | ||||
|         unsigned long irq_flags; | ||||
|         char buff[80]; | ||||
|         struct tv_reg_data *trd = (struct tv_reg_data *)filp->private_data; | ||||
|  | ||||
|         if (count >= sizeof(buff)) | ||||
|                 return -EINVAL; | ||||
|         if (copy_from_user(&buff, buf, 80)) | ||||
|                 return -EFAULT; | ||||
|         buff[count] = 0; | ||||
|  | ||||
| #if 0 | ||||
|         spin_lock_irqsave(&hdmi_dbgfs_lock, irq_flags); | ||||
|         strict_strtoul(buff, 16, &v); | ||||
|         buff[strlen(buff)]=0; | ||||
|         writel(v, tvenc_base+trd->offset); | ||||
|         spin_unlock_irqrestore(&hdmi_dbgfs_lock, irq_flags); | ||||
| #endif | ||||
|  | ||||
|         return count; | ||||
| } | ||||
| #endif | ||||
|  | ||||
| static struct file_operations hdmi_fops[] = { | ||||
| 	{ | ||||
| 		.open  = hdmi_dbgfs_open, | ||||
| 		.read  = hdmi_dbgfs_read, | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| int hdmi_debugfs_init(struct hdmi_info *hdmi) | ||||
| { | ||||
| 	HDMI_DBG("%s\n", __func__); | ||||
| 	spin_lock_init(&hdmi_dbgfs_lock); | ||||
| 	hdmi->debug_dir = debugfs_create_dir(HDMI_DEBUGFS_ROOT, 0); | ||||
| 	if (IS_ERR(hdmi->debug_dir)) | ||||
| 		return PTR_ERR(hdmi->debug_dir); | ||||
|  | ||||
| 	// FIXME: error handling | ||||
| 	debugfs_create_file("dummy", 0644, hdmi->debug_dir, hdmi, | ||||
| 		&hdmi_fops[0]); | ||||
| 	edid_debugfs_init(hdmi); | ||||
| 	tpi_debugfs_init(hdmi); | ||||
| 	hdcp_debugfs_init(hdmi); | ||||
| /* | ||||
| 	int ret; | ||||
| 	if (!ret) { | ||||
| 		pr_err("%s: failure on debugfs_create_file()\n", __func__); | ||||
| 		return -1; | ||||
| 	} | ||||
| */ | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
| #endif | ||||
							
								
								
									
										354
									
								
								drivers/video/msm/hdmi/silicon-image/hdcp.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										354
									
								
								drivers/video/msm/hdmi/silicon-image/hdcp.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,354 @@ | ||||
| #include <linux/delay.h> | ||||
| #include <linux/debugfs.h> | ||||
| #include <mach/msm_fb.h> | ||||
| #include <mach/msm_hdmi.h> | ||||
|  | ||||
| #include "../include/fb-hdmi.h" | ||||
| #include "../include/sil902x.h" | ||||
| #include "../include/tpi.h" | ||||
|  | ||||
| #if 1 | ||||
| #define HDCP_DBG(fmt, arg...) printk( "[hdmi/hdcp]%s: " fmt, __func__, ##arg) | ||||
| #else | ||||
| #define HDCP_DBG(fmt...) do {} while (0) | ||||
| #endif | ||||
|  | ||||
| #define hdcp_err(fmt, arg...) pr_err( "[hdmi/hdcp]%s: " fmt, __func__, ##arg) | ||||
|  | ||||
| //#define HDCP_DEBUG              /* Remove this definition when releasing!!! */ | ||||
| #if defined(HDCP_DEBUG) | ||||
| #define HDCP_OVERWRITE_CONTROL          0x1 | ||||
| #define HDCP_SUPPORT                    0x2 | ||||
| #define HDCP_AVALABLE                   0x4 | ||||
| static int hdcp_control = 0x7; | ||||
| module_param_named(hdcp_control, hdcp_control, int, | ||||
|                    S_IRUGO | S_IWUSR | S_IWGRP); | ||||
| #endif | ||||
|  | ||||
| bool HDCP_TxSupports; | ||||
| bool HDCP_Started; | ||||
| u8 HDCP_LinkProtectionLevel; | ||||
|  | ||||
| ////////////////////////////////////////////////////////////////////////////// | ||||
| // FUNCTION     :   IsHDCP_Supported() | ||||
| // PURPOSE      :   Check Tx revision number to find if this Tx supports HDCP | ||||
| //                  by reading the HDCP revision number from TPI register 0x30. | ||||
| // RETURNS      :   true if Tx supports HDCP. false if not. | ||||
| ////////////////////////////////////////////////////////////////////////////// | ||||
| bool hdcp_check_support(struct hdmi_info *hdmi) | ||||
| { | ||||
| 	u8 HDCP_Rev; | ||||
| 	bool HDCP_Supported; | ||||
|  | ||||
| 	HDCP_Supported = true; | ||||
| 	/* Check Device ID */ | ||||
| 	HDCP_Rev = hdmi_read(hdmi->client, TPI_HDCP_REVISION_DATA_REG); | ||||
| 	if (HDCP_Rev != | ||||
| 		(HDCP_MAJOR_REVISION_VALUE | HDCP_MINOR_REVISION_VALUE)) | ||||
| 		HDCP_Supported = false; | ||||
|  | ||||
| 	HDCP_DBG("ret=%d\n", HDCP_Supported); | ||||
| 	return HDCP_Supported; | ||||
| } | ||||
|  | ||||
| ////////////////////////////////////////////////////////////////////////////// | ||||
| // FUNCTION      :  AreAKSV_OK() | ||||
| // PURPOSE       :  Check if AKSVs contain 20 '0' and 20 '1' | ||||
| // INPUT PARAMS  :  None | ||||
| // OUTPUT PARAMS :  None | ||||
| // GLOBALS USED  :  TBD | ||||
| // RETURNS       :  true if 20 zeros and 20 ones found in AKSV. false OTHERWISE | ||||
| ////////////////////////////////////////////////////////////////////////////// | ||||
| static bool hdcp_check_aksv(struct hdmi_info *hdmi) | ||||
| { | ||||
| 	int ret; | ||||
| 	u8 B_Data[AKSV_SIZE]; | ||||
| 	u8 i, j, NumOfOnes = 0; | ||||
|  | ||||
| 	memset(B_Data, 0, AKSV_SIZE); | ||||
| #if 0 | ||||
| 	ReadBlockTPI(hdmi, TPI_AKSV_1_REG, AKSV_SIZE, B_Data); | ||||
| #else | ||||
| 	for (i = 0; i < 5; i++) { | ||||
| 		B_Data[i] = hdmi_read(hdmi->client, TPI_AKSV_1_REG+i); | ||||
| 	} | ||||
| #endif | ||||
| 	HDCP_DBG(" askv={%02x, %02x, %02x, %02x, %02x}\n", | ||||
| 		B_Data[0], B_Data[1], B_Data[2], B_Data[3], B_Data[4]); | ||||
| 	for (i=0; i < AKSV_SIZE; i++) | ||||
| 		for (j=0; j < BYTE_SIZE; j++) { | ||||
| 			if (B_Data[i] & 0x01) | ||||
| 				NumOfOnes++; | ||||
| 			B_Data[i] >>= 1; | ||||
| 		} | ||||
| 	if (NumOfOnes != NUM_OF_ONES_IN_KSV) | ||||
| 		ret = false; | ||||
| 	else ret = true; | ||||
|  | ||||
| 	HDCP_DBG(":ret=%s\n", ret ? "true" : "false"); | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| ////////////////////////////////////////////////////////////////////////////// | ||||
| // FUNCTION     :   HDCP_On() | ||||
| // PURPOSE      :   Switch hdcp on. | ||||
| // INPUT PARAMS :   None | ||||
| // OUTPUT PARAMS:   None | ||||
| // GLOBALS USED :   HDCP_Started set to true | ||||
| // RETURNS      :   None | ||||
| ////////////////////////////////////////////////////////////////////////////// | ||||
| //void hdcp_on(struct hdmi_info *hdmi) | ||||
| void hdcp_on(struct hdmi_info *hdmi, const char *caller) | ||||
| { | ||||
| 	HDCP_DBG(", caller=%s\n", caller); | ||||
| 	hdmi_write_byte(hdmi->client, TPI_HDCP_CONTROL_DATA_REG, PROTECTION_LEVEL_MAX); | ||||
| 	HDCP_Started = true; | ||||
| } | ||||
|  | ||||
| ////////////////////////////////////////////////////////////////////////////// | ||||
| // FUNCTION     :   HDCP_Off() | ||||
| // PURPOSE      :   Switch hdcp off. | ||||
| // GLOBALS USED :   HDCP_Started set to false | ||||
| // RETURNS      :   None | ||||
| ////////////////////////////////////////////////////////////////////////////// | ||||
| void hdcp_off(struct hdmi_info *hdmi) | ||||
| { | ||||
| 	HDCP_DBG("\n"); | ||||
|  | ||||
| 	SetInputColorSpace(hdmi, INPUT_COLOR_SPACE_BLACK_MODE); | ||||
| 	SetAudioMute(hdmi, AUDIO_MUTE_MUTED); | ||||
| 	hdmi_write_byte(hdmi->client, TPI_HDCP_CONTROL_DATA_REG, PROTECTION_LEVEL_MIN); | ||||
| 	HDCP_Started = false; | ||||
| 	HDCP_LinkProtectionLevel = EXTENDED_LINK_PROTECTION_NONE | LOCAL_LINK_PROTECTION_NONE; | ||||
| } | ||||
|  | ||||
| void hdcp_init(struct hdmi_info *hdmi) | ||||
| { | ||||
| 	HDCP_DBG("\n"); | ||||
|  | ||||
| 	HDCP_TxSupports = false; | ||||
| 	HDCP_Started = false; | ||||
| 	HDCP_LinkProtectionLevel = EXTENDED_LINK_PROTECTION_NONE | LOCAL_LINK_PROTECTION_NONE; | ||||
|  | ||||
| 	/* TX-related... need only be done once. */ | ||||
| 	if (!hdcp_check_support(hdmi)) { | ||||
| 		hdcp_err("TX does not support HDCP\n"); | ||||
| 		return; | ||||
| 	} | ||||
| 	if (!hdcp_check_aksv(hdmi)) { | ||||
| 		hdcp_err("Illegal AKSV\n"); | ||||
| 		return; | ||||
| 	} | ||||
| 	HDCP_TxSupports = true; | ||||
| } | ||||
|  | ||||
| void hdcp_restart(struct hdmi_info *hdmi) | ||||
| { | ||||
| 	HDCP_DBG("\n"); | ||||
| 	DisableTMDS(hdmi); | ||||
| 	hdcp_off(hdmi); | ||||
| 	EnableTMDS(hdmi); | ||||
| } | ||||
|  | ||||
| void hdcp_check_status(struct hdmi_info *hdmi, u8 InterruptStatusImage) | ||||
| { | ||||
| 	u8 QueryData, LinkStatus, RegImage, NewLinkProtectionLevel; | ||||
|  | ||||
| 	if (HDCP_TxSupports == false) | ||||
| 		return; | ||||
|  | ||||
| 	if ((HDCP_LinkProtectionLevel == | ||||
| 		(EXTENDED_LINK_PROTECTION_NONE | LOCAL_LINK_PROTECTION_NONE)) && | ||||
| 		(HDCP_Started == false)) { | ||||
| 			QueryData = hdmi_read(hdmi->client, TPI_HDCP_QUERY_DATA_REG); | ||||
| 		/* Is HDCP avaialable */ | ||||
| 		if (QueryData & PROTECTION_TYPE_MASK) { | ||||
| 			hdcp_on(hdmi, __func__); | ||||
| 		} | ||||
| 	} | ||||
| 	/* Check if Link Status has changed: */ | ||||
| 	if (InterruptStatusImage & SECURITY_CHANGE_EVENT) { | ||||
| 		HDCP_DBG("SECURITY_CHANGE_EVENT\n"); | ||||
| 		LinkStatus = hdmi_read(hdmi->client, TPI_HDCP_QUERY_DATA_REG); | ||||
| 		LinkStatus &= LINK_STATUS_MASK; | ||||
| 		tpi_clear_interrupt(hdmi, SECURITY_CHANGE_EVENT); | ||||
| 		switch (LinkStatus) { | ||||
| 		case LINK_STATUS_NORMAL: | ||||
| 			HDCP_DBG("Link = Normal\n"); | ||||
| 			break; | ||||
| 		case LINK_STATUS_LINK_LOST: | ||||
| 			HDCP_DBG("Link = Lost\n"); | ||||
| 			hdcp_restart(hdmi); | ||||
| 			break; | ||||
| 		case LINK_STATUS_RENEGOTIATION_REQ: | ||||
| 			HDCP_DBG("Link = Renegotiation Required\n"); | ||||
| 			hdcp_off(hdmi); | ||||
| 			hdcp_on(hdmi, __func__); | ||||
| 			break; | ||||
| 		case LINK_STATUS_LINK_SUSPENDED: | ||||
| 			HDCP_DBG("Link = Suspended\n"); | ||||
| 			hdcp_on(hdmi, __func__); | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	/* Check if HDCP state has changed: */ | ||||
| 	if (InterruptStatusImage & HDCP_CHANGE_EVENT) { | ||||
| 		HDCP_DBG("HDCP_CHANGE_EVENT\n"); | ||||
| 		RegImage = hdmi_read(hdmi->client, TPI_HDCP_QUERY_DATA_REG); | ||||
| 		NewLinkProtectionLevel = RegImage & | ||||
| 			(EXTENDED_LINK_PROTECTION_MASK | LOCAL_LINK_PROTECTION_MASK); | ||||
| 		if (NewLinkProtectionLevel != HDCP_LinkProtectionLevel) { | ||||
| 			HDCP_LinkProtectionLevel = NewLinkProtectionLevel; | ||||
| 			switch (HDCP_LinkProtectionLevel) { | ||||
| 			case (EXTENDED_LINK_PROTECTION_NONE | LOCAL_LINK_PROTECTION_NONE): | ||||
| 				HDCP_DBG("Protection = None\n"); | ||||
| 				hdcp_restart(hdmi); | ||||
| 				break; | ||||
| 			case LOCAL_LINK_PROTECTION_SECURE: | ||||
| 				SetAudioMute(hdmi, AUDIO_MUTE_NORMAL); | ||||
| 				//SetInputColorSpace (hdmi, INPUT_COLOR_SPACE_YCBCR422); | ||||
| 				SetInputColorSpace (hdmi, INPUT_COLOR_SPACE_RGB); | ||||
| 				HDCP_DBG("Protection = Local, Video Unmuted\n"); | ||||
| 				break; | ||||
| 			case (EXTENDED_LINK_PROTECTION_SECURE | LOCAL_LINK_PROTECTION_SECURE): | ||||
| 				HDCP_DBG("Protection = Extended\n"); | ||||
| 				break; | ||||
| 			default: | ||||
| 				HDCP_DBG("Protection = Extended but not Local?\n"); | ||||
| 				hdcp_restart(hdmi); | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 		tpi_clear_interrupt(hdmi, HDCP_CHANGE_EVENT); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /*----------------------------------------------------------------------------*/ | ||||
| #if defined(HDMI_DEBUGFS) | ||||
| static ssize_t hdcp_supported_read(struct file *filp, char __user *buf, | ||||
|                 size_t count, loff_t *ppos) | ||||
| { | ||||
|         int n=0; | ||||
|         char buffer[80]; | ||||
|         //struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; | ||||
|  | ||||
| 	HDMI_DBG("%s\n", __func__); | ||||
|         //n = scnprintf(buffer, 80, "%d\n", is_hdcp_supported(hdmi)); | ||||
|         n++; | ||||
|         buffer[n] = 0; | ||||
|         return simple_read_from_buffer(buf, count, ppos, buffer, n); | ||||
| } | ||||
|  | ||||
| static ssize_t hdcp_available_read(struct file *filp, char __user *buf, | ||||
|                 size_t count, loff_t *ppos) | ||||
| { | ||||
|         int n=0; | ||||
|         char buffer[80]; | ||||
|         //struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; | ||||
|  | ||||
| 	HDMI_DBG("%s\n", __func__); | ||||
|         //n = scnprintf(buffer, 80, "%d\n", is_hdcp_available(hdmi)); | ||||
|         n++; | ||||
|         buffer[n] = 0; | ||||
|         return simple_read_from_buffer(buf, count, ppos, buffer, n); | ||||
| } | ||||
|  | ||||
| static ssize_t hdcp_on_read(struct file *filp, char __user *buf, | ||||
|                 size_t count, loff_t *ppos) | ||||
| { | ||||
|         struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; | ||||
| 	hdcp_on(hdmi, __func__); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static ssize_t hdcp_off_read(struct file *filp, char __user *buf, | ||||
|                 size_t count, loff_t *ppos) | ||||
| { | ||||
|         struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; | ||||
|         hdcp_off(hdmi); | ||||
|         return 0; | ||||
| } | ||||
|  | ||||
| static ssize_t hdcp_restart_read(struct file *filp, char __user *buf, | ||||
|                 size_t count, loff_t *ppos) | ||||
| { | ||||
|         struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; | ||||
|         hdcp_restart(hdmi); | ||||
|         return 0; | ||||
| } | ||||
|  | ||||
| static ssize_t hdcp_handle_read(struct file *filp, char __user *buf, | ||||
|                 size_t count, loff_t *ppos) | ||||
| { | ||||
|         //struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; | ||||
|         //handle_hdcp(hdmi); | ||||
|         return 0; | ||||
| } | ||||
|  | ||||
| static ssize_t hdcp_poll_read(struct file *filp, char __user *buf, | ||||
|                 size_t count, loff_t *ppos) | ||||
| { | ||||
|         //struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; | ||||
|         //hdcp_poll(hdmi); | ||||
|         return 0; | ||||
| } | ||||
|  | ||||
| static ssize_t hdcp_aksv_read(struct file *filp, char __user *buf, | ||||
|                 size_t count, loff_t *ppos) | ||||
| { | ||||
|         struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; | ||||
|         hdcp_check_aksv(hdmi); | ||||
|         return 0; | ||||
| } | ||||
|  | ||||
| static ssize_t hdcp_bksv_read(struct file *filp, char __user *buf, | ||||
|                 size_t count, loff_t *ppos) | ||||
| { | ||||
|         //struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; | ||||
|         //hdcp_check_bksv(hdmi); | ||||
|         return 0; | ||||
| } | ||||
|  | ||||
| static struct file_operations hdcp_debugfs_fops[] = { | ||||
|         { .open  = hdmi_dbgfs_open, .read  = hdcp_supported_read, }, | ||||
|         { .open  = hdmi_dbgfs_open, .read  = hdcp_available_read, }, | ||||
|         { .open  = hdmi_dbgfs_open, .read  = hdcp_on_read, }, | ||||
|         { .open  = hdmi_dbgfs_open, .read  = hdcp_off_read, }, | ||||
|         { .open  = hdmi_dbgfs_open, .read  = hdcp_restart_read, }, | ||||
|         { .open  = hdmi_dbgfs_open, .read  = hdcp_poll_read, }, | ||||
|         { .open  = hdmi_dbgfs_open, .read  = hdcp_aksv_read, }, | ||||
|         { .open  = hdmi_dbgfs_open, .read  = hdcp_bksv_read, }, | ||||
| }; | ||||
|  | ||||
| // mutex ? | ||||
| int hdcp_debugfs_init(struct hdmi_info *hdmi) | ||||
| { | ||||
|         struct dentry *hdcp_dent; | ||||
|  | ||||
|         hdcp_dent = debugfs_create_dir("hdcp", hdmi->debug_dir); | ||||
|         if (IS_ERR(hdcp_dent)) | ||||
|                 return PTR_ERR(hdcp_dent); | ||||
|  | ||||
|         //FIXME: error handling | ||||
|         debugfs_create_file("supported", 0444, hdcp_dent, hdmi, | ||||
|                 &hdcp_debugfs_fops[0]); | ||||
|         debugfs_create_file("available", 0444, hdcp_dent, hdmi, | ||||
|                 &hdcp_debugfs_fops[1]); | ||||
|         debugfs_create_file("on", 0444, hdcp_dent, hdmi, | ||||
|                 &hdcp_debugfs_fops[2]); | ||||
|         debugfs_create_file("off", 0444, hdcp_dent, hdmi, | ||||
|                 &hdcp_debugfs_fops[3]); | ||||
|         debugfs_create_file("restart", 0444, hdcp_dent, hdmi, | ||||
|                 &hdcp_debugfs_fops[4]); | ||||
|         debugfs_create_file("poll", 0444, hdcp_dent, hdmi, | ||||
|                 &hdcp_debugfs_fops[5]); | ||||
|         debugfs_create_file("aksv", 0444, hdcp_dent, hdmi, | ||||
|                 &hdcp_debugfs_fops[6]); | ||||
|         debugfs_create_file("bksv", 0444, hdcp_dent, hdmi, | ||||
|                 &hdcp_debugfs_fops[7]); | ||||
|  | ||||
|         return 0; | ||||
| } | ||||
| #endif | ||||
|  | ||||
							
								
								
									
										960
									
								
								drivers/video/msm/hdmi/silicon-image/tpi.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										960
									
								
								drivers/video/msm/hdmi/silicon-image/tpi.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,960 @@ | ||||
| #include <linux/clk.h> | ||||
| #include <linux/err.h> | ||||
| #include <linux/delay.h> | ||||
| #include <linux/debugfs.h> | ||||
| #include <linux/uaccess.h> | ||||
| #include <mach/msm_fb.h> | ||||
| #include <mach/msm_hdmi.h> | ||||
| #include <linux/interrupt.h> | ||||
|  | ||||
| // FIXME: remove this if unnecessary in the future | ||||
| #ifdef CONFIG_HTC_HEADSET_MGR | ||||
| #include <mach/htc_headset_mgr.h> | ||||
| #endif | ||||
|  | ||||
| #if 1 | ||||
| #define HDMI_DBG(s...) printk("[hdmi/tpi]" s) | ||||
| #else | ||||
| #define HDMI_DBG(s...) do {} while (0) | ||||
| #endif | ||||
|  | ||||
| #include "../include/fb-hdmi.h" | ||||
| #include "../include/sil902x.h" | ||||
| #include "../include/tpi.h" | ||||
|  | ||||
| #define NEW_INTEGRATE | ||||
| #define DBG_POLLING     0x1 | ||||
| static int debug_mask; | ||||
| module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); | ||||
|  | ||||
| #define DLOG(mask, fmt, args...) \ | ||||
| do { \ | ||||
|         if (debug_mask & mask) \ | ||||
|                 printk(KERN_INFO "[hdmi/sil]: "fmt, ##args); \ | ||||
| } while (0) | ||||
|  | ||||
| #define X1	0x01 | ||||
| #define AFTER_INIT              1 | ||||
|  | ||||
| void HotPlugService (struct hdmi_info *hdmi); | ||||
| // FIXME: should be decide by detection | ||||
| static bool dsRxPoweredUp; | ||||
| static bool edidDataValid; | ||||
| static bool tmdsPoweredUp; | ||||
| u8 pvid_mode, vid_mode = 16; | ||||
| u8 systemInitialized; | ||||
|  | ||||
| void tpi_clear_interrupt(struct hdmi_info *hdmi, u8 pattern) | ||||
| { | ||||
| 	/* write "1" to clear interrupt bit, and 0 won't effect origin value. */ | ||||
| 	hdmi_write_byte(hdmi->client, TPI_INTERRUPT_STATUS_REG, pattern); | ||||
| } | ||||
|  | ||||
| ////////////////////////////////////////////////////////////////////////////// | ||||
| // FUNCTION      :  EnableInterrupts() | ||||
| // PURPOSE       :  Enable the interrupts specified in the input parameter | ||||
| // INPUT PARAMS  :  A bit pattern with "1" for each interrupt that needs to be | ||||
| //                  set in the Interrupt Enable Register (TPI offset 0x3C) | ||||
| // OUTPUT PARAMS :  void | ||||
| // GLOBALS USED  :  None | ||||
| // RETURNS       :  TRUE | ||||
| ////////////////////////////////////////////////////////////////////////////// | ||||
| bool tpi_enable_interrupts(struct hdmi_info *hdmi, u8 Interrupt_Pattern) | ||||
| { | ||||
| 	HDMI_DBG("%s, reg=%02x, pat=%02x\n", __func__, TPI_INTERRUPT_EN, Interrupt_Pattern); | ||||
| 	ReadSetWriteTPI(hdmi, TPI_INTERRUPT_EN, Interrupt_Pattern); | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| static void tpi_disable_interrupts(struct hdmi_info *hdmi, u8 pattern) | ||||
| { | ||||
| /* | ||||
| 	HDMI_DBG("%s, reg=%02x, pat=%02x\n", __func__, | ||||
| 			TPI_INTERRUPT_EN, pattern); | ||||
| */ | ||||
| 	ReadClearWriteTPI(hdmi, TPI_INTERRUPT_EN, pattern); | ||||
| } | ||||
|  | ||||
| static void tpi_clear_pending_event(struct hdmi_info *hdmi) | ||||
| { | ||||
| 	int retry = 100; | ||||
|  | ||||
| 	if (hdmi->sleeping == SLEEP) return; | ||||
| 	while (retry--) { | ||||
| 		hdmi_write_byte(hdmi->client, 0x3c, 1); | ||||
| 		hdmi_write_byte(hdmi->client, 0x3d, 1); | ||||
| 		if (hdmi_read(hdmi->client, 0x3d) & 0x01) | ||||
| 			msleep(1); | ||||
| 		else | ||||
| 			break; | ||||
| 	} | ||||
| 	if (retry < 19) HDMI_DBG("%s: retry=%d\n", __func__, 19 - retry); | ||||
| } | ||||
|  | ||||
| ////////////////////////////////////////////////////////////////////////////// | ||||
| // FUNCTION     :   ReadBackDoorRegister() | ||||
| // PURPOSE      :   Read a 922x register value from a backdoor register | ||||
| //                  Write: | ||||
| //                      1. 0xBC => Internal page num | ||||
| //                      2. 0xBD => Backdoor register offset | ||||
| //                  Read: | ||||
| //                      3. 0xBE => Returns the backdoor register value | ||||
| // INPUT PARAMS :   Internal page number, backdoor register offset, pointer to | ||||
| //                  buffer to store read value | ||||
| // OUTPUT PARAMS:   Buffer that stores the read value | ||||
| // RETURNS      :   TRUE  | ||||
| // NOTE         :   This workaround is needed for the 9220/2 only. | ||||
| ////////////////////////////////////////////////////////////////////////////// | ||||
| int tpi_read_backdoor_register(struct hdmi_info *hdmi, u8 PageNum, u8 RegOffset) | ||||
| { | ||||
| 	// FIXME: error handling | ||||
| 	struct i2c_client *client = hdmi->client; | ||||
|  | ||||
| 	/* Internal page */ | ||||
| 	hdmi_write_byte(client, TPI_INTERNAL_PAGE_REG, PageNum); | ||||
| 	/* Indexed register */ | ||||
| 	hdmi_write_byte(client, TPI_REGISTER_OFFSET_REG, RegOffset); | ||||
| 	/* Read value into buffer */ | ||||
| 	return hdmi_read(client, TPI_REGISTER_VALUE_REG); | ||||
| } | ||||
|  | ||||
| void tpi_write_backdoor_register(struct hdmi_info *hdmi, u8 PageNum, u8 RegOffset, u8 RegValue) { | ||||
| 	/* Internal page */ | ||||
| 	hdmi_write_byte(hdmi->client, TPI_INTERNAL_PAGE_REG, PageNum); | ||||
| 	/* Indexed register */ | ||||
| 	hdmi_write_byte(hdmi->client, TPI_REGISTER_OFFSET_REG, RegOffset); | ||||
| 	/* Read value into buffer */ | ||||
| 	hdmi_write_byte(hdmi->client, TPI_REGISTER_VALUE_REG, RegValue); | ||||
| } | ||||
|  | ||||
| #define TPI_INTERNAL_PAGE_REG		0xBC | ||||
| #define TPI_INDEXED_OFFSET_REG		0xBD | ||||
| #define TPI_INDEXED_VALUE_REG		0xBE | ||||
| #define INDEXED_PAGE_0			0x01 | ||||
| #define INDEXED_PAGE_1			0x02 | ||||
| #define INDEXED_PAGE_2			0x03 | ||||
|  | ||||
| void ReadModifyWriteIndexedRegister(struct hdmi_info *hdmi, u8 PageNum, u8 RegOffset, u8 Mask, u8 Value) | ||||
| { | ||||
| 	u8 Tmp; | ||||
|  | ||||
| 	hdmi_write_byte(hdmi->client, TPI_INTERNAL_PAGE_REG, PageNum); | ||||
| 	hdmi_write_byte(hdmi->client, TPI_INDEXED_OFFSET_REG, RegOffset); | ||||
| 	Tmp = hdmi_read(hdmi->client, TPI_INDEXED_VALUE_REG); | ||||
|  | ||||
| 	Tmp &= ~Mask; | ||||
| 	Tmp |= (Value & Mask); | ||||
|  | ||||
| 	hdmi_write_byte(hdmi->client, TPI_INDEXED_VALUE_REG, Tmp); | ||||
| } | ||||
|  | ||||
| void ReadSetWriteTPI(struct hdmi_info *hdmi, u8 Offset, u8 Pattern) | ||||
| { | ||||
| 	u8 Tmp; | ||||
| 	struct i2c_client *client = hdmi->client; | ||||
|  | ||||
| 	Tmp = hdmi_read(client, Offset); | ||||
| 	Tmp |= Pattern; | ||||
| 	hdmi_write_byte(client, Offset, Tmp); | ||||
| } | ||||
|  | ||||
| int tpi_set_bit(struct hdmi_info *hdmi, u8 reg, u8 pattern) | ||||
| { | ||||
| 	return hdmi_write_byte(hdmi->client, reg, | ||||
| 		hdmi_read(hdmi->client, reg) | pattern); | ||||
| } | ||||
| //// | ||||
| void ReadModifyWriteTPI(struct hdmi_info *hdmi, u8 Offset, u8 Mask, u8 Value) | ||||
| { | ||||
| 	u8 Tmp; | ||||
| 	struct i2c_client *client = hdmi->client; | ||||
|  | ||||
| 	Tmp = hdmi_read(client, Offset); | ||||
| 	Tmp &= ~Mask; | ||||
| 	Tmp |= (Value & Mask); | ||||
| 	hdmi_write_byte(client, Offset, Tmp); | ||||
| } | ||||
| //// | ||||
| void ReadClearWriteTPI(struct hdmi_info *hdmi, u8 Offset, u8 Pattern) | ||||
| { | ||||
| 	u8 Tmp; | ||||
|  | ||||
| 	Tmp = hdmi_read(hdmi->client, Offset); | ||||
| 	Tmp &= ~Pattern; | ||||
| 	hdmi_write_byte(hdmi->client, Offset, Tmp); | ||||
| } | ||||
| void tpi_clear_bit(struct hdmi_info *hdmi, u8 reg, u8 pattern) | ||||
| { | ||||
| 	hdmi_write_byte(hdmi->client, reg, | ||||
| 		hdmi_read(hdmi->client, reg) & pattern); | ||||
| } | ||||
| //// | ||||
|  | ||||
| /* Caller: ChangeVideoMode(), HDCP_Poll(), HotPlugServiceLoop(), RestartHDCP() | ||||
|  */ | ||||
|  | ||||
| void EnableTMDS(struct hdmi_info *hdmi) | ||||
| { | ||||
| 	u8 val; | ||||
| #if 1 | ||||
| 	/* 0x1A[4] = 0 */ | ||||
| 	ReadClearWriteTPI(hdmi, TPI_SYSTEM_CONTROL, BIT_TMDS_OUTPUT); | ||||
|  | ||||
| 	if (edid_check_sink_type(hdmi)) | ||||
| 		hdmi_write_byte(hdmi->client, 0x26, | ||||
| 			hdmi_read(hdmi->client, 0x26) & ~0x10); | ||||
|  | ||||
| #else | ||||
| 	struct i2c_client *client = hdmi->i2c_client; | ||||
|  | ||||
| 	val = hdmi_read(client, TPI_SYSTEM_CONTROL); | ||||
| 	hdmi_write_byte(client, TPI_SYSTEM_CONTROL, val & ~BIT_TMDS_OUTPUT); | ||||
| 	HDMI_DBG("%s, reg 0x1a: %02x->%02x\n", __func__, | ||||
| 		val, val & ~BIT_TMDS_OUTPUT);  | ||||
| #endif | ||||
| 	 | ||||
| } | ||||
|  | ||||
| /* Caller: ChangeVideoMode(), HDCP_Poll(), TPI_Poll(), RestartHDCP(), | ||||
|  * 	OnHdmiCableDisconnected() | ||||
|  */ | ||||
|  | ||||
| void DisableTMDS(struct hdmi_info *hdmi) | ||||
| { | ||||
| 	/* 0x1A[4] = 1 */ | ||||
| 	//ReadClearWriteTPI(hdmi, TPI_SYSTEM_CONTROL, BIT_TMDS_OUTPUT); | ||||
| 	ReadSetWriteTPI(hdmi, TPI_SYSTEM_CONTROL, BIT_TMDS_OUTPUT); | ||||
| } | ||||
| static void OnDownstreamRxPoweredDown(struct hdmi_info *hdmi) | ||||
| { | ||||
| 	HDMI_DBG("%s\n", __func__); | ||||
| 	dsRxPoweredUp = false; | ||||
| 	hdcp_off(hdmi); | ||||
| } | ||||
|  | ||||
| static void OnDownstreamRxPoweredUp(struct hdmi_info *hdmi) | ||||
| { | ||||
|         HDMI_DBG("%s\n", __func__); | ||||
| 	dsRxPoweredUp = true; | ||||
| 	HotPlugService(hdmi); | ||||
| #ifdef CONFIG_HTC_HEADSET_MGR | ||||
| 	/* send cable in event */ | ||||
| 	switch_send_event(BIT_HDMI_CABLE, 1); | ||||
| 	HDMI_DBG("Cable inserted.\n"); | ||||
| #endif | ||||
| 	pvid_mode = vid_mode; | ||||
|         hdmi_active9022_dup(hdmi->client); | ||||
| } | ||||
|  | ||||
| bool GetDDC_Access(struct hdmi_info *hdmi, u8* SysCtrlRegVal) | ||||
| { | ||||
| 	u8 sysCtrl, TPI_ControlImage, DDCReqTimeout = T_DDC_ACCESS; | ||||
|  | ||||
| 	HDMI_DBG("%s\n", __func__); | ||||
| 	/* Read and store original value. Will be passed into ReleaseDDC() */ | ||||
| 	sysCtrl = hdmi_read(hdmi->client, TPI_SYSTEM_CONTROL); | ||||
| 	*SysCtrlRegVal = sysCtrl; | ||||
|  | ||||
| 	sysCtrl |= BIT_DDC_BUS_REQ; | ||||
| 	hdmi_write_byte(hdmi->client, TPI_SYSTEM_CONTROL, sysCtrl); | ||||
|  | ||||
| 	/* Loop till 0x1A[1] reads "1" */ | ||||
| 	while (DDCReqTimeout--) { | ||||
| 		TPI_ControlImage = hdmi_read(hdmi->client, TPI_SYSTEM_CONTROL); | ||||
|  | ||||
| 		/* When 0x1A[1] reads "1" */ | ||||
| 		if (TPI_ControlImage & BIT_DDC_BUS_GRANT) { | ||||
| 			sysCtrl |= BIT_DDC_BUS_GRANT; | ||||
| 			/* lock host DDC bus access (0x1A[2:1] = 11) */ | ||||
| 			hdmi_write_byte(hdmi->client, TPI_SYSTEM_CONTROL, sysCtrl); | ||||
| 			return true; | ||||
| 		} | ||||
| 		/* 0x1A[2] = "1" - Requst the DDC bus */ | ||||
| 		hdmi_write_byte(hdmi->client, TPI_SYSTEM_CONTROL, sysCtrl); | ||||
| 		mdelay(200); | ||||
| 	} | ||||
|  | ||||
| 	/* Failure... restore original value. */ | ||||
| 	hdmi_write_byte(hdmi->client, TPI_SYSTEM_CONTROL, sysCtrl); | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| bool ReleaseDDC(struct hdmi_info *hdmi, u8 SysCtrlRegVal) | ||||
| { | ||||
| 	u8 DDCReqTimeout = T_DDC_ACCESS, TPI_ControlImage; | ||||
|  | ||||
| 	HDMI_DBG("%s\n", __func__); | ||||
| 	/* Just to be sure bits [2:1] are 0 before it is written */ | ||||
| 	SysCtrlRegVal &= ~(0x6); | ||||
| 	/* Loop till 0x1A[1] reads "0" */ | ||||
| 	while (DDCReqTimeout--) { | ||||
| 		/* Cannot use ReadClearWriteTPI() here. A read of | ||||
| 		 * TPI_SYSTEM_CONTROL is invalid while DDC is granted. | ||||
| 		 * Doing so will return 0xFF, and cause an invalid value to be | ||||
| 		 * written back.  | ||||
| 		 */ | ||||
| 		/* 0x1A[2:1] = "0" - release the DDC bus */ | ||||
| 		//ReadClearWriteTPI(TPI_SYSTEM_CONTROL,BITS_2_1); | ||||
|  | ||||
| 		hdmi_write_byte(hdmi->client, TPI_SYSTEM_CONTROL, SysCtrlRegVal); | ||||
| 		TPI_ControlImage = hdmi_read(hdmi->client, TPI_SYSTEM_CONTROL); | ||||
| 		/* When 0x1A[2:1] read "0" */ | ||||
| 		if (!(TPI_ControlImage & 0x6)) | ||||
| 			return true; | ||||
| 	} | ||||
|  | ||||
| 	/* Failed to release DDC bus control */ | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| int tpi_read_edid(struct hdmi_info *hdmi) | ||||
| { | ||||
| 	u8 SysCtrlReg; | ||||
| 	int ret, edid_blocks = 0; | ||||
| 	struct i2c_msg msg; | ||||
| 	u8 i2c_buff[2]; | ||||
| 	u8 pbuf[] = {1, 0, 1, 128} ; | ||||
|  | ||||
| 	struct i2c_msg paging_msg[] = { | ||||
| 		{ | ||||
| 			.addr = 0x30, .flags = 0, .len = 1, .buf = &pbuf[0], | ||||
| 		}, | ||||
| 		{ | ||||
| 			.addr = 0x50, .flags = 0, .len = 1, .buf = &pbuf[1], | ||||
| 		}, | ||||
| 		{	//Block-2 | ||||
| 			.addr = 0x50, .flags = I2C_M_RD, .len = 128, .buf = &hdmi->edid_buf[256], | ||||
| 		}, | ||||
|                 { | ||||
|                         .addr = 0x30, .flags = 0, .len = 1, .buf = &pbuf[2], | ||||
|                 }, | ||||
|                 { | ||||
|                         .addr = 0x50, .flags = 0, .len = 1, .buf = &pbuf[3], | ||||
|                 }, | ||||
|                 {       //Block-3 | ||||
|                         .addr = 0x50, .flags = I2C_M_RD, .len = 128, .buf = &hdmi->edid_buf[384], | ||||
|                 }, | ||||
| 	}; | ||||
|  | ||||
| 	HDMI_DBG("%s\n", __func__); | ||||
| #if 0 | ||||
| 	DisableTMDS(hdmi); | ||||
| #else | ||||
| 	u8 val; | ||||
| 	val = hdmi_read(hdmi->client, TPI_SYSTEM_CONTROL); | ||||
| 	//hdmi_write_byte(hdmi->client, TPI_SYSTEM_CONTROL, val|BIT_4|BIT_6); | ||||
| 	hdmi_write_byte(hdmi->client, TPI_SYSTEM_CONTROL, val|BIT_4); | ||||
| #endif | ||||
|  | ||||
| 	if (!GetDDC_Access(hdmi, &SysCtrlReg)) { | ||||
| 		pr_err("%s: DDC bus request failed\n", __func__); | ||||
| 		return DDC_BUS_REQ_FAILURE; | ||||
| 	} | ||||
|  | ||||
| 	// Block-0 | ||||
| 	memset(hdmi->edid_buf, 0, 512); | ||||
|  | ||||
| 	msg.addr = 0x50; | ||||
| 	msg.flags = 0; | ||||
| 	msg.len = 1; | ||||
| 	msg.buf = hdmi->edid_buf; | ||||
| 	ret = i2c_transfer(hdmi->client->adapter, &msg, 1); | ||||
| 	if (ret < 0) | ||||
| 		dev_err(&hdmi->client->dev, "%s: i2c transfer error\n", __func__); | ||||
|  | ||||
| 	msg.addr = 0x50; | ||||
| 	msg.flags = I2C_M_RD; | ||||
| 	msg.len = 128; | ||||
| 	msg.buf = hdmi->edid_buf; | ||||
| 	ret = i2c_transfer(hdmi->client->adapter, &msg, 1); | ||||
| 	if (ret < 0) { | ||||
| 		dev_err(&hdmi->client->dev, "%s: i2c transfer error\n", __func__); | ||||
|                 goto end_read_edid; | ||||
| 	} else { | ||||
| 		if (hdmi->edid_buf[0x7e] <= 3) | ||||
| 			edid_blocks = hdmi->edid_buf[0x7e] ; | ||||
|  | ||||
|                 dev_info(&hdmi->client->dev, "EDID blocks = %d\n", edid_blocks); | ||||
|  | ||||
| 		if (edid_blocks == 0 ) { | ||||
|                         goto end_read_edid; | ||||
|                 } | ||||
| 		// Block-1 | ||||
| 		msg.addr = 0x50; | ||||
| 		msg.flags = 0; | ||||
| 		msg.len = 1; | ||||
| 		i2c_buff[0] = 128; | ||||
| 		msg.buf = i2c_buff; | ||||
| 		ret = i2c_transfer(hdmi->client->adapter, &msg, 1); | ||||
|  | ||||
| 		msg.addr = 0x50; | ||||
| 		msg.flags = I2C_M_RD; | ||||
| 		msg.len = 128; | ||||
| 		msg.buf = &hdmi->edid_buf[128]; | ||||
| 		ret = i2c_transfer(hdmi->client->adapter, &msg, 1); | ||||
| 	} | ||||
|  | ||||
| 	if (edid_blocks > 1) { | ||||
| 		// block 2/3 | ||||
| 		i2c_transfer(hdmi->client->adapter, paging_msg, 3); | ||||
| 		i2c_transfer(hdmi->client->adapter, &paging_msg[3], 3); | ||||
| 	} | ||||
|  | ||||
| end_read_edid: | ||||
| 	if (!ReleaseDDC(hdmi, SysCtrlReg)) { | ||||
| 		pr_err("%s: DDC bus release failed\n", __func__); | ||||
| 		return DDC_BUS_REQ_FAILURE; | ||||
| 	} | ||||
|  | ||||
| 	edid_simple_parsing(hdmi); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| ////////////////////////////////////////////////////////////////////////////// | ||||
| // FUNCTION     :   HotPlugService() | ||||
| // PURPOSE      :   Implement Hot Plug Service Loop activities | ||||
| // INPUT PARAMS :   None | ||||
| // OUTPUT PARAMS:   void | ||||
| // GLOBALS USED :   LinkProtectionLevel | ||||
| // RETURNS      :   An error code that indicates success or cause of failure | ||||
| ////////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
| extern bool HDCP_TxSupports; | ||||
| static bool tmdsPoweredUp; | ||||
| void HotPlugService (struct hdmi_info *hdmi) | ||||
| { | ||||
|         HDMI_DBG("%s\n", __func__); | ||||
|  | ||||
| 	mutex_lock(&hdmi->lock); | ||||
| 	tpi_disable_interrupts(hdmi, 0xFF); | ||||
|  | ||||
| /* | ||||
| 	// use 1st mode supported by sink | ||||
| 	//vid_mode = EDID_Data.VideoDescriptor[0]; | ||||
| 	vid_mode = 0; | ||||
| */ | ||||
| 	avc_init_video(hdmi, vid_mode, X1, AFTER_INIT); | ||||
|  | ||||
| 	hdmi_write_byte(hdmi->client, HDMI_POWER, 0); | ||||
| 	if (edid_check_sink_type(hdmi)) | ||||
| 		avc_send_avi_info_frames(hdmi); | ||||
|  | ||||
| 	/* This check needs to be changed to if HDCP is required by the content  | ||||
| 	   once support has been added by RX-side library. */ | ||||
| 	if (HDCP_TxSupports == true) { | ||||
| 		HDMI_DBG("TMDS -> Enabled\n"); | ||||
|                 /* turn on black mode will lost around 3 secs frames thus remove it */ | ||||
| 		//SetInputColorSpace(hdmi, INPUT_COLOR_SPACE_BLACK_MODE); | ||||
| #if 1 | ||||
| 		ReadModifyWriteTPI(hdmi, TPI_SYSTEM_CONTROL, | ||||
| 			LINK_INTEGRITY_MODE_MASK | TMDS_OUTPUT_CONTROL_MASK, | ||||
| 			LINK_INTEGRITY_DYNAMIC | TMDS_OUTPUT_CONTROL_ACTIVE); | ||||
| #else | ||||
| 		ReadModifyWriteTPI(hdmi, TPI_SYSTEM_CONTROL, | ||||
| 			LINK_INTEGRITY_MODE_MASK | TMDS_OUTPUT_CONTROL_MASK, | ||||
| 			LINK_INTEGRITY_DYNAMIC); | ||||
| #endif | ||||
| 		tmdsPoweredUp = true; | ||||
| 	} else { | ||||
| 		EnableTMDS(hdmi); | ||||
| 	} | ||||
|  | ||||
| 	if (edid_check_sink_type(hdmi)) | ||||
| 		avc_set_basic_audio(hdmi); | ||||
| 	else | ||||
| 		SetAudioMute(hdmi, AUDIO_MUTE_MUTED); | ||||
|  | ||||
| 	tpi_enable_interrupts(hdmi, HOT_PLUG_EVENT | RX_SENSE_EVENT | | ||||
| 		AUDIO_ERROR_EVENT | SECURITY_CHANGE_EVENT | | ||||
| 		V_READY_EVENT | HDCP_CHANGE_EVENT); | ||||
|  | ||||
| 	//complete(&hdmi->hotplug_completion); | ||||
| 	mutex_unlock(&hdmi->lock); | ||||
| } | ||||
|  | ||||
| static bool tpi_start(struct hdmi_info *hdmi) | ||||
| { | ||||
| 	u8 devID = 0x00; | ||||
| 	u16 wID = 0x0000; | ||||
|  | ||||
| 	hdmi_write_byte(hdmi->client, TPI_ENABLE, 0x00);            // Write "0" to 72:C7 to start HW TPI mode | ||||
| 	mdelay(100); | ||||
|  | ||||
| 	devID = tpi_read_backdoor_register(hdmi, 0x00, 0x03); | ||||
| 	wID = devID; | ||||
| 	wID <<= 8; | ||||
| 	devID = tpi_read_backdoor_register(hdmi, 0x00, 0x02); | ||||
| 	wID |= devID; | ||||
| 	devID = hdmi_read(hdmi->client, TPI_DEVICE_ID); | ||||
| 	HDMI_DBG("%s, ID=%04X\n", __func__, (u32)wID); | ||||
|  | ||||
| 	if (devID == SiI_DEVICE_ID) { | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| 	pr_err("%s: Unsupported TX\n", __func__); | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| bool tpi_init(struct hdmi_info *hdmi) | ||||
| { | ||||
| 	tmdsPoweredUp = false; | ||||
| 	hdmi->cable_connected = false; | ||||
| 	dsRxPoweredUp = false; | ||||
| 	edidDataValid = false; | ||||
|  | ||||
| 	/* Enable HW TPI mode, check device ID */ | ||||
| 	if (tpi_start(hdmi)) {	 | ||||
| 		hdcp_init(hdmi); | ||||
| 		return true; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| void SetAudioMute(struct hdmi_info *hdmi, u8 audioMute) | ||||
| { | ||||
| 	ReadModifyWriteTPI(hdmi, TPI_AUDIO_INTERFACE_REG, AUDIO_MUTE_MASK, audioMute); | ||||
| } | ||||
|  | ||||
| void SetInputColorSpace(struct hdmi_info *hdmi, u8 inputColorSpace) | ||||
| { | ||||
| 	ReadModifyWriteTPI(hdmi, TPI_INPUT_FORMAT_REG, INPUT_COLOR_SPACE_MASK, inputColorSpace); | ||||
| 	/* Must be written for previous write to take effect. Just write read value unmodified. */ | ||||
| 	ReadModifyWriteTPI(hdmi, TPI_END_RIGHT_BAR_MSB, 0x00, 0x00); | ||||
| } | ||||
|  | ||||
| static char edid_hex_buff[2048]; | ||||
| int lcdc_enable_video(void); | ||||
| int lcdc_disable_video(void); | ||||
| void tpi_cable_conn(struct hdmi_info *hdmi) | ||||
| { | ||||
| 	HDMI_DBG("%s\n", __func__); | ||||
|  | ||||
| 	hdmi->cable_connected = true; | ||||
| 	tpi_write_backdoor_register(hdmi, INTERNAL_PAGE_0, 0xCE, 0x00); // Clear BStatus | ||||
| 	tpi_write_backdoor_register(hdmi, INTERNAL_PAGE_0, 0xCF, 0x00); | ||||
|  | ||||
| //----------------------------------------------- | ||||
| 	hdmi_write_byte(hdmi->client, 0x09, 0x03); | ||||
| 	hdmi_write_byte(hdmi->client, 0x19, 0x00); // go to blank mode, avoid screen noise | ||||
|  | ||||
| /* | ||||
| 	HDMI_DBG("solomon: H/V total=%02x, %02x, %02x, %02x\n", | ||||
| 		hdmi_read(hdmi->client, 0x6a), | ||||
| 		hdmi_read(hdmi->client, 0x6b), | ||||
| 		hdmi_read(hdmi->client, 0x6c), | ||||
| 		hdmi_read(hdmi->client, 0x6d) | ||||
| 		); | ||||
| */ | ||||
|  | ||||
| 	lcdc_enable_video(); | ||||
| 	msleep(160); | ||||
| /* | ||||
| 	//clk_set_rate(hdmi->ebi1_clk, 120000000); | ||||
| 	HDMI_DBG("solomon: H/V total=%02x, %02x, %02x, %02x\n", | ||||
| 		hdmi_read(hdmi->client, 0x6a), | ||||
| 		hdmi_read(hdmi->client, 0x6b), | ||||
| 		hdmi_read(hdmi->client, 0x6c), | ||||
| 		hdmi_read(hdmi->client, 0x6d) | ||||
| 		); | ||||
| */ | ||||
| 	EnableTMDS(hdmi);	 | ||||
| 	 | ||||
| //----------------------------------------------- | ||||
|  | ||||
| 	tpi_read_edid(hdmi); | ||||
| 	memset(edid_hex_buff, 0, 2048); | ||||
| 	edid_dump_hex(hdmi->edid_buf, 256, edid_hex_buff, 2048); | ||||
| 	printk("EDID data:\n%s\n=====", edid_hex_buff); | ||||
| 	/* select output mode (HDMI/DVI) according to sink capabilty */ | ||||
| 	if (edid_check_sink_type(hdmi)) | ||||
| 		ReadModifyWriteTPI(hdmi, TPI_SYSTEM_CONTROL, OUTPUT_MODE_MASK, OUTPUT_MODE_HDMI); | ||||
| 	else | ||||
| 		ReadModifyWriteTPI(hdmi, TPI_SYSTEM_CONTROL, OUTPUT_MODE_MASK, OUTPUT_MODE_DVI); | ||||
|  | ||||
| 	hdmi->first = false; | ||||
| #if 0 | ||||
| #ifdef CONFIG_HTC_HEADSET_MGR | ||||
| 	/* send cable in event */ | ||||
| 	switch_send_event(BIT_HDMI_CABLE, 1); | ||||
| 	HDMI_DBG("Cable inserted.\n"); | ||||
| #endif | ||||
| #endif | ||||
| } | ||||
|  | ||||
| void tpi_cable_disconn(struct hdmi_info *hdmi, bool into_d3) | ||||
| { | ||||
| 	HDMI_DBG("%s, into_d3=%d\n", __func__, into_d3); | ||||
|  | ||||
| 	hdmi->cable_connected = false; | ||||
| 	dsRxPoweredUp = false; | ||||
| 	edidDataValid = false; | ||||
| 	hdcp_off(hdmi); | ||||
| 	DisableTMDS(hdmi); | ||||
| #if 1 | ||||
|         /* wait for debounce */ | ||||
|         msleep(20); | ||||
| 	tpi_clear_pending_event(hdmi); | ||||
| #else | ||||
| 	reg = hdmi_read(hdmi->client, 0x3d); | ||||
| 	if (!(reg & 0x0c)) | ||||
| 		tpi_clear_pending_event(hdmi); | ||||
| #endif | ||||
| 	if (into_d3) { | ||||
| 		mutex_lock(&hdmi->lock); | ||||
| 		HDMI_DBG("%s, playing=%d\n", __func__, hdmi->user_playing); | ||||
| 		if (false == hdmi->user_playing) | ||||
| 			lcdc_disable_video(); | ||||
| 		clk_set_rate(hdmi->ebi1_clk, 0); | ||||
| 		hdmi_standby(hdmi); | ||||
| 		hdmi->power(2); | ||||
| 		memset(hdmi->edid_buf, 0, 512); | ||||
| 		mutex_unlock(&hdmi->lock); | ||||
| 	} | ||||
| #ifdef CONFIG_HTC_HEADSET_MGR | ||||
| 	HDMI_DBG("Cable unplugged.\n"); | ||||
| 	switch_send_event(BIT_HDMI_CABLE, 0); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| static char *str_debug_interrupt[] = { | ||||
| 	"HOT_PLUG_EVENT\t\t\t", | ||||
| 	"RECEIVER_SENSE_EVENT\t\t", | ||||
| 	"HOT_PLUG_PIN_STATE\t\t", | ||||
| 	"RX_SENSE_MASK\t\t\t", | ||||
| 	"AUDIO_ERROR_EVENT\t\t", | ||||
| 	"HDCP_SECURITY_CHANGE_EVENT\t", | ||||
| 	"HDCP_VPRIME_VALUE_READY_EVENT\t", | ||||
| 	"HDCP_AUTH_STATUS_CHANGE_EVENT\t", | ||||
| }; | ||||
|  | ||||
| void tpi_debug_interrupt(struct hdmi_info *hdmi, u8 old_status, u8 new_status) | ||||
| { | ||||
| 	int i, diff, on_off; | ||||
| 	HDMI_DBG("%s: status changed, %02x to %02x\n", __func__, | ||||
| 		old_status, new_status); | ||||
| 	for (i = 7; i >= 0; i--) { | ||||
| 		diff = (old_status ^ new_status) & (1 << i); | ||||
| 		if (!diff) | ||||
| 			continue; | ||||
| 		on_off = new_status & (1 << i); | ||||
| 		HDMI_DBG("%d-%s->%s\n", i, str_debug_interrupt[i], | ||||
| 			on_off ? "on" : "off"); | ||||
| 	} | ||||
| } | ||||
| ////////////////////////////////////////////////////////////////////////////// | ||||
| // FUNCTION     :   TPI_Poll () | ||||
| // PURPOSE      :   Poll Interrupt Status register for new interrupts | ||||
| // INPUT PARAMS :   None | ||||
| // OUTPUT PARAMS:   None | ||||
| // GLOBALS USED :   LinkProtectionLevel | ||||
| // RETURNS      :   None | ||||
| ////////////////////////////////////////////////////////////////////////////// | ||||
| static u8 last_status = 0; | ||||
| static void tpi_poll(struct hdmi_info *hdmi) | ||||
| { | ||||
| 	u8 status, orig_status; | ||||
| 	int retry = 20; | ||||
|  | ||||
| 	mutex_lock(&hdmi->polling_lock); | ||||
| 	orig_status = status = hdmi_read(hdmi->client, TPI_INTERRUPT_STATUS_REG); | ||||
| 	if (last_status != status) { | ||||
| 		tpi_debug_interrupt(hdmi, last_status, status); | ||||
| 	} | ||||
| 	last_status = status; | ||||
|         DLOG(DBG_POLLING, "%s, INT_STAT=%02x\n", __func__, status); | ||||
| #if 0 | ||||
| 	if (status & HOT_PLUG_EVENT) { | ||||
| #else | ||||
| 	if (hdmi->first || status & HOT_PLUG_EVENT) { | ||||
| 		if (hdmi->first) hdmi->first = false; | ||||
| #endif | ||||
| 		// Enable HPD interrupt bit | ||||
| 		ReadSetWriteTPI(hdmi, TPI_INTERRUPT_ENABLE_REG, HOT_PLUG_EVENT); | ||||
| 		// Repeat this loop while cable is bouncing: | ||||
| 		do { | ||||
| 			//DLOG(DBG_POLLING, "TPI: Interrupt status image - 2= %02x\n", status); | ||||
| 			hdmi_write_byte(hdmi->client, TPI_INTERRUPT_STATUS_REG, HOT_PLUG_EVENT); | ||||
| 			// Delay for metastability protection and to help filter out connection bouncing | ||||
| 			mdelay(T_HPD_DELAY); | ||||
| 			// Read Interrupt status register | ||||
| 			status = hdmi_read(hdmi->client, TPI_INTERRUPT_STATUS_REG); | ||||
| 			//DLOG(DBG_POLLING, "TPI: Interrupt status image - 3= %02x\n", status); | ||||
| 			if (!retry--) {  | ||||
| 				HDMI_DBG("%s: retry failed\n", __func__); | ||||
| 				break; | ||||
| 			} | ||||
|  | ||||
| 		} while (status & HOT_PLUG_EVENT);// loop as long as HP interrupts recur | ||||
| 		DLOG(DBG_POLLING, "int status: %02x, after debouncing: %02x\n", | ||||
| 			orig_status, status); | ||||
|  | ||||
| 		//DLOG(DBG_POLLING, "TPI->hdmiCableConnected = %d\n", hdmi->cable_connected); | ||||
| 		if (((status & HOT_PLUG_STATE) >> 2) != hdmi->cable_connected) { | ||||
| 			DLOG(DBG_POLLING, "cable status changed: from %d to %d\n", | ||||
| 				hdmi->cable_connected, !!(status & HOT_PLUG_STATE)); | ||||
| 			//DLOG(DBG_POLLING, "TPI-> CONDITION\n"); | ||||
| 			if (hdmi->cable_connected == true) | ||||
| 				tpi_cable_disconn(hdmi, status & 0x8 ? false : true); | ||||
| 			else { | ||||
| 				tpi_cable_conn(hdmi); | ||||
| 				ReadModifyWriteIndexedRegister(hdmi, INDEXED_PAGE_0, 0x0A, 0x08, 0x08); | ||||
| 			} | ||||
| 			if (hdmi->cable_connected == false) { | ||||
| 				mutex_unlock(&hdmi->polling_lock); | ||||
| 				return; | ||||
| 			} | ||||
| 		} else if ( false == hdmi->cable_connected) | ||||
| 			/* only occur while booting without cable attached. */ | ||||
| 			tpi_cable_disconn(hdmi, true); | ||||
| 	} | ||||
|  | ||||
| 	// Check rx power | ||||
| 	if (((status & RX_SENSE_STATE) >> 3) != dsRxPoweredUp) | ||||
| 	{ | ||||
| 		if (hdmi->cable_connected == true) { | ||||
| 			if (dsRxPoweredUp == true) | ||||
| 				OnDownstreamRxPoweredDown(hdmi); | ||||
| 			else | ||||
| 				OnDownstreamRxPoweredUp(hdmi); | ||||
| 		} | ||||
| 		tpi_clear_interrupt(hdmi, RX_SENSE_EVENT); | ||||
| 	} | ||||
|  | ||||
| 	// Check if Audio Error event has occurred: | ||||
| 	if (status & AUDIO_ERROR_EVENT) | ||||
| 		//  The hardware handles the event without need for host intervention (PR, p. 31) | ||||
| 		tpi_clear_interrupt(hdmi, AUDIO_ERROR_EVENT); | ||||
|  | ||||
| 	if (hdmi->video_streaming) { | ||||
| 		if ((hdmi->cable_connected == true) && (dsRxPoweredUp == true)) | ||||
| 			hdcp_check_status(hdmi, status); | ||||
| 	} | ||||
| 	mutex_unlock(&hdmi->polling_lock); | ||||
| } | ||||
|  | ||||
| static void tpi_work_func(struct work_struct *work) | ||||
| { | ||||
| 	u8 reg = 0; | ||||
| 	struct hdmi_info *hdmi = | ||||
| 		container_of(work, struct hdmi_info, polling_work); | ||||
|  | ||||
| 	if (hdmi->sleeping == SLEEP) { | ||||
| 		mutex_lock(&hdmi->lock); | ||||
| 		hdmi->power(3); | ||||
| 		hdmi_wakeup(hdmi); | ||||
| 		tpi_init(hdmi); | ||||
| 		hdcp_off(hdmi); | ||||
| 		mutex_unlock(&hdmi->lock); | ||||
| 	} | ||||
|  | ||||
| 	tpi_poll(hdmi); | ||||
| #if 1 | ||||
| 	mutex_lock(&hdmi->lock); | ||||
| 	if (hdmi->sleeping == AWAKE) | ||||
| 		reg = hdmi_read(hdmi->client, 0x3d) & 0x0c; | ||||
| 	if (hdmi->cable_connected || reg) { | ||||
| 		hdmi->polling = true; | ||||
| 		mod_timer(&hdmi->timer, jiffies + INTERVAL_HDCP_POLLING); | ||||
| 	} else { | ||||
| 		enable_irq(hdmi->client->irq); | ||||
| 		hdmi->isr_enabled = true; | ||||
| 		hdmi->polling = false; | ||||
| 	} | ||||
| 	mutex_unlock(&hdmi->lock); | ||||
| #else | ||||
|         if (hdmi->sleeping == AWAKE) { | ||||
| 		reg = hdmi_read(hdmi->client, 0x3d); | ||||
| 		if (reg & 0x0c) { | ||||
|                 	hdmi->polling = true; | ||||
|                 	mod_timer(&hdmi->timer, jiffies + INTERVAL_HDCP_POLLING); | ||||
| 		} else { | ||||
| 			tpi_clear_pending_event(hdmi); | ||||
| 		}	 | ||||
| 	} | ||||
| 		 | ||||
|         if (hdmi->cable_connected ) { | ||||
|                 hdmi->polling = true; | ||||
|                 mod_timer(&hdmi->timer, jiffies + INTERVAL_HDCP_POLLING); | ||||
|         } else { | ||||
|                 enable_irq(hdmi->client->irq); | ||||
|                 hdmi->isr_enabled = true; | ||||
|                 hdmi->polling = false; | ||||
|         } | ||||
| #endif | ||||
| /* | ||||
| 	HDMI_DBG("after polling: reg=%02x, conn=%d, isr=%d, polling=%d\n", | ||||
| 		reg, hdmi->cable_connected, hdmi->isr_enabled, hdmi->polling); | ||||
| */ | ||||
| } | ||||
|  | ||||
| static void tpi_timer_func(unsigned long arg) | ||||
| { | ||||
| 	struct hdmi_info *hdmi = (struct hdmi_info *) arg; | ||||
|  | ||||
|         schedule_work(&hdmi->polling_work); | ||||
| } | ||||
|  | ||||
| int tpi_prepare(struct hdmi_info *hdmi) | ||||
| { | ||||
|         HDMI_DBG("%s\n", __func__); | ||||
|         init_timer(&hdmi->timer); | ||||
|         hdmi->timer.data = (unsigned long)hdmi; | ||||
|         hdmi->timer.function = tpi_timer_func; | ||||
| 	hdmi->cable_connected = false; | ||||
|  | ||||
| 	init_completion(&hdmi->hotplug_completion); | ||||
|         INIT_WORK(&hdmi->polling_work, tpi_work_func); | ||||
|  | ||||
|         return 0; | ||||
| } | ||||
|  | ||||
| /*============================================================================*/ | ||||
| #if defined(HDMI_DEBUGFS) | ||||
| static ssize_t tpi_dbg_open(struct inode *inode, struct file *file) | ||||
| { | ||||
|         file->private_data = inode->i_private; | ||||
|         return 0; | ||||
| } | ||||
|  | ||||
| static ssize_t tpi_ddc_request_read(struct file *filp, char __user *buf, | ||||
|                 size_t count, loff_t *ppos) | ||||
| { | ||||
|         //struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static ssize_t tpi_ddc_request_write(struct file *filp, const char __user *buf, | ||||
|                 size_t count, loff_t *ppos) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static ssize_t tpi_isr_read(struct file *filp, char __user *buf, | ||||
|                 size_t count, loff_t *ppos) | ||||
| { | ||||
|         int n = 0; | ||||
|         char buffer[4]; | ||||
|         struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; | ||||
|  | ||||
|         HDMI_DBG("%s\n", __func__); | ||||
|         n = scnprintf(buffer, 4, "%d\n", hdmi->isr_enabled); | ||||
|         n++; | ||||
|         buffer[n] = 0; | ||||
|         return simple_read_from_buffer(buf, count, ppos, buffer, n); | ||||
| } | ||||
|  | ||||
| static ssize_t tpi_polling_read(struct file *filp, char __user *buf, | ||||
|                 size_t count, loff_t *ppos) | ||||
| { | ||||
|         int n = 0; | ||||
|         char buffer[4]; | ||||
|         struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; | ||||
|  | ||||
|         HDMI_DBG("%s\n", __func__); | ||||
|         n = scnprintf(buffer, 4, "%d\n", hdmi->polling); | ||||
|         n++; | ||||
|         buffer[n] = 0; | ||||
|         return simple_read_from_buffer(buf, count, ppos, buffer, n); | ||||
| } | ||||
|  | ||||
| static ssize_t tpi_int_status_read(struct file *filp, char __user *buf, | ||||
|                 size_t count, loff_t *ppos) | ||||
| { | ||||
|         int n = 0; | ||||
|         char buffer[8]; | ||||
|         struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; | ||||
|  | ||||
|         HDMI_DBG("%s\n", __func__); | ||||
|         n = scnprintf(buffer, 8, "%02x\n", hdmi_read(hdmi->client, 0x3d)); | ||||
|         n++; | ||||
|         buffer[n] = 0; | ||||
|         return simple_read_from_buffer(buf, count, ppos, buffer, n); | ||||
| } | ||||
|  | ||||
| static ssize_t tpi_int_enable_read(struct file *filp, char __user *buf, | ||||
|                 size_t count, loff_t *ppos) | ||||
| { | ||||
|         int n = 0; | ||||
|         char buffer[8]; | ||||
|         struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; | ||||
|  | ||||
|         HDMI_DBG("%s\n", __func__); | ||||
|         n = scnprintf(buffer, 8, "%02x\n", hdmi_read(hdmi->client, 0x3c)); | ||||
|         n++; | ||||
|         buffer[n] = 0; | ||||
|         return simple_read_from_buffer(buf, count, ppos, buffer, n); | ||||
| } | ||||
|  | ||||
| static ssize_t tpi_avc_read(struct file *filp, char __user *buf, | ||||
|                 size_t count, loff_t *ppos) | ||||
| { | ||||
|         int n = 0; | ||||
|         char buffer[8]; | ||||
|         struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; | ||||
|  | ||||
|         HDMI_DBG("%s\n", __func__); | ||||
| /* | ||||
|         n = scnprintf(buffer, 8, "%02x\n", hdmi_read(hdmi->client, 0x3c)); | ||||
|         n++; | ||||
|         buffer[n] = 0; | ||||
|         return simple_read_from_buffer(buf, count, ppos, buffer, n); | ||||
| */ | ||||
| 	hdmi_active9022(hdmi->client); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static struct file_operations tpi_debugfs_fops[] = { | ||||
|         { | ||||
|                 .open  = tpi_dbg_open, | ||||
|                 .read  = tpi_ddc_request_read, | ||||
|                 .write = tpi_ddc_request_write, | ||||
|         }, | ||||
| 	{ | ||||
|                 .open  = tpi_dbg_open, | ||||
|                 .read  = tpi_isr_read, | ||||
| 	}, | ||||
| 	{ | ||||
|                 .open  = tpi_dbg_open, | ||||
|                 .read  = tpi_polling_read, | ||||
| 	}, | ||||
|         { | ||||
|                 .open  = tpi_dbg_open, | ||||
|                 .read  = tpi_int_status_read, | ||||
|         }, | ||||
|         { | ||||
|                 .open  = tpi_dbg_open, | ||||
|                 .read  = tpi_int_enable_read, | ||||
|         }, | ||||
|         { | ||||
|                 .open  = tpi_dbg_open, | ||||
|                 .read  = tpi_avc_read, | ||||
|         }, | ||||
| }; | ||||
|  | ||||
| int tpi_debugfs_init(struct hdmi_info *hdmi) | ||||
| { | ||||
|         struct dentry *tpi_dent; | ||||
|  | ||||
|         tpi_dent = debugfs_create_dir("tpi", hdmi->debug_dir); | ||||
|         if (IS_ERR(tpi_dent)) | ||||
|                 return PTR_ERR(tpi_dent); | ||||
|  | ||||
| 	//FIXME: error handling | ||||
|         debugfs_create_file("ddc_request", 0644, tpi_dent, hdmi, | ||||
|                 &tpi_debugfs_fops[0]); | ||||
|         debugfs_create_file("isr_enabled", 0444, tpi_dent, hdmi, | ||||
|                 &tpi_debugfs_fops[1]); | ||||
|         debugfs_create_file("polling", 0444, tpi_dent, hdmi, | ||||
|                 &tpi_debugfs_fops[2]); | ||||
|         debugfs_create_file("int_stat", 0444, tpi_dent, hdmi, | ||||
|                 &tpi_debugfs_fops[3]); | ||||
|         debugfs_create_file("int_ena", 0444, tpi_dent, hdmi, | ||||
|                 &tpi_debugfs_fops[4]); | ||||
|         debugfs_create_file("avc", 0444, tpi_dent, hdmi, | ||||
|                 &tpi_debugfs_fops[5]); | ||||
|  | ||||
|         return 0; | ||||
| } | ||||
| #endif | ||||
							
								
								
									
										1019
									
								
								drivers/video/msm/hdmi/transmitter.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1019
									
								
								drivers/video/msm/hdmi/transmitter.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -44,7 +44,9 @@ | ||||
| #define CMD_GET_CLIENT_STATUS  0x0602 | ||||
|  | ||||
| static uint32_t mddi_debug_flags; | ||||
|  | ||||
| #ifdef CONFIG_MSM_MDP40 | ||||
| static struct clk *mdp_clk; | ||||
| #endif | ||||
| union mddi_rev { | ||||
| 	unsigned char raw[MDDI_REV_BUFFER_SIZE]; | ||||
| 	struct mddi_rev_packet hdr; | ||||
| @@ -100,9 +102,11 @@ struct mddi_info { | ||||
| 	char client_name[20]; | ||||
|  | ||||
| 	struct platform_device client_pdev; | ||||
| 	unsigned type; | ||||
| }; | ||||
|  | ||||
| static void mddi_init_rev_encap(struct mddi_info *mddi); | ||||
| static void mddi_skew_calibration(struct mddi_info *mddi); | ||||
|  | ||||
| #define mddi_readl(r) readl(mddi->base + (MDDI_##r)) | ||||
| #define mddi_writel(v, r) writel((v), mddi->base + (MDDI_##r)) | ||||
| @@ -160,7 +164,7 @@ static void mddi_handle_rev_data(struct mddi_info *mddi, union mddi_rev *rev) | ||||
| 				printk(KERN_INFO "rev: got reg %x = %x without " | ||||
| 						 " pending read\n", | ||||
| 				       rev->reg.register_address, | ||||
| 				       rev->reg.register_data_list); | ||||
| 				       rev->reg.u.reg_data); | ||||
| 				break; | ||||
| 			} | ||||
| 			if (ri->reg != rev->reg.register_address) { | ||||
| @@ -168,12 +172,12 @@ static void mddi_handle_rev_data(struct mddi_info *mddi, union mddi_rev *rev) | ||||
| 						 "wrong register, expected " | ||||
| 						 "%x\n", | ||||
| 				       rev->reg.register_address, | ||||
| 				       rev->reg.register_data_list, ri->reg); | ||||
| 				       rev->reg.u.reg_data, ri->reg); | ||||
| 				break; | ||||
| 			} | ||||
| 			mddi->reg_read = NULL; | ||||
| 			ri->status = 0; | ||||
| 			ri->result = rev->reg.register_data_list; | ||||
| 			ri->result = rev->reg.u.reg_data; | ||||
| 			complete(&ri->done); | ||||
| 			break; | ||||
| 		default: | ||||
| @@ -397,9 +401,6 @@ static uint16_t mddi_init_registers(struct mddi_info *mddi) | ||||
| 	mddi_writel(0x0003, SPM); /* subframes per media */ | ||||
| 	mddi_writel(0x0005, TA1_LEN); | ||||
| 	mddi_writel(MDDI_HOST_TA2_LEN, TA2_LEN); | ||||
| 	mddi_writel(0x0096, DRIVE_HI); | ||||
| 	/* 0x32 normal, 0x50 for Toshiba display */ | ||||
| 	mddi_writel(0x0050, DRIVE_LO); | ||||
| 	mddi_writel(0x003C, DISP_WAKE); /* wakeup counter */ | ||||
| 	mddi_writel(MDDI_HOST_REV_RATE_DIV, REV_RATE_DIV); | ||||
|  | ||||
| @@ -418,8 +419,26 @@ static uint16_t mddi_init_registers(struct mddi_info *mddi) | ||||
| 	} | ||||
|  | ||||
| 	/* Recommendation from PAD hw team */ | ||||
| 	mddi_writel(0xa850f, PAD_CTL); | ||||
| 	if (mddi->type == MSM_MDP_MDDI_TYPE_II) | ||||
| 		mddi_writel(0x402a850f, PAD_CTL); | ||||
| 	else | ||||
| 		mddi_writel(0xa850f, PAD_CTL); | ||||
|  | ||||
| #if defined (CONFIG_ARCH_QSD8X50) || defined (CONFIG_ARCH_MSM7X30) | ||||
| 	/* Only for novatek driver IC*/ | ||||
| 	mddi_writel(0x00C8, DRIVE_HI); | ||||
| 	/* 0x32 normal, 0x50 for Toshiba display */ | ||||
| 	mddi_writel(0x0050, DRIVE_LO); | ||||
| 	mddi_writel(0x00320000, PAD_IO_CTL); | ||||
| 	if (mddi->type == MSM_MDP_MDDI_TYPE_II) | ||||
| 		mddi_writel(0x40884020, PAD_CAL); | ||||
| 	else | ||||
| 		mddi_writel(0x00220020, PAD_CAL); | ||||
| #else | ||||
| 	mddi_writel(0x0096, DRIVE_HI); | ||||
| 	/* 0x32 normal, 0x50 for Toshiba display */ | ||||
| 	mddi_writel(0x0050, DRIVE_LO); | ||||
| #endif | ||||
|  | ||||
| 	/* Need an even number for counts */ | ||||
| 	mddi_writel(0x60006, DRIVER_START_CNT); | ||||
| @@ -450,6 +469,9 @@ static void mddi_suspend(struct msm_mddi_client_data *cdata) | ||||
| 	mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); | ||||
| 	/* turn off the clock */ | ||||
| 	clk_disable(mddi->clk); | ||||
| #if CONFIG_MSM_MDP40 | ||||
| 	clk_disable(mdp_clk); | ||||
| #endif | ||||
| 	wake_unlock(&mddi->idle_lock); | ||||
| } | ||||
|  | ||||
| @@ -462,11 +484,18 @@ static void mddi_resume(struct msm_mddi_client_data *cdata) | ||||
| 	/* turn on the client */ | ||||
| 	if (mddi->power_client) | ||||
| 		mddi->power_client(&mddi->client_data, 1); | ||||
| #if CONFIG_MSM_MDP40 | ||||
| 	clk_enable(mdp_clk); | ||||
| #endif | ||||
| 	/* turn on the clock */ | ||||
| 	clk_enable(mddi->clk); | ||||
| 	/* set up the local registers */ | ||||
| 	mddi->rev_data_curr = 0; | ||||
| 	mddi_init_registers(mddi); | ||||
| /*	FIXME: Workaround for Novatek | ||||
| 	if (mddi->type == MSM_MDP_MDDI_TYPE_II) | ||||
| 		mddi_skew_calibration(mddi); | ||||
| */ | ||||
| 	mddi_writel(mddi->int_enable, INTEN); | ||||
| 	mddi_writel(MDDI_CMD_LINK_ACTIVE, CMD); | ||||
| 	mddi_writel(MDDI_CMD_SEND_RTD, CMD); | ||||
| @@ -493,37 +522,44 @@ static int __init mddi_get_client_caps(struct mddi_info *mddi) | ||||
|  | ||||
| 	mddi_writel(MDDI_CMD_LINK_ACTIVE, CMD); | ||||
| 	mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); | ||||
| 	/*FIXME: mddi host can't get caps on MDDI type 2*/ | ||||
| 	if (mddi->type == MSM_MDP_MDDI_TYPE_I) { | ||||
| 		for (j = 0; j < 3; j++) { | ||||
| 			/* the toshiba vga panel does not respond to get | ||||
| 			 * caps unless you SEND_RTD, but the first SEND_RTD | ||||
| 			 * will fail... | ||||
| 			 */ | ||||
| 			for (i = 0; i < 4; i++) { | ||||
| 				uint32_t stat; | ||||
|  | ||||
| 	for (j = 0; j < 3; j++) { | ||||
| 		/* the toshiba vga panel does not respond to get | ||||
| 		 * caps unless you SEND_RTD, but the first SEND_RTD | ||||
| 		 * will fail... | ||||
| 		 */ | ||||
| 		for (i = 0; i < 4; i++) { | ||||
| 			uint32_t stat; | ||||
| 				mddi_writel(MDDI_CMD_SEND_RTD, CMD); | ||||
| 				mdelay(1); | ||||
| 				mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); | ||||
| 				stat = mddi_readl(STAT); | ||||
| 				printk(KERN_INFO "mddi cmd send rtd: int %x, stat %x, " | ||||
| 						"rtd val %x\n", mddi_readl(INT), stat, | ||||
| 						mddi_readl(RTD_VAL)); | ||||
| 				if ((stat & MDDI_STAT_RTD_MEAS_FAIL) == 0) { | ||||
| 					mdelay(1); | ||||
| 					break; | ||||
| 				} | ||||
| 				msleep(1); | ||||
| 			} | ||||
|  | ||||
| 			mddi_writel(MDDI_CMD_SEND_RTD, CMD); | ||||
| 			mddi_writel(CMD_GET_CLIENT_CAP, CMD); | ||||
| 			mdelay(1); | ||||
| 			mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); | ||||
| 			stat = mddi_readl(STAT); | ||||
| 			printk(KERN_INFO "mddi cmd send rtd: int %x, stat %x, " | ||||
| 					"rtd val %x\n", mddi_readl(INT), stat, | ||||
| 					mddi_readl(RTD_VAL)); | ||||
| 			if ((stat & MDDI_STAT_RTD_MEAS_FAIL) == 0) | ||||
| 				break; | ||||
| 			msleep(1); | ||||
| 		} | ||||
|  | ||||
| 		mddi_writel(CMD_GET_CLIENT_CAP, CMD); | ||||
| 		mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); | ||||
| 		wait_event_timeout(mddi->int_wait, mddi->flags & FLAG_HAVE_CAPS, | ||||
| 			wait_event_timeout(mddi->int_wait, mddi->flags & FLAG_HAVE_CAPS, | ||||
| 				   HZ / 100); | ||||
|  | ||||
| 		if (mddi->flags & FLAG_HAVE_CAPS) | ||||
| 			break; | ||||
| 		printk(KERN_INFO KERN_ERR "mddi_init, timeout waiting for " | ||||
| 			if (mddi->flags & FLAG_HAVE_CAPS) | ||||
| 				break; | ||||
| 			printk(KERN_INFO KERN_ERR "mddi_init, timeout waiting for " | ||||
| 				"caps\n"); | ||||
| 	} | ||||
| 	return mddi->flags & FLAG_HAVE_CAPS; | ||||
| 		} | ||||
| 		return (mddi->flags & FLAG_HAVE_CAPS); | ||||
| 	} else | ||||
| 		return 1; | ||||
| } | ||||
|  | ||||
| /* link must be active when this is called */ | ||||
| @@ -564,53 +600,86 @@ int mddi_check_status(struct mddi_info *mddi) | ||||
| } | ||||
|  | ||||
|  | ||||
| void mddi_remote_write(struct msm_mddi_client_data *cdata, uint32_t val, | ||||
| 		       uint32_t reg) | ||||
| /* | ||||
|  * mddi_remote_write_vals - send the register access packet | ||||
|  * | ||||
|  * @cdata: mddi layer dedicated structure, holding info needed by mddi | ||||
|  * @val  : parameters | ||||
|  * @reg  : cmd | ||||
|  * @nr_bytes: size of parameters in bytes | ||||
|  * | ||||
|  * jay, Nov 13, 08' | ||||
|  * extend the single parameter to multiple. | ||||
|  */ | ||||
| void mddi_remote_write_vals(struct msm_mddi_client_data *cdata, uint8_t * val, | ||||
| 			uint32_t reg, unsigned int nr_bytes) | ||||
| { | ||||
| 	struct mddi_info *mddi = container_of(cdata, struct mddi_info, | ||||
| 					      client_data); | ||||
| 	struct mddi_llentry *ll; | ||||
| 	struct mddi_register_access *ra; | ||||
| 	/* unsigned s; */ | ||||
| 	dma_addr_t bus_addr = 0; | ||||
|  | ||||
| 	mutex_lock(&mddi->reg_write_lock); | ||||
|  | ||||
| 	ll = mddi->reg_write_data; | ||||
|  | ||||
| 	ra = &(ll->u.r); | ||||
| 	ra->length = 14 + 4; | ||||
| 	ra->length = 14 + nr_bytes; | ||||
| 	ra->type = TYPE_REGISTER_ACCESS; | ||||
| 	ra->client_id = 0; | ||||
| 	ra->read_write_info = MDDI_WRITE | 1; | ||||
| 	ra->read_write_info = MDDI_WRITE | (nr_bytes / 4); | ||||
| 	ra->crc16 = 0; | ||||
|  | ||||
| 	ra->register_address = reg; | ||||
| 	ra->register_data_list = val; | ||||
|  | ||||
| 	ll->flags = 1; | ||||
| 	/* register access packet header occupies 14 bytes */ | ||||
| 	ll->header_count = 14; | ||||
| 	ll->data_count = 4; | ||||
| 	ll->data = mddi->reg_write_addr + offsetof(struct mddi_llentry, | ||||
| 						   u.r.register_data_list); | ||||
| 	ll->data_count = nr_bytes; /* num of bytes in the data field */ | ||||
|  | ||||
| 	if (nr_bytes == 4) { | ||||
| 		uint32_t *prm = (uint32_t *)val; | ||||
|  | ||||
| 		ll->data = mddi->reg_write_addr + | ||||
| 			offsetof(struct mddi_llentry, u.r.u.reg_data); | ||||
| 		ra->u.reg_data = *prm; | ||||
| 	} else { | ||||
| 		int dma_retry = 5; | ||||
|  | ||||
| 		while (dma_retry--) { | ||||
| 			bus_addr = dma_map_single(NULL, (void *)val, nr_bytes, | ||||
| 					DMA_TO_DEVICE); | ||||
| 			if (dma_mapping_error(NULL, bus_addr) == 0) | ||||
| 				break; | ||||
| 			msleep(1); | ||||
| 		} | ||||
| 		if (dma_retry == 0) { | ||||
| 			printk(KERN_ERR "%s: dma map fail!\n", __func__); | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 		ll->data = bus_addr; | ||||
| 		ra->u.reg_data_list = (uint32_t *)bus_addr; | ||||
| 	} | ||||
| 	ll->next = 0; | ||||
| 	ll->reserved = 0; | ||||
|  | ||||
| 	/* s = mddi_readl(STAT); */ | ||||
| 	/* printk(KERN_INFO "mddi_remote_write(%x, %x), stat = %x\n", val, | ||||
| 	 * reg, s); */ | ||||
|  | ||||
| 	/* inform mddi to start */ | ||||
| 	mddi_writel(mddi->reg_write_addr, PRI_PTR); | ||||
|  | ||||
| 	/* s = mddi_readl(STAT); */ | ||||
| 	/* printk(KERN_INFO "mddi_remote_write(%x, %x) sent, stat = %x\n", | ||||
| 	 * val, reg, s); */ | ||||
|  | ||||
| 	mddi_wait_interrupt(mddi, MDDI_INT_PRI_LINK_LIST_DONE); | ||||
| 	/* printk(KERN_INFO "mddi_remote_write(%x, %x) done, stat = %x\n", | ||||
| 	 * val, reg, s); */ | ||||
| 	if (bus_addr) | ||||
| 		dma_unmap_single(NULL, bus_addr, nr_bytes, DMA_TO_DEVICE); | ||||
| 	mutex_unlock(&mddi->reg_write_lock); | ||||
| } | ||||
|  | ||||
| void mddi_remote_write(struct msm_mddi_client_data *cdata, uint32_t val, | ||||
| 			uint32_t reg) | ||||
| { | ||||
| 	uint8_t * p = (uint8_t *)&val; | ||||
| 	mddi_remote_write_vals(cdata, p, reg, 4); | ||||
| } | ||||
|  | ||||
| uint32_t mddi_remote_read(struct msm_mddi_client_data *cdata, uint32_t reg) | ||||
| { | ||||
| 	struct mddi_info *mddi = container_of(cdata, struct mddi_info, | ||||
| @@ -647,6 +716,7 @@ uint32_t mddi_remote_read(struct msm_mddi_client_data *cdata, uint32_t reg) | ||||
|  | ||||
| 	ri.reg = reg; | ||||
| 	ri.status = -1; | ||||
| 	ri.result = -1; | ||||
|  | ||||
| 	do { | ||||
| 		init_completion(&ri.done); | ||||
| @@ -711,7 +781,16 @@ static int __init mddi_clk_setup(struct platform_device *pdev, | ||||
| 				 unsigned long clk_rate) | ||||
| { | ||||
| 	int ret; | ||||
|  | ||||
| #ifdef CONFIG_MSM_MDP40 | ||||
| 	mdp_clk = clk_get(&pdev->dev, "mdp_clk"); | ||||
| 	if (IS_ERR(mdp_clk)) { | ||||
| 		printk(KERN_INFO "mddi: failed to get mdp clk"); | ||||
| 		return PTR_ERR(mdp_clk); | ||||
| 	} | ||||
| 	ret =  clk_enable(mdp_clk); | ||||
| 	if (ret) | ||||
| 		goto fail; | ||||
| #endif | ||||
| 	/* set up the clocks */ | ||||
| 	mddi->clk = clk_get(&pdev->dev, "mddi_clk"); | ||||
| 	if (IS_ERR(mddi->clk)) { | ||||
| @@ -724,6 +803,7 @@ static int __init mddi_clk_setup(struct platform_device *pdev, | ||||
| 	ret = clk_set_rate(mddi->clk, clk_rate); | ||||
| 	if (ret) | ||||
| 		goto fail; | ||||
| 	printk(KERN_DEBUG "mddi runs at %ld\n", clk_get_rate(mddi->clk)); | ||||
| 	return 0; | ||||
|  | ||||
| fail: | ||||
| @@ -751,6 +831,18 @@ static int __init mddi_rev_data_setup(struct mddi_info *mddi) | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static void mddi_skew_calibration(struct mddi_info *mddi) | ||||
| { | ||||
| 	struct msm_mddi_platform_data *pdata = mddi->client_pdev.dev.platform_data; | ||||
|  | ||||
| 	clk_set_rate( mddi->clk, 50000000); | ||||
| 	mdelay(1); | ||||
| 	mddi_writel(MDDI_CMD_SKEW_CALIBRATION, CMD); | ||||
| 	mdelay(1); | ||||
| 	clk_set_rate( mddi->clk, pdata->clk_rate); | ||||
| 	mdelay(1); | ||||
| } | ||||
|  | ||||
| static int __init mddi_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	struct msm_mddi_platform_data *pdata = pdev->dev.platform_data; | ||||
| @@ -779,6 +871,8 @@ static int __init mddi_probe(struct platform_device *pdev) | ||||
| 	printk(KERN_INFO "mddi: init() base=0x%p irq=%d\n", mddi->base, | ||||
| 	       mddi->irq); | ||||
| 	mddi->power_client = pdata->power_client; | ||||
| 	if (pdata->type != NULL) | ||||
| 		mddi->type = pdata->type; | ||||
|  | ||||
| 	mutex_init(&mddi->reg_write_lock); | ||||
| 	mutex_init(&mddi->reg_read_lock); | ||||
| @@ -811,9 +905,10 @@ static int __init mddi_probe(struct platform_device *pdev) | ||||
| 	} | ||||
|  | ||||
| 	/* turn on the mddi client bridge chip */ | ||||
| 	#if 0 /*advised by SKY*/ | ||||
| 	if (mddi->power_client) | ||||
| 		mddi->power_client(&mddi->client_data, 1); | ||||
|  | ||||
| 	#endif | ||||
| 	/* initialize the mddi registers */ | ||||
| 	mddi_set_auto_hibernate(&mddi->client_data, 0); | ||||
| 	mddi_writel(MDDI_CMD_RESET, CMD); | ||||
| @@ -834,11 +929,19 @@ static int __init mddi_probe(struct platform_device *pdev) | ||||
| 		printk(KERN_INFO "mddi powerdown: stat %x\n", mddi_readl(STAT)); | ||||
| 		msleep(100); | ||||
| 		printk(KERN_INFO "mddi powerdown: stat %x\n", mddi_readl(STAT)); | ||||
| 		return 0; | ||||
| 		goto dummy_client; | ||||
| 	} | ||||
|  | ||||
| 	mddi_set_auto_hibernate(&mddi->client_data, 1); | ||||
|  | ||||
| 	if (mddi->caps.Mfr_Name == 0 && mddi->caps.Product_Code == 0) | ||||
| 	/*  | ||||
| 	 * FIXME: User kernel defconfig to link dedicated mddi client driver. | ||||
| 	 */ | ||||
| #if 0 | ||||
| 	if ( mddi->caps.Mfr_Name == 0 && mddi->caps.Product_Code == 0) | ||||
| #else | ||||
| 	if (mddi->caps.Mfr_Name == 0 ) | ||||
| #endif | ||||
| 		pdata->fixup(&mddi->caps.Mfr_Name, &mddi->caps.Product_Code); | ||||
|  | ||||
| 	mddi->client_pdev.id = 0; | ||||
| @@ -856,8 +959,17 @@ static int __init mddi_probe(struct platform_device *pdev) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (i >= pdata->num_clients) | ||||
| 	if (i >= pdata->num_clients) { | ||||
| dummy_client: | ||||
| 		mddi->client_data.private_client_data = | ||||
| 			pdata->client_platform_data[0].client_data; | ||||
| 		mddi->client_pdev.name = | ||||
| 			pdata->client_platform_data[0].name; | ||||
| 		mddi->client_pdev.id = | ||||
| 			pdata->client_platform_data[0].id; | ||||
| 		mddi->client_pdev.name = "mddi_c_dummy"; | ||||
| 		clk_disable(mddi->clk); | ||||
| 	} | ||||
| 	printk(KERN_INFO "mddi: registering panel %s\n", | ||||
| 		mddi->client_pdev.name); | ||||
|  | ||||
| @@ -865,6 +977,7 @@ static int __init mddi_probe(struct platform_device *pdev) | ||||
| 	mddi->client_data.resume = mddi_resume; | ||||
| 	mddi->client_data.activate_link = mddi_activate_link; | ||||
| 	mddi->client_data.remote_write = mddi_remote_write; | ||||
| 	mddi->client_data.remote_write_vals = mddi_remote_write_vals; | ||||
| 	mddi->client_data.remote_read = mddi_remote_read; | ||||
| 	mddi->client_data.auto_hibernate = mddi_set_auto_hibernate; | ||||
| 	mddi->client_data.fb_resource = pdata->fb_resource; | ||||
|   | ||||
							
								
								
									
										267
									
								
								drivers/video/msm/mddi_client_epson.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										267
									
								
								drivers/video/msm/mddi_client_epson.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,267 @@ | ||||
| /* | ||||
|  * Copyright (C) 2008 HTC Corporation. | ||||
|  * | ||||
|  * This software is licensed under the terms of the GNU General Public | ||||
|  * License version 2, as published by the Free Software Foundation, and | ||||
|  * may be copied, distributed, and modified under those terms. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  */ | ||||
|  | ||||
| #include <linux/module.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/delay.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/gpio.h> | ||||
| #include <linux/wakelock.h> | ||||
| #include <mach/msm_fb.h> | ||||
|  | ||||
| static DECLARE_WAIT_QUEUE_HEAD(epson_vsync_wait); | ||||
|  | ||||
| struct panel_info { | ||||
| 	struct msm_mddi_client_data *client_data; | ||||
| 	struct platform_device pdev; | ||||
| 	struct msm_panel_data panel_data; | ||||
| 	struct msmfb_callback *epson_callback; | ||||
| 	struct wake_lock idle_lock; | ||||
| 	int epson_got_int; | ||||
| }; | ||||
|  | ||||
| static struct platform_device mddi_eps_cabc = { | ||||
| 	.name = "eps_cabc", | ||||
| 	.id = 0, | ||||
| }; | ||||
|  | ||||
| static void epson_request_vsync(struct msm_panel_data *panel_data, | ||||
| 				  struct msmfb_callback *callback) | ||||
| { | ||||
| 	struct panel_info *panel = container_of(panel_data, struct panel_info, | ||||
| 						panel_data); | ||||
| 	struct msm_mddi_client_data *client_data = panel->client_data; | ||||
|  | ||||
| 	panel->epson_callback = callback; | ||||
| 	if (panel->epson_got_int) { | ||||
| 		panel->epson_got_int = 0; | ||||
| 		client_data->activate_link(client_data); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void epson_clear_vsync(struct msm_panel_data *panel_data) | ||||
| { | ||||
| 	struct panel_info *panel = container_of(panel_data, struct panel_info, | ||||
| 						panel_data); | ||||
| 	struct msm_mddi_client_data *client_data = panel->client_data; | ||||
|  | ||||
| 	client_data->activate_link(client_data); | ||||
| } | ||||
|  | ||||
| static void epson_wait_vsync(struct msm_panel_data *panel_data) | ||||
| { | ||||
| 	struct panel_info *panel = container_of(panel_data, struct panel_info, | ||||
| 						panel_data); | ||||
| 	struct msm_mddi_client_data *client_data = panel->client_data; | ||||
|  | ||||
| 	if (panel->epson_got_int) { | ||||
| 		panel->epson_got_int = 0; | ||||
| 		client_data->activate_link(client_data); /* clears interrupt */ | ||||
| 	} | ||||
| 	if (wait_event_timeout(epson_vsync_wait, panel->epson_got_int, | ||||
| 				HZ/2) == 0) | ||||
| 		printk(KERN_ERR "timeout waiting for VSYNC\n"); | ||||
| 	panel->epson_got_int = 0; | ||||
| 	/* interrupt clears when screen dma starts */ | ||||
| } | ||||
|  | ||||
| static int epson_suspend(struct msm_panel_data *panel_data) | ||||
| { | ||||
| 	struct panel_info *panel = container_of(panel_data, struct panel_info, | ||||
| 						panel_data); | ||||
| 	struct msm_mddi_client_data *client_data = panel->client_data; | ||||
|  | ||||
| 	struct msm_mddi_bridge_platform_data *bridge_data = | ||||
| 		client_data->private_client_data; | ||||
| 	int ret; | ||||
|  | ||||
| 	wake_lock(&panel->idle_lock); | ||||
| 	ret = bridge_data->uninit(bridge_data, client_data); | ||||
| 	wake_unlock(&panel->idle_lock); | ||||
| 	if (ret) { | ||||
| 		printk(KERN_INFO "mddi epson client: non zero return from " | ||||
| 			"uninit\n"); | ||||
| 		return ret; | ||||
| 	} | ||||
| 	client_data->suspend(client_data); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int epson_resume(struct msm_panel_data *panel_data) | ||||
| { | ||||
| 	struct panel_info *panel = container_of(panel_data, struct panel_info, | ||||
| 						panel_data); | ||||
| 	struct msm_mddi_client_data *client_data = panel->client_data; | ||||
|  | ||||
| 	struct msm_mddi_bridge_platform_data *bridge_data = | ||||
| 		client_data->private_client_data; | ||||
| 	int ret; | ||||
|  | ||||
| 	wake_lock(&panel->idle_lock); | ||||
| 	client_data->resume(client_data); | ||||
| 	wake_unlock(&panel->idle_lock); | ||||
| 	ret = bridge_data->init(bridge_data, client_data); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int epson_blank(struct msm_panel_data *panel_data) | ||||
| { | ||||
| 	struct panel_info *panel = container_of(panel_data, struct panel_info, | ||||
| 						panel_data); | ||||
| 	struct msm_mddi_client_data *client_data = panel->client_data; | ||||
| 	struct msm_mddi_bridge_platform_data *bridge_data = | ||||
| 		client_data->private_client_data; | ||||
|  | ||||
| 	return bridge_data->blank(bridge_data, client_data); | ||||
| } | ||||
|  | ||||
| static int epson_unblank(struct msm_panel_data *panel_data) | ||||
| { | ||||
| 	struct panel_info *panel = container_of(panel_data, struct panel_info, | ||||
| 						panel_data); | ||||
| 	struct msm_mddi_client_data *client_data = panel->client_data; | ||||
| 	struct msm_mddi_bridge_platform_data *bridge_data = | ||||
| 		client_data->private_client_data; | ||||
|  | ||||
| 	return bridge_data->unblank(bridge_data, client_data); | ||||
| } | ||||
|  | ||||
| static irqreturn_t epson_vsync_interrupt(int irq, void *data) | ||||
| { | ||||
| 	struct panel_info *panel = data; | ||||
|  | ||||
| 	panel->epson_got_int = 1; | ||||
| 	if (panel->epson_callback) { | ||||
| 		panel->epson_callback->func(panel->epson_callback); | ||||
| 		panel->epson_callback = 0; | ||||
| 	} | ||||
| 	wake_up(&epson_vsync_wait); | ||||
| 	return IRQ_HANDLED; | ||||
| } | ||||
|  | ||||
| static int setup_vsync(struct panel_info *panel, | ||||
| 		       int init) | ||||
| { | ||||
| 	int ret; | ||||
| 	int gpio = 98; | ||||
| 	unsigned int irq; | ||||
|  | ||||
| 	if (!init) { | ||||
| 		ret = 0; | ||||
| 		goto uninit; | ||||
| 	} | ||||
| 	ret = gpio_request(gpio, "vsync"); | ||||
| 	if (ret) | ||||
| 		goto err_request_gpio_failed; | ||||
|  | ||||
| 	ret = gpio_direction_input(gpio); | ||||
| 	if (ret) | ||||
| 		goto err_gpio_direction_input_failed; | ||||
|  | ||||
| 	ret = irq = gpio_to_irq(gpio); | ||||
| 	if (ret < 0) | ||||
| 		goto err_get_irq_num_failed; | ||||
|  | ||||
| 	ret = request_irq(irq, epson_vsync_interrupt, IRQF_TRIGGER_FALLING, | ||||
| 			  "vsync", panel); | ||||
| 	if (ret) | ||||
| 		goto err_request_irq_failed; | ||||
| 	printk(KERN_INFO "vsync on gpio %d now %d\n", | ||||
| 	       gpio, gpio_get_value(gpio)); | ||||
| 	return 0; | ||||
|  | ||||
| uninit: | ||||
| 	free_irq(gpio_to_irq(gpio), panel); | ||||
| err_request_irq_failed: | ||||
| err_get_irq_num_failed: | ||||
| err_gpio_direction_input_failed: | ||||
| 	gpio_free(gpio); | ||||
| err_request_gpio_failed: | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| static int mddi_epson_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	int ret; | ||||
| 	struct msm_mddi_client_data *client_data = pdev->dev.platform_data; | ||||
| 	struct msm_mddi_bridge_platform_data *bridge_data = | ||||
| 		client_data->private_client_data; | ||||
| 	struct panel_data *panel_data = &bridge_data->panel_conf; | ||||
| 	struct panel_info *panel = | ||||
| 		kzalloc(sizeof(struct panel_info), GFP_KERNEL); | ||||
| 	if (!panel) | ||||
| 		return -ENOMEM; | ||||
| 	platform_set_drvdata(pdev, panel); | ||||
|  | ||||
| 	printk(KERN_DEBUG "%s\n", __func__); | ||||
|  | ||||
| 	if (panel_data->caps & MSMFB_CAP_CABC) { | ||||
| 		printk(KERN_INFO "CABC enabled\n"); | ||||
| 		mddi_eps_cabc.dev.platform_data = client_data; | ||||
| 		platform_device_register(&mddi_eps_cabc); | ||||
| 	} | ||||
|  | ||||
| 	ret = setup_vsync(panel, 1); | ||||
| 	if (ret) { | ||||
| 		dev_err(&pdev->dev, "mddi_bridge_setup_vsync failed\n"); | ||||
| 		return ret; | ||||
| 	} | ||||
|  | ||||
| 	panel->client_data = client_data; | ||||
| 	panel->panel_data.suspend = epson_suspend; | ||||
| 	panel->panel_data.resume = epson_resume; | ||||
| 	panel->panel_data.wait_vsync = epson_wait_vsync; | ||||
| 	panel->panel_data.request_vsync = epson_request_vsync; | ||||
| 	panel->panel_data.clear_vsync = epson_clear_vsync; | ||||
| 	panel->panel_data.blank = epson_blank; | ||||
| 	panel->panel_data.unblank = epson_unblank; | ||||
| 	panel->panel_data.fb_data =  &bridge_data->fb_data; | ||||
| 	panel->panel_data.caps = ~MSMFB_CAP_PARTIAL_UPDATES; | ||||
|  | ||||
| 	panel->pdev.name = "msm_panel"; | ||||
| 	panel->pdev.id = pdev->id; | ||||
| 	panel->pdev.resource = client_data->fb_resource; | ||||
| 	panel->pdev.num_resources = 1; | ||||
| 	panel->pdev.dev.platform_data = &panel->panel_data; | ||||
| 	platform_device_register(&panel->pdev); | ||||
| 	wake_lock_init(&panel->idle_lock, WAKE_LOCK_IDLE, "eps_idle_lock"); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int mddi_epson_remove(struct platform_device *pdev) | ||||
| { | ||||
| 	struct panel_info *panel = platform_get_drvdata(pdev); | ||||
|  | ||||
| 	setup_vsync(panel, 0); | ||||
| 	kfree(panel); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static struct platform_driver mddi_client_d263_0000 = { | ||||
| 	.probe = mddi_epson_probe, | ||||
| 	.remove = mddi_epson_remove, | ||||
| 	.driver = { .name = "mddi_c_4ca3_0000" }, | ||||
| }; | ||||
|  | ||||
| static int __init mddi_client_epson_init(void) | ||||
| { | ||||
| 	platform_driver_register(&mddi_client_d263_0000); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| module_init(mddi_client_epson_init); | ||||
|  | ||||
							
								
								
									
										269
									
								
								drivers/video/msm/mddi_client_novb9f6_5582.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										269
									
								
								drivers/video/msm/mddi_client_novb9f6_5582.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,269 @@ | ||||
| /* | ||||
|  * Copyright (C) 2008 HTC Corporation. | ||||
|  * | ||||
|  * This software is licensed under the terms of the GNU General Public | ||||
|  * License version 2, as published by the Free Software Foundation, and | ||||
|  * may be copied, distributed, and modified under those terms. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  */ | ||||
|  | ||||
| #include <linux/module.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/platform_device.h> | ||||
| #include <linux/delay.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/gpio.h> | ||||
| #include <linux/wakelock.h> | ||||
| #include <mach/msm_fb.h> | ||||
|  | ||||
| static DECLARE_WAIT_QUEUE_HEAD(novtec_vsync_wait); | ||||
|  | ||||
| struct panel_info { | ||||
| 	struct msm_mddi_client_data *client_data; | ||||
| 	struct platform_device pdev; | ||||
| 	struct msm_panel_data panel_data; | ||||
| 	struct msmfb_callback *novtec_callback; | ||||
| 	struct wake_lock idle_lock; | ||||
| 	int novtec_got_int; | ||||
| }; | ||||
|  | ||||
| static struct platform_device mddi_nov_cabc = { | ||||
| 	.name = "nov_cabc", | ||||
| 	.id = 0, | ||||
| }; | ||||
|  | ||||
| static void novtec_request_vsync(struct msm_panel_data *panel_data, | ||||
| 				  struct msmfb_callback *callback) | ||||
| { | ||||
| 	struct panel_info *panel = container_of(panel_data, struct panel_info, | ||||
| 						panel_data); | ||||
| 	struct msm_mddi_client_data *client_data = panel->client_data; | ||||
|  | ||||
| 	panel->novtec_callback = callback; | ||||
| 	if (panel->novtec_got_int) { | ||||
| 		panel->novtec_got_int = 0; | ||||
| 		client_data->activate_link(client_data); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void novtec_clear_vsync(struct msm_panel_data *panel_data) | ||||
| { | ||||
| 	struct panel_info *panel = container_of(panel_data, struct panel_info, | ||||
| 						panel_data); | ||||
| 	struct msm_mddi_client_data *client_data = panel->client_data; | ||||
|  | ||||
| 	client_data->activate_link(client_data); | ||||
| } | ||||
|  | ||||
| static void novtec_wait_vsync(struct msm_panel_data *panel_data) | ||||
| { | ||||
| 	struct panel_info *panel = container_of(panel_data, struct panel_info, | ||||
| 						panel_data); | ||||
| 	struct msm_mddi_client_data *client_data = panel->client_data; | ||||
|  | ||||
| 	if (panel->novtec_got_int) { | ||||
| 		panel->novtec_got_int = 0; | ||||
| 		client_data->activate_link(client_data); /* clears interrupt */ | ||||
| 	} | ||||
| 	if (wait_event_timeout(novtec_vsync_wait, panel->novtec_got_int, | ||||
| 				HZ/2) == 0) | ||||
| 		printk(KERN_ERR "timeout waiting for VSYNC\n"); | ||||
| 	panel->novtec_got_int = 0; | ||||
| 	/* interrupt clears when screen dma starts */ | ||||
| } | ||||
|  | ||||
| static int novtec_suspend(struct msm_panel_data *panel_data) | ||||
| { | ||||
| 	struct panel_info *panel = container_of(panel_data, struct panel_info, | ||||
| 						panel_data); | ||||
| 	struct msm_mddi_client_data *client_data = panel->client_data; | ||||
|  | ||||
| 	struct msm_mddi_bridge_platform_data *bridge_data = | ||||
| 		client_data->private_client_data; | ||||
| 	int ret; | ||||
|  | ||||
| 	wake_lock(&panel->idle_lock); | ||||
| 	ret = bridge_data->uninit(bridge_data, client_data); | ||||
| 	wake_unlock(&panel->idle_lock); | ||||
| 	if (ret) { | ||||
| 		printk(KERN_INFO "mddi novtec client: non zero return from " | ||||
| 			"uninit\n"); | ||||
| 		return ret; | ||||
| 	} | ||||
| 	client_data->suspend(client_data); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int novtec_resume(struct msm_panel_data *panel_data) | ||||
| { | ||||
| 	struct panel_info *panel = container_of(panel_data, struct panel_info, | ||||
| 						panel_data); | ||||
| 	struct msm_mddi_client_data *client_data = panel->client_data; | ||||
|  | ||||
| 	struct msm_mddi_bridge_platform_data *bridge_data = | ||||
| 		client_data->private_client_data; | ||||
| 	int ret; | ||||
|  | ||||
| 	wake_lock(&panel->idle_lock); | ||||
| 	client_data->resume(client_data); | ||||
| 	wake_unlock(&panel->idle_lock); | ||||
| 	ret = bridge_data->init(bridge_data, client_data); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int novtec_blank(struct msm_panel_data *panel_data) | ||||
| { | ||||
| 	struct panel_info *panel = container_of(panel_data, struct panel_info, | ||||
| 						panel_data); | ||||
| 	struct msm_mddi_client_data *client_data = panel->client_data; | ||||
| 	struct msm_mddi_bridge_platform_data *bridge_data = | ||||
| 		client_data->private_client_data; | ||||
|  | ||||
| 	return bridge_data->blank(bridge_data, client_data); | ||||
| } | ||||
|  | ||||
| static int novtec_unblank(struct msm_panel_data *panel_data) | ||||
| { | ||||
| 	struct panel_info *panel = container_of(panel_data, struct panel_info, | ||||
| 						panel_data); | ||||
| 	struct msm_mddi_client_data *client_data = panel->client_data; | ||||
| 	struct msm_mddi_bridge_platform_data *bridge_data = | ||||
| 		client_data->private_client_data; | ||||
|  | ||||
| 	return bridge_data->unblank(bridge_data, client_data); | ||||
| } | ||||
|  | ||||
| static irqreturn_t novtec_vsync_interrupt(int irq, void *data) | ||||
| { | ||||
| 	struct panel_info *panel = data; | ||||
|  | ||||
| 	panel->novtec_got_int = 1; | ||||
| 	if (panel->novtec_callback) { | ||||
| 		mdelay(3); | ||||
| 		panel->novtec_callback->func(panel->novtec_callback); | ||||
| 		panel->novtec_callback = 0; | ||||
| 	} | ||||
| 	wake_up(&novtec_vsync_wait); | ||||
| 	return IRQ_HANDLED; | ||||
| } | ||||
|  | ||||
| static int setup_vsync(struct panel_info *panel, | ||||
| 		       int init) | ||||
| { | ||||
| 	int ret; | ||||
| 	int gpio = 98; | ||||
| 	unsigned int irq; | ||||
|  | ||||
| 	if (!init) { | ||||
| 		ret = 0; | ||||
| 		goto uninit; | ||||
| 	} | ||||
| 	ret = gpio_request(gpio, "vsync"); | ||||
| 	if (ret) | ||||
| 		goto err_request_gpio_failed; | ||||
|  | ||||
| 	ret = gpio_direction_input(gpio); | ||||
| 	if (ret) | ||||
| 		goto err_gpio_direction_input_failed; | ||||
|  | ||||
| 	ret = irq = gpio_to_irq(gpio); | ||||
| 	if (ret < 0) | ||||
| 		goto err_get_irq_num_failed; | ||||
|  | ||||
| 	ret = request_irq(irq, novtec_vsync_interrupt, IRQF_TRIGGER_FALLING, | ||||
| 			  "vsync", panel); | ||||
| 	if (ret) | ||||
| 		goto err_request_irq_failed; | ||||
| 	printk(KERN_INFO "vsync on gpio %d now %d\n", | ||||
| 	       gpio, gpio_get_value(gpio)); | ||||
| 	return 0; | ||||
|  | ||||
| uninit: | ||||
| 	free_irq(gpio_to_irq(gpio), panel); | ||||
| err_request_irq_failed: | ||||
| err_get_irq_num_failed: | ||||
| err_gpio_direction_input_failed: | ||||
| 	gpio_free(gpio); | ||||
| err_request_gpio_failed: | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| static int mddi_novtec_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	int ret; | ||||
| 	struct msm_mddi_client_data *client_data = pdev->dev.platform_data; | ||||
| 	struct msm_mddi_bridge_platform_data *bridge_data = | ||||
| 		client_data->private_client_data; | ||||
| 	struct panel_data *panel_data = &bridge_data->panel_conf; | ||||
| 	struct panel_info *panel = | ||||
| 		kzalloc(sizeof(struct panel_info), GFP_KERNEL); | ||||
|  | ||||
| 	if (!panel) | ||||
| 		return -ENOMEM; | ||||
| 	platform_set_drvdata(pdev, panel); | ||||
|  | ||||
| 	printk(KERN_DEBUG "%s\n", __func__); | ||||
|  | ||||
| 	if (panel_data->caps & MSMFB_CAP_CABC) { | ||||
| 		printk(KERN_INFO "CABC enabled\n"); | ||||
| 		mddi_nov_cabc.dev.platform_data = client_data; | ||||
| 		platform_device_register(&mddi_nov_cabc); | ||||
| 	} | ||||
|  | ||||
| 	ret = setup_vsync(panel, 1); | ||||
| 	if (ret) { | ||||
| 		dev_err(&pdev->dev, "mddi_bridge_setup_vsync failed\n"); | ||||
| 		return ret; | ||||
| 	} | ||||
|  | ||||
| 	panel->client_data = client_data; | ||||
| 	panel->panel_data.suspend = novtec_suspend; | ||||
| 	panel->panel_data.resume = novtec_resume; | ||||
| 	panel->panel_data.wait_vsync = novtec_wait_vsync; | ||||
| 	panel->panel_data.request_vsync = novtec_request_vsync; | ||||
| 	panel->panel_data.clear_vsync = novtec_clear_vsync; | ||||
| 	panel->panel_data.blank = novtec_blank; | ||||
| 	panel->panel_data.unblank = novtec_unblank; | ||||
| 	panel->panel_data.fb_data =  &bridge_data->fb_data; | ||||
| 	panel->panel_data.caps = MSMFB_CAP_PARTIAL_UPDATES; | ||||
|  | ||||
| 	panel->pdev.name = "msm_panel"; | ||||
| 	panel->pdev.id = pdev->id; | ||||
| 	panel->pdev.resource = client_data->fb_resource; | ||||
| 	panel->pdev.num_resources = 1; | ||||
| 	panel->pdev.dev.platform_data = &panel->panel_data; | ||||
| 	platform_device_register(&panel->pdev); | ||||
| 	wake_lock_init(&panel->idle_lock, WAKE_LOCK_IDLE, "nov_idle_lock"); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int mddi_novtec_remove(struct platform_device *pdev) | ||||
| { | ||||
| 	struct panel_info *panel = platform_get_drvdata(pdev); | ||||
|  | ||||
| 	setup_vsync(panel, 0); | ||||
| 	kfree(panel); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static struct platform_driver mddi_client_d263_0000 = { | ||||
| 	.probe = mddi_novtec_probe, | ||||
| 	.remove = mddi_novtec_remove, | ||||
| 	.driver = { .name = "mddi_c_b9f6_5582" }, | ||||
| }; | ||||
|  | ||||
| static int __init mddi_client_novtec_init(void) | ||||
| { | ||||
| 	platform_driver_register(&mddi_client_d263_0000); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| module_init(mddi_client_novtec_init); | ||||
|  | ||||
| @@ -53,6 +53,8 @@ | ||||
| #define MDDI_MF_CNT             0x0084 | ||||
| #define MDDI_CURR_REV_PTR       0x0088 | ||||
| #define MDDI_CORE_VER           0x008c | ||||
| #define MDDI_PAD_IO_CTL		0x00a0 | ||||
| #define MDDI_PAD_CAL		0x00a4 | ||||
|  | ||||
| #define MDDI_INT_PRI_PTR_READ       0x0001 | ||||
| #define MDDI_INT_SEC_PTR_READ       0x0002 | ||||
| @@ -112,9 +114,7 @@ | ||||
| #define MDDI_CMD_LINK_ACTIVE         0x0900 | ||||
| #define MDDI_CMD_PERIODIC_REV_ENCAP  0x0A00 | ||||
| #define MDDI_CMD_FORCE_NEW_REV_PTR   0x0C00 | ||||
|  | ||||
|  | ||||
|  | ||||
| #define MDDI_CMD_SKEW_CALIBRATION    0x0D00 | ||||
| #define MDDI_VIDEO_REV_PKT_SIZE              0x40 | ||||
| #define MDDI_CLIENT_CAPABILITY_REV_PKT_SIZE  0x60 | ||||
| #define MDDI_MAX_REV_PKT_SIZE                0x60 | ||||
| @@ -125,9 +125,17 @@ | ||||
| /* MDP sends 256 pixel packets, so lower value hibernates more without | ||||
|  * significantly increasing latency of waiting for next subframe */ | ||||
| #define MDDI_HOST_BYTES_PER_SUBFRAME  0x3C00 | ||||
| #if defined (CONFIG_ARCH_QSD8X50) || defined (CONFIG_ARCH_MSM7X30) | ||||
| #define MDDI_HOST_TA2_LEN       0x001a | ||||
| #else | ||||
| #define MDDI_HOST_TA2_LEN       0x000c | ||||
| #define MDDI_HOST_REV_RATE_DIV  0x0002 | ||||
| #endif | ||||
|  | ||||
| #if defined (CONFIG_ARCH_QSD8X50) || defined (CONFIG_ARCH_MSM7X30) | ||||
| #define MDDI_HOST_REV_RATE_DIV  0x0004 | ||||
| #else | ||||
| #define MDDI_HOST_REV_RATE_DIV  0x0002 | ||||
| #endif | ||||
|  | ||||
| struct __attribute__((packed)) mddi_rev_packet { | ||||
| 	uint16_t length; | ||||
| @@ -284,8 +292,12 @@ struct __attribute__((packed)) mddi_register_access { | ||||
|  | ||||
| 	uint16_t crc16; | ||||
|  | ||||
| 	uint32_t register_data_list; | ||||
| 	/* list of 4-byte register data values for/from client registers */ | ||||
| 	union { | ||||
| 		uint32_t reg_data; | ||||
| 		uint32_t *reg_data_list; | ||||
| 	} u; | ||||
|  | ||||
| 	uint16_t crc_data; | ||||
| }; | ||||
|  | ||||
| struct __attribute__((packed)) mddi_llentry { | ||||
|   | ||||
| @@ -17,7 +17,6 @@ | ||||
|  | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/fb.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/msm_mdp.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/wait.h> | ||||
| @@ -29,11 +28,11 @@ | ||||
|  | ||||
| #include <mach/msm_iomap.h> | ||||
| #include <mach/msm_fb.h> | ||||
| #include <asm/mach-types.h> | ||||
| #include <linux/platform_device.h> | ||||
|  | ||||
| #include "mdp_hw.h" | ||||
| #include "mdp_ppp.h" | ||||
| #include <asm/mach-types.h> | ||||
|  | ||||
| struct class *mdp_class; | ||||
|  | ||||
| @@ -42,6 +41,18 @@ struct class *mdp_class; | ||||
| static DECLARE_WAIT_QUEUE_HEAD(mdp_ppp_waitqueue); | ||||
| static unsigned int mdp_irq_mask; | ||||
| struct clk *mdp_clk_to_disable_later = 0; | ||||
| static struct  mdp_blit_req *timeout_req; | ||||
| #ifdef CONFIG_FB_MSM_OVERLAY | ||||
| extern int mdp4_overlay_get(struct mdp_device *mdp_dev, struct fb_info *info, struct mdp_overlay *req); | ||||
| extern int mdp4_overlay_set(struct mdp_device *mdp_dev, struct fb_info *info, struct mdp_overlay *req); | ||||
| extern int mdp4_overlay_unset(struct mdp_device *mdp_dev, struct fb_info *info, int ndx); | ||||
| extern int mdp4_overlay_play(struct mdp_device *mdp_dev, struct fb_info *info, struct msmfb_overlay_data *req, | ||||
| 				struct file **pp_src_file); | ||||
| extern void mdp4_mddi_overlay(void *priv, uint32_t addr, uint32_t stride, | ||||
| 			    uint32_t width, uint32_t height, uint32_t x, | ||||
| 			    uint32_t y); | ||||
| #include "mdp4.h" | ||||
| #endif | ||||
| DEFINE_MUTEX(mdp_mutex); | ||||
|  | ||||
| static int locked_enable_mdp_irq(struct mdp_info *mdp, uint32_t mask) | ||||
| @@ -51,16 +62,14 @@ static int locked_enable_mdp_irq(struct mdp_info *mdp, uint32_t mask) | ||||
| 	/* if the mask bits are already set return an error, this interrupt | ||||
| 	 * is already enabled */ | ||||
| 	if (mdp_irq_mask & mask) { | ||||
| //		pr_err("mdp irq already on %x %x\n", mdp_irq_mask, mask); | ||||
| 		pr_err("mdp irq already on %x %x\n", mdp_irq_mask, mask); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	/* if the mdp irq is not already enabled enable it */ | ||||
| 	if (!mdp_irq_mask) { | ||||
|  | ||||
| 		clk_set_rate(mdp->ebi1_clk, 128000000); | ||||
| 		clk_enable(mdp->clk); | ||||
|  | ||||
| 		enable_irq(mdp->irq); | ||||
| 		clk_set_rate(mdp->ebi1_clk, 128000000); | ||||
| 	} | ||||
|  | ||||
| 	/* clear out any previous irqs for the requested mask*/ | ||||
| @@ -86,8 +95,6 @@ static int enable_mdp_irq(struct mdp_info *mdp, uint32_t mask) | ||||
|  | ||||
| static int locked_disable_mdp_irq(struct mdp_info *mdp, uint32_t mask) | ||||
| { | ||||
|         if (!mask) return 1; | ||||
|  | ||||
| 	/* this interrupt is already disabled! */ | ||||
| 	if (!(mdp_irq_mask & mask)) { | ||||
| 		printk(KERN_ERR "mdp irq already off %x %x\n", | ||||
| @@ -102,11 +109,9 @@ static int locked_disable_mdp_irq(struct mdp_info *mdp, uint32_t mask) | ||||
| 	/* if no one is waiting on the interrupt, disable it */ | ||||
| 	if (!mdp_irq_mask) { | ||||
| 		disable_irq_nosync(mdp->irq); | ||||
|  | ||||
| 		if (mdp->clk) | ||||
| 			clk_disable(mdp->clk); | ||||
| 		clk_set_rate(mdp->ebi1_clk, 0); | ||||
|  | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| @@ -133,13 +138,21 @@ static irqreturn_t mdp_isr(int irq, void *data) | ||||
|  | ||||
| 	status = mdp_readl(mdp, MDP_INTR_STATUS); | ||||
| 	mdp_writel(mdp, status, MDP_INTR_CLEAR); | ||||
| 	 | ||||
|  | ||||
|  | ||||
| #if defined(CONFIG_MACH_HTCLEO) | ||||
| 	status &= ~0x10000; // Cotulla | ||||
| #endif | ||||
|  | ||||
| //	pr_info("%s: status=%08x (irq_mask=%08x)\n", __func__, status, | ||||
| //		mdp_irq_mask); | ||||
| 	status &= mdp_irq_mask; | ||||
|  | ||||
| #ifdef CONFIG_MSM_MDP40 | ||||
| 	if (mdp->mdp_dev.overrides & MSM_MDP4_MDDI_DMA_SWITCH) { | ||||
| 		if(status && mdp->out_if[MSM_MDDI_PMDH_INTERFACE].dma_cb != NULL) | ||||
| 			status |= (INTR_OVERLAY0_DONE | MDP_DMA_S_DONE); | ||||
| 	} | ||||
| #endif | ||||
| 	for (i = 0; i < MSM_MDP_NUM_INTERFACES; ++i) { | ||||
| 		struct mdp_out_interface *out_if = &mdp->out_if[i]; | ||||
| 		if (status & out_if->dma_mask) { | ||||
| @@ -182,20 +195,16 @@ static int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq) | ||||
| 	unsigned long irq_flags; | ||||
|  | ||||
| //	pr_info("%s: WAITING for 0x%x\n", __func__, mask); | ||||
| 	wait_event_timeout(*wq, !mdp_check_mask(mdp, mask), HZ); // Cotulla TEST | ||||
| 	wait_event_timeout(*wq, !mdp_check_mask(mdp, mask), HZ); | ||||
|  | ||||
| 	spin_lock_irqsave(&mdp->lock, irq_flags); | ||||
| 	if (mdp_irq_mask & mask) { | ||||
| 		locked_disable_mdp_irq(mdp, mask); | ||||
| 		pr_warning("%s: timeout waiting for mdp to complete 0x%x\n", | ||||
| 			   __func__, mask); | ||||
| 		printk("GLBL_CLK_ENA: %08X\n", readl(MSM_CLK_CTL_BASE + 0x0000)); | ||||
| 		printk("GLBL_CLK_STATE: %08X\n", readl(MSM_CLK_CTL_BASE + 0x0004)); | ||||
| 		printk("GLBL_SLEEP_EN: %08X\n", readl(MSM_CLK_CTL_BASE + 0x001C)); | ||||
| 		printk("GLBL_CLK_ENA_2: %08X\n", readl(MSM_CLK_CTL_BASE + 0x0220)); | ||||
| 		printk("GLBL_CLK_STATE_2: %08X\n", readl(MSM_CLK_CTL_BASE + 0x0224)); | ||||
| 		printk("GLBL_CLK_SLEEP_EN_2: %08X\n", readl(MSM_CLK_CTL_BASE + 0x023C)); | ||||
| 		mdp_ppp_dump_debug(mdp); | ||||
| 		locked_disable_mdp_irq(mdp, mask); | ||||
| 	if(timeout_req) | ||||
| 		mdp_dump_blit(timeout_req); | ||||
|  | ||||
| 		ret = -ETIMEDOUT; | ||||
| 	} else { | ||||
| //		pr_info("%s: SUCCESS waiting for 0x%x\n", __func__, mask); | ||||
| @@ -217,6 +226,7 @@ void mdp_dma_wait(struct mdp_device *mdp_dev, int interface) | ||||
| 	case MSM_MDDI_PMDH_INTERFACE: | ||||
| 	case MSM_MDDI_EMDH_INTERFACE: | ||||
| 	case MSM_LCDC_INTERFACE: | ||||
| 	case MSM_TV_INTERFACE: | ||||
| 		BUG_ON(!mdp->out_if[interface].registered); | ||||
| 		mask = mdp->out_if[interface].dma_mask; | ||||
| 		wq = &mdp->out_if[interface].dma_waitqueue; | ||||
| @@ -243,15 +253,15 @@ static int mdp_ppp_wait(struct mdp_info *mdp) | ||||
| 	return mdp_wait(mdp, DL0_ROI_DONE, &mdp_ppp_waitqueue); | ||||
| } | ||||
|  | ||||
| static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride, | ||||
| 			    uint32_t width, uint32_t height, uint32_t x, | ||||
| 			    uint32_t y) | ||||
| static void mdp_dmas_to_mddi(void *priv, uint32_t addr, uint32_t stride, | ||||
| 		uint32_t width, uint32_t height, uint32_t x, uint32_t y) | ||||
| { | ||||
| 	struct mdp_info *mdp = priv; | ||||
| 	uint32_t dma2_cfg; | ||||
| 	uint16_t ld_param = 0; /* 0=PRIM, 1=SECD, 2=EXT */ | ||||
| 	uint32_t video_packet_parameter; | ||||
| 	uint16_t ld_param = 1; | ||||
|  | ||||
|  | ||||
| 	printk("MDDI start\n"); | ||||
| 	if(machine_is_htcleo()) { | ||||
| 		dma2_cfg = DMA_PACK_ALIGN_MSB | | ||||
| 			DMA_PACK_PATTERN_RGB; | ||||
| @@ -261,30 +271,120 @@ static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride, | ||||
| 		dma2_cfg |= DMA_OUT_SEL_LCDC; | ||||
|  | ||||
| 		dma2_cfg |= DMA_IBUF_FORMAT_RGB565; | ||||
|  | ||||
| 		/* 655 16BPP */ | ||||
| 		dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS; | ||||
| 	} | ||||
| 	else { | ||||
| 		 | ||||
| 	} else { | ||||
| 		dma2_cfg = DMA_PACK_TIGHT | | ||||
| 			DMA_PACK_ALIGN_LSB | | ||||
| 			DMA_PACK_PATTERN_RGB | | ||||
| 			DMA_OUT_SEL_AHB | | ||||
| 			DMA_IBUF_NONCONTIGUOUS; | ||||
|  | ||||
| 		dma2_cfg |= mdp->format; | ||||
|  | ||||
| 		dma2_cfg |= DMA_OUT_SEL_MDDI; | ||||
| 	#if defined CONFIG_MSM_MDP22 || defined CONFIG_MSM_MDP30 | ||||
| 		if (mdp->format == DMA_IBUF_FORMAT_RGB888_OR_ARGB8888) | ||||
| 	#else | ||||
| 		if (mdp->format == DMA_IBUF_FORMAT_XRGB8888) | ||||
| 	#endif | ||||
| 			dma2_cfg |= DMA_PACK_PATTERN_BGR; | ||||
| 		else | ||||
| 			dma2_cfg |= DMA_PACK_PATTERN_RGB; | ||||
|  | ||||
| 		dma2_cfg |= DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY; | ||||
| 			dma2_cfg |= DMA_OUT_SEL_MDDI; | ||||
|  | ||||
| 		dma2_cfg |= DMA_DITHER_EN; | ||||
| 			dma2_cfg |= DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY; | ||||
|  | ||||
| 		/* 666 18BPP */ | ||||
| 		dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; | ||||
| 			dma2_cfg |= DMA_DITHER_EN; | ||||
| 	} | ||||
|  | ||||
| #ifdef CONFIG_MSM_MDP22 | ||||
| 	if (mdp->mdp_dev.color_format == MSM_MDP_OUT_IF_FMT_RGB565) { | ||||
| 		dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS; | ||||
| 		video_packet_parameter = MDDI_VDO_PACKET_DESC_RGB565; | ||||
| 	} else if (mdp->mdp_dev.color_format == MSM_MDP_OUT_IF_FMT_RGB666) { | ||||
| 		dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; | ||||
| 		video_packet_parameter = MDDI_VDO_PACKET_DESC_RGB666; | ||||
| 	} | ||||
|  | ||||
| 	/* setup size, address, and stride */ | ||||
| 	mdp_writel(mdp, (height << 16) | (width), MDP_DMA_S_SIZE); | ||||
| 	mdp_writel(mdp, addr, MDP_DMA_S_IBUF_ADDR); | ||||
| 	mdp_writel(mdp, stride, MDP_DMA_S_IBUF_Y_STRIDE); | ||||
|  | ||||
| 	/* set y & x offset and MDDI transaction parameters */ | ||||
| 	mdp_writel(mdp, (y << 16) | (x), MDP_DMA_S_OUT_XY); | ||||
| 	mdp_writel(mdp, ld_param, MDP_MDDI_PARAM_WR_SEL); | ||||
| 	if (mdp->mdp_dev.overrides & MSM_MDP_PANEL_IGNORE_PIXEL_DATA) { | ||||
| 		mdp_writel(mdp, (video_packet_parameter << 16) | 0xE3, | ||||
| 			MDP_MDDI_PARAM); | ||||
| 	} | ||||
| 	else { | ||||
| 		mdp_writel(mdp, (video_packet_parameter << 16) | MDDI_VDO_PACKET_PRIM, | ||||
| 			MDP_MDDI_PARAM); | ||||
| 	} | ||||
|  | ||||
| 	mdp_writel(mdp, dma2_cfg, MDP_DMA_S_CONFIG); | ||||
| 	mdp_writel(mdp, 0, MDP_DMA_S_START); | ||||
| } | ||||
|  | ||||
| static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride, | ||||
| 			    uint32_t width, uint32_t height, uint32_t x, | ||||
| 			    uint32_t y) | ||||
| { | ||||
| 	struct mdp_info *mdp = priv; | ||||
| 	uint32_t dma2_cfg = 0; | ||||
| 	uint32_t video_packet_parameter = 0; | ||||
| 	uint16_t ld_param = 0; /* 0=PRIM, 1=SECD, 2=EXT */ | ||||
|  | ||||
| #if !defined(CONFIG_MSM_MDP30) | ||||
| 	dma2_cfg = DMA_PACK_TIGHT | | ||||
| 		DMA_PACK_ALIGN_LSB | | ||||
| 		DMA_OUT_SEL_AHB | | ||||
| 		DMA_IBUF_NONCONTIGUOUS; | ||||
|  | ||||
| #endif | ||||
| 	dma2_cfg |= mdp->format; | ||||
|  | ||||
| #if defined CONFIG_MSM_MDP22 || defined CONFIG_MSM_MDP30 | ||||
| 	if (mdp->format == DMA_IBUF_FORMAT_RGB888_OR_ARGB8888) | ||||
| #else | ||||
| 	if (mdp->format == DMA_IBUF_FORMAT_XRGB8888) | ||||
| #endif | ||||
| 		dma2_cfg |= DMA_PACK_PATTERN_BGR; | ||||
| 	else | ||||
| 		dma2_cfg |= DMA_PACK_PATTERN_RGB; | ||||
|  | ||||
| 	dma2_cfg |= DMA_OUT_SEL_MDDI; | ||||
|  | ||||
| 	dma2_cfg |= DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY; | ||||
|  | ||||
| #if !defined(CONFIG_MSM_MDP30) | ||||
| 	dma2_cfg |= DMA_DITHER_EN; | ||||
| #endif | ||||
|  | ||||
| 	if (mdp->mdp_dev.color_format == MSM_MDP_OUT_IF_FMT_RGB565) { | ||||
| 		dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS; | ||||
| 		video_packet_parameter = MDDI_VDO_PACKET_DESC_RGB565; | ||||
| 	} else if (mdp->mdp_dev.color_format == MSM_MDP_OUT_IF_FMT_RGB666) { | ||||
| 		dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; | ||||
| 		video_packet_parameter = MDDI_VDO_PACKET_DESC_RGB666; | ||||
| 	} | ||||
|  | ||||
|  | ||||
| #if defined(CONFIG_MSM_MDP30) || defined(CONFIG_MSM_MDP302) | ||||
| 	writel(height << 16 | width, mdp->base + 0x90004); | ||||
| 	writel(addr, mdp->base + 0x90008); | ||||
| 	writel(stride, mdp->base + 0x9000c); | ||||
|  | ||||
| 	/* set y & x offset and MDDI transaction parameters */ | ||||
| 	writel(y << 16 | x, mdp->base + 0x90010); | ||||
| 	writel(ld_param, mdp->base + 0x00090); | ||||
| 	writel((video_packet_parameter << 16) | MDDI_VDO_PACKET_PRIM, | ||||
| 		mdp->base + 0x00094); | ||||
|  | ||||
| 	writel(dma2_cfg, mdp->base + 0x90000); | ||||
|  | ||||
| 	/* start DMA2 */ | ||||
| 	writel(0, mdp->base + 0x0044); | ||||
| #elif defined(CONFIG_MSM_MDP22) | ||||
| 	/* setup size, address, and stride */ | ||||
| 	mdp_writel(mdp, (height << 16) | (width), | ||||
| 		   MDP_CMD_DEBUG_ACCESS_BASE + 0x0184); | ||||
| @@ -294,7 +394,7 @@ static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride, | ||||
| 	/* set y & x offset and MDDI transaction parameters */ | ||||
| 	mdp_writel(mdp, (y << 16) | (x), MDP_CMD_DEBUG_ACCESS_BASE + 0x0194); | ||||
| 	mdp_writel(mdp, ld_param, MDP_CMD_DEBUG_ACCESS_BASE + 0x01a0); | ||||
| 	mdp_writel(mdp, (MDDI_VDO_PACKET_DESC << 16) | MDDI_VDO_PACKET_PRIM, | ||||
| 	mdp_writel(mdp, (video_packet_parameter << 16) | MDDI_VDO_PACKET_PRIM, | ||||
| 		   MDP_CMD_DEBUG_ACCESS_BASE + 0x01a4); | ||||
|  | ||||
| 	mdp_writel(mdp, dma2_cfg, MDP_CMD_DEBUG_ACCESS_BASE + 0x0180); | ||||
| @@ -310,7 +410,7 @@ static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride, | ||||
| 	/* set y & x offset and MDDI transaction parameters */ | ||||
| 	mdp_writel(mdp, (y << 16) | (x), MDP_DMA_P_OUT_XY); | ||||
| 	mdp_writel(mdp, ld_param, MDP_MDDI_PARAM_WR_SEL); | ||||
| 	mdp_writel(mdp, (MDDI_VDO_PACKET_DESC << 16) | MDDI_VDO_PACKET_PRIM, | ||||
| 	mdp_writel(mdp, (video_packet_parameter << 16) | MDDI_VDO_PACKET_PRIM, | ||||
| 		   MDP_MDDI_PARAM); | ||||
|  | ||||
| 	mdp_writel(mdp, dma2_cfg, MDP_DMA_P_CONFIG); | ||||
| @@ -326,7 +426,7 @@ void mdp_dma(struct mdp_device *mdp_dev, uint32_t addr, uint32_t stride, | ||||
| 	struct mdp_out_interface *out_if; | ||||
| 	unsigned long flags; | ||||
|  | ||||
| 	if (interface < 0 || interface > MSM_MDP_NUM_INTERFACES || | ||||
| 	if (interface < 0 || interface >= MSM_MDP_NUM_INTERFACES || | ||||
| 	    !mdp->out_if[interface].registered) { | ||||
| 		pr_err("%s: Unknown interface: %d\n", __func__, interface); | ||||
| 		BUG(); | ||||
| @@ -335,7 +435,7 @@ void mdp_dma(struct mdp_device *mdp_dev, uint32_t addr, uint32_t stride, | ||||
|  | ||||
| 	spin_lock_irqsave(&mdp->lock, flags); | ||||
| 	if (locked_enable_mdp_irq(mdp, out_if->dma_mask)) { | ||||
| //		pr_err("%s: busy\n", __func__); | ||||
| 		pr_err("%s: busy\n", __func__); | ||||
| 		goto done; | ||||
| 	} | ||||
|  | ||||
| @@ -417,14 +517,14 @@ int mdp_check_output_format(struct mdp_device *mdp_dev, int bpp) | ||||
| int mdp_set_output_format(struct mdp_device *mdp_dev, int bpp) | ||||
| { | ||||
| 	struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); | ||||
| 	uint32_t format, pack_pattern; | ||||
| 	uint32_t format, pack_pattern = DMA_PACK_PATTERN_RGB; | ||||
|  | ||||
| 	switch (bpp) { | ||||
| 	case 16: | ||||
| 		format = DMA_IBUF_FORMAT_RGB565; | ||||
| 		pack_pattern = DMA_PACK_PATTERN_RGB; | ||||
| 		break; | ||||
| #ifdef CONFIG_MSM_MDP22 | ||||
| #if defined CONFIG_MSM_MDP22 || defined CONFIG_MSM_MDP30 | ||||
| 	case 24: | ||||
| 	case 32: | ||||
| 		format = DMA_IBUF_FORMAT_RGB888_OR_ARGB8888; | ||||
| @@ -480,8 +580,8 @@ static void dump_req(struct mdp_blit_req *req, | ||||
| } | ||||
|  | ||||
| int mdp_blit_and_wait(struct mdp_info *mdp, struct mdp_blit_req *req, | ||||
| 	struct file *src_file, unsigned long src_start, unsigned long src_len, | ||||
| 	struct file *dst_file, unsigned long dst_start, unsigned long dst_len) | ||||
| 		struct file *src_file, unsigned long src_start, unsigned long src_len, | ||||
| 		struct file *dst_file, unsigned long dst_start, unsigned long dst_len) | ||||
| { | ||||
| 	int ret; | ||||
| 	enable_mdp_irq(mdp, DL0_ROI_DONE); | ||||
| @@ -495,17 +595,13 @@ int mdp_blit_and_wait(struct mdp_info *mdp, struct mdp_blit_req *req, | ||||
| 	ret = mdp_ppp_wait(mdp); | ||||
| 	if (unlikely(ret)) { | ||||
| 		printk(KERN_ERR "%s: failed!\n", __func__); | ||||
| //		pr_err("original request:\n"); | ||||
| //		dump_req(mdp->req, src_start, src_len, dst_start, dst_len); | ||||
| 		pr_err("original request:\n"); | ||||
| 		dump_req(mdp->req, src_start, src_len, dst_start, dst_len); | ||||
| 		pr_err("dead request:\n"); | ||||
| 		dump_req(req, src_start, src_len, dst_start, dst_len); | ||||
| 		//BUG(); | ||||
| 		BUG(); | ||||
| 		return ret; | ||||
| 	} | ||||
| // Cotulla: Disable each request dump to prevent spam in logs | ||||
| //	pr_err("good request:\n"); | ||||
| //	dump_req(req, src_start, src_len, dst_start, dst_len); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| @@ -517,9 +613,7 @@ int mdp_blit(struct mdp_device *mdp_dev, struct fb_info *fb, | ||||
| 	struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); | ||||
| 	struct file *src_file = 0, *dst_file = 0; | ||||
|  | ||||
| 	printk("+MDP_BLIT: %08X\n", req->flags); | ||||
|  | ||||
| #ifdef CONFIG_MSM_MDP31 | ||||
| #if defined(CONFIG_MSM_MDP31) || defined(CONFIG_MSM_MDP302) | ||||
| 	if (req->flags & MDP_ROT_90) { | ||||
| 		if (unlikely(((req->dst_rect.h == 1) && | ||||
| 			((req->src_rect.w != 1) || | ||||
| @@ -567,10 +661,11 @@ int mdp_blit(struct mdp_device *mdp_dev, struct fb_info *fb, | ||||
| 	} | ||||
| 	mutex_lock(&mdp_mutex); | ||||
|  | ||||
| 	timeout_req = req; | ||||
| 	/* transp_masking unimplemented */ | ||||
| 	req->transp_mask = MDP_TRANSP_NOP; | ||||
| 	mdp->req = req; | ||||
| #ifndef CONFIG_MSM_MDP31 | ||||
| #if !defined(CONFIG_MSM_MDP31) && !defined(CONFIG_MSM_MDP302) | ||||
| 	if (unlikely((req->transp_mask != MDP_TRANSP_NOP || | ||||
| 		      req->alpha != MDP_ALPHA_NOP || | ||||
| 		      HAS_ALPHA(req->src.format)) && | ||||
| @@ -622,7 +717,37 @@ end: | ||||
| 	put_img(src_file); | ||||
| 	put_img(dst_file); | ||||
| 	mutex_unlock(&mdp_mutex); | ||||
| 	printk("-MDP_BLIT\n"); | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| int mdp_fb_mirror(struct mdp_device *mdp_dev, | ||||
| 		struct fb_info *src_fb, struct fb_info *dst_fb, | ||||
| 		struct mdp_blit_req *req) | ||||
| { | ||||
| 	int ret; | ||||
| 	struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); | ||||
|  | ||||
| 	if (!src_fb || !dst_fb) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	enable_mdp_irq(mdp, DL0_ROI_DONE); | ||||
| 	ret = mdp_ppp_blit(mdp, req, | ||||
| 			-1, src_fb->fix.smem_start, src_fb->fix.smem_len, | ||||
| 			-1, dst_fb->fix.smem_start, dst_fb->fix.smem_len); | ||||
| 	if (ret) | ||||
| 		goto err_bad_blit; | ||||
|  | ||||
| 	ret = mdp_ppp_wait(mdp); | ||||
| 	if (ret) { | ||||
| 		pr_err("mdp_ppp_wait error\n"); | ||||
| 		goto err_wait_failed; | ||||
| 	} | ||||
| 	return 0; | ||||
|  | ||||
| err_bad_blit: | ||||
| 	disable_mdp_irq(mdp, DL0_ROI_DONE); | ||||
|  | ||||
| err_wait_failed: | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| @@ -690,7 +815,7 @@ int mdp_out_if_req_irq(struct mdp_device *mdp_dev, int interface, | ||||
| 	if (mask) { | ||||
| 		ret = locked_enable_mdp_irq(mdp, mask); | ||||
| 		if (ret) { | ||||
| //			pr_err("%s: busy\n", __func__); | ||||
| 			pr_err("%s: busy\n", __func__); | ||||
| 			goto done; | ||||
| 		} | ||||
| 		mdp->out_if[interface].irq_mask = mask; | ||||
| @@ -716,11 +841,26 @@ int register_mdp_client(struct class_interface *cint) | ||||
| 	return class_interface_register(cint); | ||||
| } | ||||
|  | ||||
| #ifdef CONFIG_MSM_MDP40 | ||||
| void mdp_hw_init(struct mdp_info *mdp) | ||||
| { | ||||
| 	mdp_irq_mask = 0; | ||||
| 	mdp_writel(mdp, 0, MDP_INTR_ENABLE); | ||||
| } | ||||
| #else | ||||
| #include "mdp_csc_table.h" | ||||
|  | ||||
| void mdp_check_tearing(struct mdp_info *mdp, struct msm_mdp_platform_data *pdata) | ||||
| { | ||||
| 	mdp_writel(mdp, pdata->sync_config, MDP_SYNC_CONFIG_0); | ||||
| 	mdp_writel(mdp, 1, MDP_TEAR_CHECK_EN); | ||||
| 	mdp_writel(mdp, pdata->sync_thresh, MDP_SYNC_THRESH_0); | ||||
| 	mdp_writel(mdp, pdata->sync_start_pos, MDP_PRIM_START_POS); | ||||
| } | ||||
| void mdp_hw_init(struct mdp_info *mdp) | ||||
| { | ||||
| 	int n; | ||||
| 	int lcdc_enabled; | ||||
|  | ||||
| 	mdp_irq_mask = 0; | ||||
|  | ||||
| @@ -731,6 +871,7 @@ void mdp_hw_init(struct mdp_info *mdp) | ||||
| 	mdp_writel(mdp, 1, MDP_EBI2_PORTMAP_MODE); | ||||
|  | ||||
| #ifndef CONFIG_MSM_MDP22 | ||||
| 	lcdc_enabled = mdp_readl(mdp, MDP_LCDC_EN); | ||||
| 	/* disable lcdc */ | ||||
| 	mdp_writel(mdp, 0, MDP_LCDC_EN); | ||||
| 	/* enable auto clock gating for all blocks by default */ | ||||
| @@ -781,15 +922,19 @@ void mdp_hw_init(struct mdp_info *mdp) | ||||
| #ifndef CONFIG_MSM_MDP31 | ||||
| 	mdp_writel(mdp, 0x04000400, MDP_COMMAND_CONFIG); | ||||
| #endif | ||||
| #ifndef CONFIG_MSM_MDP22 | ||||
| 	if(lcdc_enabled) | ||||
| 		mdp_writel(mdp, 1, MDP_LCDC_EN); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| #endif //CONFIG_MSM_MDP40 | ||||
|  | ||||
| int mdp_probe(struct platform_device *pdev) | ||||
| { | ||||
| 	struct resource *resource; | ||||
| 	int ret; | ||||
| 	int ret = -EINVAL; | ||||
| 	struct mdp_info *mdp; | ||||
|  | ||||
| 	struct msm_mdp_platform_data *pdata = pdev->dev.platform_data; | ||||
|  | ||||
| 	resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||||
| 	if (!resource) { | ||||
| @@ -821,13 +966,49 @@ int mdp_probe(struct platform_device *pdev) | ||||
| 	mdp->mdp_dev.dma = mdp_dma; | ||||
| 	mdp->mdp_dev.dma_wait = mdp_dma_wait; | ||||
| 	mdp->mdp_dev.blit = mdp_blit; | ||||
| #ifdef CONFIG_FB_MSM_OVERLAY | ||||
| 	mdp->mdp_dev.overlay_get = mdp4_overlay_get; | ||||
| 	mdp->mdp_dev.overlay_set = mdp4_overlay_set; | ||||
| 	mdp->mdp_dev.overlay_unset = mdp4_overlay_unset; | ||||
| 	mdp->mdp_dev.overlay_play = mdp4_overlay_play; | ||||
| #endif | ||||
| 	mdp->mdp_dev.set_grp_disp = mdp_set_grp_disp; | ||||
| 	mdp->mdp_dev.set_output_format = mdp_set_output_format; | ||||
| 	mdp->mdp_dev.check_output_format = mdp_check_output_format; | ||||
| 	mdp->mdp_dev.configure_dma = mdp_configure_dma; | ||||
|  | ||||
| 	ret = mdp_out_if_register(&mdp->mdp_dev, MSM_MDDI_PMDH_INTERFACE, mdp, | ||||
| 				  MDP_DMA_P_DONE, mdp_dma_to_mddi); | ||||
| 	if (pdata == NULL || pdata->overrides == 0) | ||||
| 		mdp->mdp_dev.overrides = 0; | ||||
| 	else if(pdata->overrides) | ||||
| 		mdp->mdp_dev.overrides = pdata->overrides; | ||||
|  | ||||
| 	if (pdata == NULL || pdata->color_format == 0) | ||||
| 		mdp->mdp_dev.color_format = MSM_MDP_OUT_IF_FMT_RGB565; | ||||
| 	else if(pdata->color_format) | ||||
| 		mdp->mdp_dev.color_format = pdata->color_format; | ||||
|  | ||||
| 	if (pdata == NULL || pdata->dma_channel == MDP_DMA_P) { | ||||
| #ifdef CONFIG_MSM_MDP40 | ||||
| 		if (mdp->mdp_dev.overrides & MSM_MDP4_MDDI_DMA_SWITCH) { | ||||
| 			ret = mdp_out_if_register(&mdp->mdp_dev, | ||||
| 				MSM_MDDI_PMDH_INTERFACE, mdp, INTR_OVERLAY0_DONE | ||||
| 				| MDP_DMA_S_DONE, mdp4_mddi_overlay); | ||||
| 		} else { | ||||
| 			ret = mdp_out_if_register(&mdp->mdp_dev, | ||||
| 				MSM_MDDI_PMDH_INTERFACE, mdp, INTR_OVERLAY0_DONE, | ||||
| 				mdp4_mddi_overlay); | ||||
| 		} | ||||
| #else | ||||
| 		ret = mdp_out_if_register(&mdp->mdp_dev, | ||||
| 				MSM_MDDI_PMDH_INTERFACE, mdp, MDP_DMA_P_DONE, | ||||
| 				mdp_dma_to_mddi); | ||||
| #endif | ||||
| 	} else if (pdata->dma_channel == MDP_DMA_S) { | ||||
| 		ret = mdp_out_if_register(&mdp->mdp_dev, | ||||
| 				MSM_MDDI_PMDH_INTERFACE, mdp, MDP_DMA_S_DONE, | ||||
| 				mdp_dmas_to_mddi); | ||||
| 	} | ||||
|  | ||||
| 	if (ret) | ||||
| 		goto error_mddi_pmdh_register; | ||||
|  | ||||
| @@ -853,8 +1034,28 @@ int mdp_probe(struct platform_device *pdev) | ||||
|  | ||||
| 	clk_enable(mdp->clk); | ||||
| 	mdp_clk_to_disable_later = mdp->clk; | ||||
| 	mdp_hw_init(mdp); | ||||
| #ifdef CONFIG_MSM_MDP40 | ||||
| 	//MDP_DISP_INTF_SEL | ||||
| 	if (mdp_readl(mdp, 0xc0000)) | ||||
| 		mdp_writel(mdp, 0x8, 0x0038); | ||||
| 	else | ||||
| 		mdp_writel(mdp, 0xa, 0x0038); //mddi | ||||
| 	//FIXME: should select mddi or lcdc interface | ||||
| 	//mdp_writel(mdp, 0x8, 0x0038); //lcdc | ||||
| #endif | ||||
|  | ||||
| #ifdef CONFIG_MSM_MDP40 | ||||
| extern void mdp4_hw_init(struct mdp_info *mdp); | ||||
| 	mdp4_hw_init(mdp); | ||||
| #else | ||||
| 	mdp_hw_init(mdp); | ||||
| #endif | ||||
|  | ||||
| #if defined CONFIG_MSM_MDP302 | ||||
| 	/* enable the tearing check in MDP */ | ||||
| 	if(pdata != NULL && pdata->tearing_check) | ||||
| 		mdp_check_tearing(mdp, pdata); | ||||
| #endif | ||||
| 	/* register mdp device */ | ||||
| 	mdp->mdp_dev.dev.parent = &pdev->dev; | ||||
| 	mdp->mdp_dev.dev.class = mdp_class; | ||||
|   | ||||
| @@ -74,23 +74,52 @@ void mdp_ppp_dump_debug(const struct mdp_info *mdp); | ||||
| #define mdp_writel(mdp, value, offset) writel(value, mdp->base + offset) | ||||
| #define mdp_readl(mdp, offset) readl(mdp->base + offset) | ||||
|  | ||||
| #define MDP_SYNC_CONFIG_0                (0x00000) | ||||
| #define MDP_SYNC_CONFIG_1                (0x00004) | ||||
| #define MDP_SYNC_CONFIG_2                (0x00008) | ||||
| #define MDP_SYNC_STATUS_0                (0x0000c) | ||||
| #define MDP_SYNC_STATUS_1                (0x00010) | ||||
| #define MDP_SYNC_STATUS_2                (0x00014) | ||||
| #define MDP_SYNC_THRESH_0                (0x00018) | ||||
| #define MDP_SYNC_THRESH_1                (0x0001c) | ||||
| #define MDP_INTR_ENABLE                  (0x00020) | ||||
| #define MDP_INTR_STATUS                  (0x00024) | ||||
| #define MDP_INTR_CLEAR                   (0x00028) | ||||
| #define MDP_DISPLAY0_START               (0x00030) | ||||
| #define MDP_DISPLAY1_START               (0x00034) | ||||
| #define MDP_DISPLAY_STATUS               (0x00038) | ||||
| #define MDP_EBI2_LCD0                    (0x0003c) | ||||
| #define MDP_EBI2_LCD1                    (0x00040) | ||||
| #define MDP_EBI2_PORTMAP_MODE            (0x0005c) | ||||
| #ifdef CONFIG_MSM_MDP302 | ||||
| #define MDP_SYNC_CONFIG_0                ( 0x00300) | ||||
| #define MDP_SYNC_CONFIG_1                ( 0x00304) | ||||
| #define MDP_SYNC_CONFIG_2                ( 0x00308) | ||||
| #else | ||||
| #define MDP_SYNC_CONFIG_0                ( 0x00000) | ||||
| #define MDP_SYNC_CONFIG_1                ( 0x00004) | ||||
| #define MDP_SYNC_CONFIG_2                ( 0x00008) | ||||
| #endif | ||||
|  | ||||
| #define MDP_SYNC_STATUS_0                ( 0x0000c) | ||||
| #define MDP_SYNC_STATUS_1                ( 0x00010) | ||||
| #define MDP_SYNC_STATUS_2                ( 0x00014) | ||||
|  | ||||
| #ifdef CONFIG_MSM_MDP302 | ||||
| #define MDP_SYNC_THRESH_0                ( 0x00200) | ||||
| #define MDP_SYNC_THRESH_1                ( 0x00204) | ||||
| #else | ||||
| #define MDP_SYNC_THRESH_0                ( 0x00018) | ||||
| #define MDP_SYNC_THRESH_1                ( 0x0001c) | ||||
| #endif | ||||
| #ifdef CONFIG_MSM_MDP40 | ||||
| #define MDP_INTR_ENABLE                  ( 0x0050) | ||||
| #define MDP_INTR_STATUS                  ( 0x0054) | ||||
| #define MDP_INTR_CLEAR                   ( 0x0058) | ||||
| #define MDP_EBI2_LCD0                    ( 0x0060) | ||||
| #define MDP_EBI2_LCD1                    ( 0x0064) | ||||
| #define MDP_EBI2_PORTMAP_MODE            ( 0x0070) | ||||
|  | ||||
| #define MDP_DMA_P_HIST_INTR_STATUS 	( 0x95014) | ||||
| #define MDP_DMA_P_HIST_INTR_CLEAR 	( 0x95018) | ||||
| #define MDP_DMA_P_HIST_INTR_ENABLE 	( 0x9501C) | ||||
| #else | ||||
| #define MDP_INTR_ENABLE                  ( 0x00020) | ||||
| #define MDP_INTR_STATUS                  ( 0x00024) | ||||
| #define MDP_INTR_CLEAR                   ( 0x00028) | ||||
| #define MDP_EBI2_LCD0                    ( 0x0003c) | ||||
| #define MDP_EBI2_LCD1                    ( 0x00040) | ||||
| #define MDP_EBI2_PORTMAP_MODE            ( 0x0005c) | ||||
| #endif | ||||
| #define MDP_DISPLAY0_START               ( 0x00030) | ||||
| #define MDP_DISPLAY1_START               ( 0x00034) | ||||
| #define MDP_DISPLAY_STATUS               ( 0x00038) | ||||
| /* CONFIG_MSM_MDP302 */ | ||||
| #define MDP_TEAR_CHECK_EN                ( 0x0020c) | ||||
| #define MDP_PRIM_START_POS               ( 0x00210) | ||||
|  | ||||
| #ifndef CONFIG_MSM_MDP31 | ||||
| #define MDP_DISPLAY0_ADDR                (0x00054) | ||||
| @@ -228,7 +257,11 @@ void mdp_ppp_dump_debug(const struct mdp_info *mdp); | ||||
| #define MDP_TEST_CAPTURED_DCLK           (0xd0210) | ||||
| #define MDP_TEST_MISR_CAPT_VAL_DCLK      (0xd0214) | ||||
|  | ||||
| #ifdef CONFIG_MSM_MDP40 | ||||
| #define MDP_DMA_P_START                  (0x000c) | ||||
| #else | ||||
| #define MDP_DMA_P_START                  (0x00044) | ||||
| #endif | ||||
| #define MDP_DMA_P_CONFIG                 (0x90000) | ||||
| #define MDP_DMA_P_SIZE                   (0x90004) | ||||
| #define MDP_DMA_P_IBUF_ADDR              (0x90008) | ||||
| @@ -236,6 +269,30 @@ void mdp_ppp_dump_debug(const struct mdp_info *mdp); | ||||
| #define MDP_DMA_P_OUT_XY                 (0x90010) | ||||
| #define MDP_DMA_P_COLOR_CORRECT_CONFIG   (0x90070) | ||||
|  | ||||
| #define MDP_DMA_S_START                  (0x00048) | ||||
| #define MDP_DMA_S_CONFIG                 (0xa0000) | ||||
| #define MDP_DMA_S_SIZE                   (0xa0004) | ||||
| #define MDP_DMA_S_IBUF_ADDR              (0xa0008) | ||||
| #define MDP_DMA_S_IBUF_Y_STRIDE          (0xa000c) | ||||
| #define MDP_DMA_S_OUT_XY                 (0xa0010) | ||||
|  | ||||
| #ifdef CONFIG_MSM_MDP40 | ||||
| #define MDP_LCDC_EN                      (0xc0000) | ||||
| #define MDP_LCDC_HSYNC_CTL               (0xc0004) | ||||
| #define MDP_LCDC_VSYNC_PERIOD            (0xc0008) | ||||
| #define MDP_LCDC_VSYNC_PULSE_WIDTH       (0xc000c) | ||||
| #define MDP_LCDC_DISPLAY_HCTL            (0xc0010) | ||||
| #define MDP_LCDC_DISPLAY_V_START         (0xc0014) | ||||
| #define MDP_LCDC_DISPLAY_V_END           (0xc0018) | ||||
| #define MDP_LCDC_ACTIVE_HCTL             (0xc001c) | ||||
| #define MDP_LCDC_ACTIVE_V_START          (0xc0020) | ||||
| #define MDP_LCDC_ACTIVE_V_END            (0xc0024) | ||||
| #define MDP_LCDC_BORDER_CLR              (0xc0028) | ||||
| #define MDP_LCDC_UNDERFLOW_CTL           (0xc002c) | ||||
| #define MDP_LCDC_HSYNC_SKEW              (0xc0030) | ||||
| #define MDP_LCDC_TEST_CTL                (0xc0034) | ||||
| #define MDP_LCDC_CTL_POLARITY            (0xc0038) | ||||
| #else | ||||
| #define MDP_LCDC_EN                      (0xe0000) | ||||
| #define MDP_LCDC_HSYNC_CTL               (0xe0004) | ||||
| #define MDP_LCDC_VSYNC_PERIOD            (0xe0008) | ||||
| @@ -251,6 +308,7 @@ void mdp_ppp_dump_debug(const struct mdp_info *mdp); | ||||
| #define MDP_LCDC_HSYNC_SKEW              (0xe0030) | ||||
| #define MDP_LCDC_TEST_CTL                (0xe0034) | ||||
| #define MDP_LCDC_CTL_POLARITY            (0xe0038) | ||||
| #endif | ||||
|  | ||||
| #define MDP_PPP_SCALE_STATUS             (0x50000) | ||||
| #define MDP_PPP_BLEND_STATUS             (0x70000) | ||||
| @@ -262,15 +320,30 @@ void mdp_ppp_dump_debug(const struct mdp_info *mdp); | ||||
| #define DL0_ROI_DONE			(1<<0) | ||||
| #define TV_OUT_DMA3_DONE		(1<<6) | ||||
| #define TV_ENC_UNDERRUN			(1<<7) | ||||
| #define TV_OUT_FRAME_START		(1<<13) | ||||
|  | ||||
| #ifdef CONFIG_MSM_MDP22 | ||||
| #define MDP_DMA_P_DONE			(1 << 2) | ||||
| #define MDP_DMA_S_DONE			(1 << 3) | ||||
| #else /* CONFIG_MSM_MDP31 */ | ||||
|  | ||||
| #ifdef CONFIG_MSM_MDP40 | ||||
| #define MDP_DMA_P_DONE			(1 << 4) | ||||
| #else | ||||
| #define MDP_DMA_P_DONE			(1 << 14) | ||||
| #endif | ||||
|  | ||||
| #define MDP_DMA_S_DONE			(1 << 2) | ||||
| #define MDP_LCDC_UNDERFLOW		(1 << 16) | ||||
|  | ||||
| #ifdef CONFIG_MSM_MDP40 | ||||
| #define MDP_LCDC_FRAME_START		(1 << 7) | ||||
| #else | ||||
| #define MDP_LCDC_FRAME_START		(1 << 15) | ||||
| #endif | ||||
|  | ||||
| #endif | ||||
|  | ||||
| #define MDP_TOP_LUMA       16 | ||||
| #define MDP_TOP_CHROMA     0 | ||||
| #define MDP_BOTTOM_LUMA    19 | ||||
| @@ -294,9 +367,15 @@ void mdp_ppp_dump_debug(const struct mdp_info *mdp); | ||||
| 	(((a)<<(bit*3))|((x)<<(bit*2))|((y)<<bit)|(z)) | ||||
|  | ||||
| /* MDP_SYNC_CONFIG_0/1/2 */ | ||||
| #if defined(CONFIG_MSM_MDP30) | ||||
| #define MDP_SYNCFG_HGT_LOC 21 | ||||
| #define MDP_SYNCFG_VSYNC_EXT_EN (1<<20) | ||||
| #define MDP_SYNCFG_VSYNC_INT_EN (1<<19) | ||||
| #else | ||||
| #define MDP_SYNCFG_HGT_LOC 22 | ||||
| #define MDP_SYNCFG_VSYNC_EXT_EN (1<<21) | ||||
| #define MDP_SYNCFG_VSYNC_INT_EN (1<<20) | ||||
| #endif | ||||
|  | ||||
| /* MDP_SYNC_THRESH_0 */ | ||||
| #define MDP_PRIM_BELOW_LOC 0 | ||||
| @@ -309,11 +388,11 @@ void mdp_ppp_dump_debug(const struct mdp_info *mdp); | ||||
| /* MDP_VSYNC_CTRL */ | ||||
| #define DISP0_VSYNC_MAP_VSYNC0 0 | ||||
| #define DISP0_VSYNC_MAP_VSYNC1 (1<<0) | ||||
| #define DISP0_VSYNC_MAP_VSYNC2 ((1<<0)|(1<<1)) | ||||
| #define DISP0_VSYNC_MAP_VSYNC2 (1<<0)|(1<<1) | ||||
|  | ||||
| #define DISP1_VSYNC_MAP_VSYNC0 0 | ||||
| #define DISP1_VSYNC_MAP_VSYNC1 (1<<2) | ||||
| #define DISP1_VSYNC_MAP_VSYNC2 ((1<<2)|(1<<3)) | ||||
| #define DISP1_VSYNC_MAP_VSYNC2 (1<<2)|(1<<3) | ||||
|  | ||||
| #define PRIMARY_LCD_SYNC_EN (1<<4) | ||||
| #define PRIMARY_LCD_SYNC_DISABLE 0 | ||||
| @@ -744,7 +823,25 @@ void mdp_ppp_dump_debug(const struct mdp_info *mdp); | ||||
| #define DMA_IBUF_FORMAT_MASK (1 << 20) | ||||
| #define DMA_IBUF_NONCONTIGUOUS (1<<21) | ||||
|  | ||||
| #else /* CONFIG_MSM_MDP31 */ | ||||
| #elif defined(CONFIG_MSM_MDP30) | ||||
|  | ||||
| #define DMA_OUT_SEL_AHB  0 | ||||
| #define DMA_OUT_SEL_MDDI (1<<19) | ||||
| #define DMA_AHBM_LCD_SEL_PRIMARY 0 | ||||
| #define DMA_AHBM_LCD_SEL_SECONDARY (0) | ||||
| #define DMA_IBUF_C3ALPHA_EN (0) | ||||
| #define DMA_DITHER_EN (1<<24) | ||||
|  | ||||
| #define DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY 0 | ||||
| #define DMA_MDDI_DMAOUT_LCD_SEL_SECONDARY (0) | ||||
| #define DMA_MDDI_DMAOUT_LCD_SEL_EXTERNAL (0) | ||||
|  | ||||
| #define DMA_IBUF_FORMAT_MASK (1 << 20) | ||||
| #define DMA_IBUF_FORMAT_RGB565 (1<<25) | ||||
| #define DMA_IBUF_FORMAT_RGB888_OR_ARGB8888 (1<<26) | ||||
| #define DMA_IBUF_NONCONTIGUOUS (0) | ||||
|  | ||||
| #else /* CONFIG_MSM_MDP31 | CONFIG_MSM_MDP302 */ | ||||
|  | ||||
| #define DMA_OUT_SEL_AHB				(0 << 19) | ||||
| #define DMA_OUT_SEL_MDDI			(1 << 19) | ||||
| @@ -763,7 +860,8 @@ void mdp_ppp_dump_debug(const struct mdp_info *mdp); | ||||
| #endif | ||||
|  | ||||
| /* MDDI REGISTER ? */ | ||||
| #define MDDI_VDO_PACKET_DESC  0x5666 | ||||
| #define MDDI_VDO_PACKET_DESC_RGB565  0x5565 | ||||
| #define MDDI_VDO_PACKET_DESC_RGB666  0x5666 | ||||
| #define MDDI_VDO_PACKET_PRIM  0xC3 | ||||
| #define MDDI_VDO_PACKET_SECD  0xC0 | ||||
|  | ||||
|   | ||||
| @@ -26,11 +26,23 @@ | ||||
|  | ||||
| #include <asm/io.h> | ||||
| #include <asm/mach-types.h> | ||||
|  | ||||
| #include <linux/msm_mdp.h> | ||||
| #include <mach/msm_fb.h> | ||||
|  | ||||
| #include "mdp_hw.h" | ||||
| #ifdef CONFIG_MSM_MDP40 | ||||
| #include "mdp4.h" | ||||
| #endif | ||||
|  | ||||
| #if 0 | ||||
| #define D(fmt, args...) printk(KERN_INFO "Dispaly: " fmt, ##args) | ||||
| #else | ||||
| #define D(fmt, args...) do {} while (0) | ||||
| #endif | ||||
|  | ||||
| #if defined(CONFIG_ARCH_MSM7227) | ||||
| #define LCDC_MUX_CTL (MSM_TGPIO1_BASE + 0x278) | ||||
| #endif | ||||
| struct mdp_lcdc_info { | ||||
| 	struct mdp_info			*mdp; | ||||
| 	struct clk			*mdp_clk; | ||||
| @@ -44,7 +56,7 @@ struct mdp_lcdc_info { | ||||
| 	struct msmfb_callback		frame_start_cb; | ||||
| 	wait_queue_head_t		vsync_waitq; | ||||
| 	int				got_vsync; | ||||
|  | ||||
| 	unsigned			color_format; | ||||
| 	struct { | ||||
| 		uint32_t	clk_rate; | ||||
| 		uint32_t	hsync_ctl; | ||||
| @@ -60,6 +72,10 @@ struct mdp_lcdc_info { | ||||
|  | ||||
| static struct mdp_device *mdp_dev; | ||||
|  | ||||
| #ifdef CONFIG_MSM_MDP40 | ||||
| static struct mdp4_overlay_pipe *lcdc_pipe; | ||||
| #endif | ||||
|  | ||||
| #define panel_to_lcdc(p) container_of((p), struct mdp_lcdc_info, fb_panel_data) | ||||
|  | ||||
| static int lcdc_unblank(struct msm_panel_data *fb_panel) | ||||
| @@ -68,6 +84,7 @@ static int lcdc_unblank(struct msm_panel_data *fb_panel) | ||||
| 	struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops; | ||||
|  | ||||
| 	pr_info("%s: ()\n", __func__); | ||||
|  | ||||
| 	panel_ops->unblank(panel_ops); | ||||
|  | ||||
| 	return 0; | ||||
| @@ -86,27 +103,49 @@ static int lcdc_blank(struct msm_panel_data *fb_panel) | ||||
|  | ||||
| static int lcdc_suspend(struct msm_panel_data *fb_panel) | ||||
| { | ||||
| 	int status; | ||||
| 	struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel); | ||||
| 	struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops; | ||||
|  | ||||
| 	pr_info("%s: suspending\n", __func__); | ||||
|  | ||||
| #if defined(CONFIG_ARCH_MSM7227) | ||||
| 	writel(0x0, LCDC_MUX_CTL); | ||||
| 	status = readl(LCDC_MUX_CTL); | ||||
| 	D("suspend_lcdc_mux_ctl = %x\n", status); | ||||
| #endif | ||||
| 	mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN); | ||||
| 	clk_disable(lcdc->pad_pclk); | ||||
| 	clk_disable(lcdc->pclk); | ||||
| 	clk_disable(lcdc->mdp_clk); | ||||
| 	if (panel_ops->uninit) | ||||
| 		panel_ops->uninit(panel_ops); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int lcdc_resume(struct msm_panel_data *fb_panel) | ||||
| { | ||||
| 	unsigned int status; | ||||
| 	struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel); | ||||
| 	struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops; | ||||
|  | ||||
| 	pr_info("%s: resuming\n", __func__); | ||||
|  | ||||
| 	if (panel_ops->init) { | ||||
| 		if (panel_ops->init(panel_ops) < 0) | ||||
| 			printk(KERN_ERR "LCD init fail!\n"); | ||||
| 	} | ||||
|  | ||||
| 	clk_enable(lcdc->mdp_clk); | ||||
| 	clk_enable(lcdc->pclk); | ||||
| 	clk_enable(lcdc->pad_pclk); | ||||
| #if defined(CONFIG_ARCH_MSM7227) | ||||
| 	writel(0x1, LCDC_MUX_CTL); | ||||
| 	status = readl(LCDC_MUX_CTL); | ||||
| 	D("resume_lcdc_mux_ctl = %x\n",status); | ||||
| #endif | ||||
|  | ||||
| 	mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN); | ||||
|  | ||||
| 	return 0; | ||||
| @@ -124,8 +163,6 @@ static int lcdc_hw_init(struct mdp_lcdc_info *lcdc) | ||||
| 	clk_set_rate(lcdc->pclk, lcdc->parms.clk_rate); | ||||
| 	clk_set_rate(lcdc->pad_pclk, lcdc->parms.clk_rate); | ||||
|  | ||||
| //	mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN); // Cotulla | ||||
|  | ||||
| 	/* write the lcdc params */ | ||||
| 	mdp_writel(lcdc->mdp, lcdc->parms.hsync_ctl, MDP_LCDC_HSYNC_CTL); | ||||
| 	mdp_writel(lcdc->mdp, lcdc->parms.vsync_period, MDP_LCDC_VSYNC_PERIOD); | ||||
| @@ -138,7 +175,7 @@ static int lcdc_hw_init(struct mdp_lcdc_info *lcdc) | ||||
| 	mdp_writel(lcdc->mdp, lcdc->parms.hsync_skew, MDP_LCDC_HSYNC_SKEW); | ||||
|  | ||||
| 	mdp_writel(lcdc->mdp, 0, MDP_LCDC_BORDER_CLR); | ||||
| 	mdp_writel(lcdc->mdp, 0xFFFFFF | 0x80000000, MDP_LCDC_UNDERFLOW_CTL); // Cotulla | ||||
| 	mdp_writel(lcdc->mdp, 0xff, MDP_LCDC_UNDERFLOW_CTL); | ||||
| 	mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_HCTL); | ||||
| 	mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_START); | ||||
| 	mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_END); | ||||
| @@ -152,38 +189,28 @@ static int lcdc_hw_init(struct mdp_lcdc_info *lcdc) | ||||
|  | ||||
| 	mdp_writel(lcdc->mdp, 0, MDP_DMA_P_OUT_XY); | ||||
|  | ||||
| 	dma_cfg = mdp_readl(lcdc->mdp, MDP_DMA_P_CONFIG); | ||||
| 	 | ||||
| 	 | ||||
| 	dma_cfg |= (DMA_PACK_ALIGN_LSB | | ||||
| 		   DMA_PACK_PATTERN_RGB | | ||||
| 		   DMA_DITHER_EN); | ||||
| 	dma_cfg |= DMA_OUT_SEL_LCDC; | ||||
| 	dma_cfg &= ~DMA_DST_BITS_MASK; | ||||
|  | ||||
| 	if(machine_is_htcleo()) { | ||||
| 		dma_cfg = DMA_PACK_ALIGN_MSB | | ||||
| 			DMA_PACK_PATTERN_RGB; | ||||
|  | ||||
| 		dma_cfg |= DMA_OUT_SEL_LCDC; | ||||
| 		dma_cfg &= ~DMA_DST_BITS_MASK; | ||||
| 	} | ||||
| 	else { | ||||
| 		dma_cfg |= (DMA_PACK_ALIGN_LSB | | ||||
| 			  DMA_PACK_PATTERN_RGB | | ||||
| 	} else { | ||||
| 		dma_cfg = mdp_readl(lcdc->mdp, MDP_DMA_P_CONFIG); | ||||
| 		if (lcdc->pdata->overrides & MSM_MDP_LCDC_DMA_PACK_ALIGN_LSB) | ||||
| 			dma_cfg &= ~DMA_PACK_ALIGN_MSB; | ||||
| 		else | ||||
| 			dma_cfg |= DMA_PACK_ALIGN_MSB; | ||||
|  | ||||
| 		dma_cfg |= (DMA_PACK_PATTERN_RGB | | ||||
| 			  DMA_DITHER_EN); | ||||
| 		dma_cfg |= DMA_OUT_SEL_LCDC; | ||||
| 		dma_cfg &= ~DMA_DST_BITS_MASK; | ||||
| 	} | ||||
|  | ||||
| 	/* enable the lcdc timing generation */ | ||||
| 	mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN); | ||||
| 	 | ||||
| 	 | ||||
| 	if (fb_panel->fb_data->output_format == MSM_MDP_OUT_IF_FMT_RGB666) | ||||
| 		dma_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; | ||||
| 	else | ||||
| 	if(lcdc->color_format == MSM_MDP_OUT_IF_FMT_RGB565) | ||||
| 		dma_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS; | ||||
| 	else if (lcdc->color_format == MSM_MDP_OUT_IF_FMT_RGB666) | ||||
| 		dma_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; | ||||
|  | ||||
| 	mdp_writel(lcdc->mdp, dma_cfg, MDP_DMA_P_CONFIG); | ||||
|  | ||||
| @@ -193,14 +220,12 @@ static int lcdc_hw_init(struct mdp_lcdc_info *lcdc) | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| static void lcdc_wait_vsync(struct msm_panel_data *panel) | ||||
| { | ||||
| 	struct mdp_lcdc_info *lcdc = panel_to_lcdc(panel); | ||||
| 	int ret; | ||||
|         | ||||
|     ret = wait_event_timeout(lcdc->vsync_waitq, lcdc->got_vsync, HZ / 2); | ||||
|  | ||||
| 	ret = wait_event_timeout(lcdc->vsync_waitq, lcdc->got_vsync, HZ / 2); | ||||
| 	if (!ret && !lcdc->got_vsync) | ||||
| 		pr_err("%s: timeout waiting for VSYNC\n", __func__); | ||||
| 	lcdc->got_vsync = 0; | ||||
| @@ -233,13 +258,13 @@ static void lcdc_request_vsync(struct msm_panel_data *fb_panel, | ||||
| //		vsync_cb->func(vsync_cb); | ||||
| 	} | ||||
| 	else | ||||
|     { | ||||
|         lcdc->got_vsync = 0; | ||||
|      	mdp_out_if_req_irq(mdp_dev, MSM_LCDC_INTERFACE, MDP_LCDC_FRAME_START,  &lcdc->frame_start_cb); | ||||
|         lcdc_wait_vsync(fb_panel); | ||||
|     } | ||||
| 	{ | ||||
| 		lcdc->got_vsync = 0; | ||||
| 		mdp_out_if_req_irq(mdp_dev, MSM_LCDC_INTERFACE, MDP_LCDC_FRAME_START, | ||||
| 			  &lcdc->frame_start_cb); | ||||
| 		lcdc_wait_vsync(fb_panel); | ||||
| 	} | ||||
| // CotullaFIX end        | ||||
| 	 | ||||
| } | ||||
|  | ||||
| static void lcdc_clear_vsync(struct msm_panel_data *fb_panel) | ||||
| @@ -266,13 +291,11 @@ static void lcdc_dma_start(void *priv, uint32_t addr, uint32_t stride, | ||||
| 			   uint32_t y) | ||||
| { | ||||
| 	struct mdp_lcdc_info *lcdc = priv; | ||||
|  | ||||
| //    printk("DMA start\n"); | ||||
| 	struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); | ||||
| 	if (mdp->dma_config_dirty) | ||||
| 	{ | ||||
| 		mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN); | ||||
| 		mdelay(20); | ||||
| 		mdelay(30); | ||||
| 		mdp_dev->configure_dma(mdp_dev); | ||||
| 		mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN); | ||||
| 	} | ||||
| @@ -280,6 +303,35 @@ static void lcdc_dma_start(void *priv, uint32_t addr, uint32_t stride, | ||||
| 	mdp_writel(lcdc->mdp, addr, MDP_DMA_P_IBUF_ADDR); | ||||
| } | ||||
|  | ||||
| #ifdef CONFIG_MSM_MDP40 | ||||
| static void lcdc_overlay_start(void *priv, uint32_t addr, uint32_t stride, | ||||
| 			   uint32_t width, uint32_t height, uint32_t x, | ||||
| 			   uint32_t y) | ||||
| { | ||||
| 	struct mdp_lcdc_info *lcdc = priv; | ||||
| 	struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); | ||||
|  | ||||
| 	struct mdp4_overlay_pipe *pipe; | ||||
| 	pipe = lcdc_pipe; | ||||
| 	pipe->srcp0_addr = addr; | ||||
|  | ||||
| 	if (mdp->dma_config_dirty) | ||||
| 	{ | ||||
| 		if(mdp->format == DMA_IBUF_FORMAT_RGB565) { | ||||
| 			pipe->src_format = MDP_RGB_565; | ||||
| 			pipe->srcp0_ystride = pipe->src_width * 2; | ||||
| 		} else if(mdp->format == DMA_IBUF_FORMAT_XRGB8888) { | ||||
| 			pipe->src_format = MDP_RGBA_8888; | ||||
| 			pipe->srcp0_ystride = pipe->src_width * 4; | ||||
| 		} | ||||
| 		mdp4_overlay_format2pipe(pipe); | ||||
| 		mdp->dma_config_dirty = false; | ||||
| 	} | ||||
| 	mdp4_overlay_rgb_setup(pipe); | ||||
| 	mdp4_overlay_reg_flush(pipe, 1); /* rgb1 and mixer0 */ | ||||
|  | ||||
| } | ||||
| #endif | ||||
| static void precompute_timing_parms(struct mdp_lcdc_info *lcdc) | ||||
| { | ||||
| 	struct msm_lcdc_timing *timing = lcdc->pdata->timing; | ||||
| @@ -291,21 +343,21 @@ static void precompute_timing_parms(struct mdp_lcdc_info *lcdc) | ||||
| 	unsigned int display_vstart; | ||||
| 	unsigned int display_vend; | ||||
|  | ||||
| 	hsync_period = (timing->hsync_back_porch + | ||||
| 	hsync_period = (timing->hsync_pulse_width + timing->hsync_back_porch + | ||||
| 			fb_data->xres + timing->hsync_front_porch); | ||||
| 	hsync_start_x = timing->hsync_back_porch; | ||||
| 	hsync_start_x = (timing->hsync_pulse_width + timing->hsync_back_porch); | ||||
| 	hsync_end_x = hsync_start_x + fb_data->xres - 1; | ||||
|  | ||||
| 	vsync_period = (timing->vsync_back_porch + | ||||
| 	vsync_period = (timing->vsync_pulse_width + timing->vsync_back_porch + | ||||
| 			fb_data->yres + timing->vsync_front_porch); | ||||
| 	vsync_period *= hsync_period; | ||||
|  | ||||
| 	display_vstart = timing->vsync_back_porch; | ||||
| 	display_vstart = timing->vsync_pulse_width + timing->vsync_back_porch; | ||||
| 	display_vstart *= hsync_period; | ||||
| 	display_vstart += timing->hsync_skew; | ||||
|  | ||||
| 	display_vend = (timing->vsync_back_porch + fb_data->yres) * | ||||
| 		hsync_period; | ||||
| 	display_vend = (timing->vsync_pulse_width + timing->vsync_back_porch + | ||||
| 			 fb_data->yres) * hsync_period; | ||||
| 	display_vend += timing->hsync_skew - 1; | ||||
|  | ||||
| 	/* register values we pre-compute at init time from the timing | ||||
| @@ -332,6 +384,10 @@ static int mdp_lcdc_probe(struct platform_device *pdev) | ||||
| 	struct msm_lcdc_platform_data *pdata = pdev->dev.platform_data; | ||||
| 	struct mdp_lcdc_info *lcdc; | ||||
| 	int ret = 0; | ||||
| #ifdef CONFIG_MSM_MDP40 | ||||
| 	struct mdp4_overlay_pipe *pipe; | ||||
| 	int ptype; | ||||
| #endif | ||||
|  | ||||
| 	if (!pdata) { | ||||
| 		pr_err("%s: no LCDC platform data found\n", __func__); | ||||
| @@ -369,25 +425,61 @@ static int mdp_lcdc_probe(struct platform_device *pdev) | ||||
| 	lcdc->frame_start_cb.func = lcdc_frame_start; | ||||
|  | ||||
| 	platform_set_drvdata(pdev, lcdc); | ||||
|  | ||||
| #ifdef CONFIG_MSM_MDP40 | ||||
| 	mdp_out_if_register(mdp_dev, MSM_LCDC_INTERFACE, lcdc, INTR_OVERLAY0_DONE, | ||||
| 			    lcdc_overlay_start); | ||||
| #else | ||||
| 	mdp_out_if_register(mdp_dev, MSM_LCDC_INTERFACE, lcdc, MDP_DMA_P_DONE, | ||||
| 			    lcdc_dma_start); | ||||
|  | ||||
| #endif | ||||
| 	precompute_timing_parms(lcdc); | ||||
|  | ||||
| 	lcdc->fb_start = pdata->fb_resource->start; | ||||
| 	lcdc->mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); | ||||
| 	if(lcdc->mdp->mdp_dev.color_format) | ||||
| 		lcdc->color_format = lcdc->mdp->mdp_dev.color_format; | ||||
| 	else | ||||
| 		lcdc->color_format = MSM_MDP_OUT_IF_FMT_RGB565; | ||||
|  | ||||
| #ifdef CONFIG_MSM_MDP40 | ||||
| 	if (lcdc_pipe == NULL) { | ||||
| 		ptype = mdp4_overlay_format2type(MDP_RGB_565); | ||||
| 		pipe = mdp4_overlay_pipe_alloc(ptype); | ||||
| 		pipe->mixer_stage  = MDP4_MIXER_STAGE_BASE; | ||||
| 		pipe->mixer_num  = MDP4_MIXER0; | ||||
| 		pipe->src_format = MDP_RGB_565; | ||||
| 		mdp4_overlay_format2pipe(pipe); | ||||
| 		pipe->mdp = lcdc->mdp; | ||||
|  | ||||
| 		lcdc_pipe = pipe; /* keep it */ | ||||
| 	} else { | ||||
| 		pipe = lcdc_pipe; | ||||
| 	} | ||||
|  | ||||
| 	pipe->src_height = pdata->fb_data->yres; | ||||
| 	pipe->src_width = pdata->fb_data->xres; | ||||
| 	pipe->src_h = pdata->fb_data->yres; | ||||
| 	pipe->src_w = pdata->fb_data->xres; | ||||
| 	pipe->src_y = 0; | ||||
| 	pipe->src_x = 0; | ||||
| 	pipe->srcp0_addr = (uint32_t) lcdc->fb_start; | ||||
| 	pipe->srcp0_ystride = pdata->fb_data->xres * 2; | ||||
|  | ||||
| 	mdp4_overlay_dmap_xy(pipe); | ||||
| 	mdp4_overlay_dmap_cfg(pipe, 1); | ||||
|  | ||||
| 	mdp4_overlay_rgb_setup(pipe); | ||||
|  | ||||
| 	mdp4_mixer_stage_up(pipe); | ||||
|  | ||||
| 	mdp4_overlayproc_cfg(pipe); | ||||
| 	mdp4_overlay_reg_flush(pipe, 1); | ||||
| #endif | ||||
|  | ||||
| 	lcdc->fb_panel_data.suspend = lcdc_suspend; | ||||
| 	lcdc->fb_panel_data.resume = lcdc_resume; | ||||
| 	lcdc->fb_panel_data.wait_vsync = lcdc_wait_vsync; | ||||
| //#if defined(CONFIG_MACH_HTCLEO) | ||||
| 	// Temporarily disable vsync to prevent a scheduler bug, need | ||||
| 	// to be looked into further. | ||||
| //	lcdc->fb_panel_data.request_vsync = 0; | ||||
| //#else | ||||
| 	lcdc->fb_panel_data.request_vsync = lcdc_request_vsync; | ||||
| //#endif | ||||
| 	lcdc->fb_panel_data.clear_vsync = lcdc_clear_vsync; | ||||
| 	lcdc->fb_panel_data.blank = lcdc_blank; | ||||
| 	lcdc->fb_panel_data.unblank = lcdc_unblank; | ||||
| @@ -395,7 +487,6 @@ static int mdp_lcdc_probe(struct platform_device *pdev) | ||||
| 	lcdc->fb_panel_data.interface_type = MSM_LCDC_INTERFACE; | ||||
|  | ||||
| 	ret = lcdc_hw_init(lcdc); | ||||
| //	ret = 0; | ||||
| 	if (ret) { | ||||
| 		pr_err("%s: Cannot initialize the mdp_lcdc\n", __func__); | ||||
| 		goto err_hw_init; | ||||
| @@ -407,8 +498,6 @@ static int mdp_lcdc_probe(struct platform_device *pdev) | ||||
| 	lcdc->fb_pdev.num_resources = 1; | ||||
| 	lcdc->fb_pdev.dev.platform_data = &lcdc->fb_panel_data; | ||||
|  | ||||
| 	if (pdata->panel_ops->init) | ||||
| 		pdata->panel_ops->init(pdata->panel_ops); | ||||
|  | ||||
| 	ret = platform_device_register(&lcdc->fb_pdev); | ||||
| 	if (ret) { | ||||
|   | ||||
| @@ -52,19 +52,19 @@ static uint32_t dst_img_cfg[] = { | ||||
| 	PPP_ARRAY1(CFG, DST) | ||||
| }; | ||||
|  | ||||
| static const uint32_t bytes_per_pixel[] = { | ||||
| static uint32_t bytes_per_pixel[] = { | ||||
| 	[MDP_RGB_565] = 2, | ||||
| 	[MDP_XRGB_8888] = 4, | ||||
| 	[MDP_Y_CBCR_H2V2] = 1, | ||||
| 	[MDP_ARGB_8888] = 4, | ||||
| 	[MDP_RGB_888] = 3, | ||||
| 	[MDP_Y_CRCB_H2V2] = 1, | ||||
| 	[MDP_YCRYCB_H2V1] = 2, | ||||
| 	[MDP_Y_CRCB_H2V1] = 1, | ||||
| 	[MDP_Y_CBCR_H2V1] = 1, | ||||
| 	[MDP_XRGB_8888] = 4, | ||||
| 	[MDP_ARGB_8888] = 4, | ||||
| 	[MDP_RGBA_8888] = 4, | ||||
| 	[MDP_BGRA_8888] = 4, | ||||
| 	[MDP_RGBX_8888] = 4, | ||||
| 	[MDP_Y_CBCR_H2V1] = 1, | ||||
| 	[MDP_Y_CBCR_H2V2] = 1, | ||||
| 	[MDP_Y_CRCB_H2V1] = 1, | ||||
| 	[MDP_Y_CRCB_H2V2] = 1, | ||||
| 	[MDP_YCRYCB_H2V1] = 2 | ||||
| }; | ||||
|  | ||||
| static uint32_t dst_op_chroma[] = { | ||||
| @@ -268,7 +268,7 @@ static void blit_blend(struct mdp_blit_req *req, struct ppp_regs *regs) | ||||
| 	req->alpha &= 0xff; | ||||
| 	/* ALPHA BLEND */ | ||||
| 	if (HAS_ALPHA(req->src.format)) { | ||||
| #if 0 | ||||
| #if !defined(CONFIG_MACH_HTCLEO)	 | ||||
| 		regs->op |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON; | ||||
| 		if (req->flags & MDP_BLEND_FG_PREMULT) { | ||||
| #ifdef CONFIG_MSM_MDP31 | ||||
| @@ -282,7 +282,7 @@ static void blit_blend(struct mdp_blit_req *req, struct ppp_regs *regs) | ||||
| 			regs->op |= PPP_OP_BLEND_CONSTANT_ALPHA; | ||||
| 			req->alpha = 0xff; | ||||
| #endif | ||||
| 		} els { | ||||
| 		} else { | ||||
| 			regs->op |= PPP_OP_BLEND_SRCPIXEL_ALPHA; | ||||
| 		} | ||||
| #else | ||||
| @@ -499,19 +499,21 @@ static int send_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, | ||||
| #ifdef CONFIG_MSM_MDP31 | ||||
| 		mdp_writel_dbg(mdp, regs->bg_xy, MDP_PPP_BG_XY); | ||||
| 		mdp_writel_dbg(mdp, regs->bg_img_sz, MDP_PPP_BG_IMAGE_SIZE); | ||||
| 		mdp_writel_dbg(mdp, regs->bg_alpha_sel, MDP_PPP_BLEND_BG_ALPHA_SEL); | ||||
|  | ||||
| 		mdp_writel_dbg(mdp, 0, MDP_TFETCH_TEST_MODE); | ||||
|  | ||||
| 		mdp_writel_dbg(mdp, regs->bg_alpha_sel, | ||||
| 			       MDP_PPP_BLEND_BG_ALPHA_SEL); | ||||
| 			        | ||||
| #if defined(CONFIG_MACH_HTCLEO) | ||||
|       		mdp_writel_dbg(mdp, 0, MDP_TFETCH_TEST_MODE); | ||||
| #endif | ||||
| #endif | ||||
| 	} | ||||
| 	flush_imgs(req, regs, src_file, dst_file); | ||||
| 	if( src_file != -1 && dst_file != -1 ) | ||||
| 		flush_imgs(req, regs, src_file, dst_file); | ||||
| 	mdp_writel_dbg(mdp, 0x1000, MDP_DISPLAY0_START); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| #if PPP_DUMP_BLITS | ||||
| static void mdp_dump_blit(struct mdp_blit_req *req) | ||||
| void mdp_dump_blit(struct mdp_blit_req *req) | ||||
| { | ||||
| 	pr_info("%s: src: w=%d h=%d f=0x%x offs=0x%x mem_id=%d\n", __func__, | ||||
| 		req->src.width, req->src.height, req->src.format, | ||||
| @@ -529,7 +531,6 @@ static void mdp_dump_blit(struct mdp_blit_req *req) | ||||
| 	pr_info("%s: transp_max=0x%08x\n", __func__, req->transp_mask); | ||||
| 	pr_info("%s: flags=%08x\n", __func__, req->flags); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, | ||||
| 		 struct file *src_file, unsigned long src_start, unsigned long src_len, | ||||
| @@ -655,13 +656,6 @@ int mdp_get_bytes_per_pixel(int format) | ||||
|  | ||||
| void mdp_ppp_dump_debug(const struct mdp_info *mdp) | ||||
| { | ||||
| 	mdp_dump_register(mdp, MDP_CMD_STATUS); | ||||
| 	mdp_dump_register(mdp, MDP_DMA_P_CONFIG); | ||||
| 	mdp_dump_register(mdp, MDP_DISPLAY_STATUS); | ||||
| 	mdp_dump_register(mdp, MDP_DMA_P_IBUF_Y_STRIDE); | ||||
| 	mdp_dump_register(mdp, MDP_DMA_P_IBUF_ADDR); | ||||
| 	mdp_dump_register(mdp, MDP_LCDC_UNDERFLOW_CTL); | ||||
| 	mdp_dump_register(mdp, MDP_TFETCH_TEST_MODE); | ||||
| 	mdp_dump_register(mdp, MDP_TFETCH_STATUS); | ||||
| 	mdp_dump_register(mdp, MDP_TFETCH_TILE_COUNT); | ||||
| 	mdp_dump_register(mdp, MDP_TFETCH_FETCH_COUNT); | ||||
| @@ -673,3 +667,235 @@ void mdp_ppp_dump_debug(const struct mdp_info *mdp) | ||||
| 	mdp_dump_register(mdp, MDP_INTR_STATUS); | ||||
| 	mdp_dump_register(mdp, MDP_INTR_ENABLE); | ||||
| } | ||||
|  | ||||
|  | ||||
| /* Splits a blit into two horizontal stripes.  Used to work around MDP bugs */ | ||||
| int mdp_ppp_blit_split_height(struct mdp_info *mdp, const struct mdp_blit_req *req, | ||||
| 	struct file *src_file, unsigned long src_start, unsigned long src_len, | ||||
| 	struct file *dst_file, unsigned long dst_start, unsigned long dst_len) | ||||
| { | ||||
| 	int ret; | ||||
| 	struct mdp_blit_req splitreq; | ||||
| 	int s_x_0, s_x_1, s_w_0, s_w_1, s_y_0, s_y_1, s_h_0, s_h_1; | ||||
| 	int d_x_0, d_x_1, d_w_0, d_w_1, d_y_0, d_y_1, d_h_0, d_h_1; | ||||
|  | ||||
| 	splitreq = *req; | ||||
| 	/* break dest roi at height*/ | ||||
| 	d_x_0 = d_x_1 = req->dst_rect.x; | ||||
| 	d_w_0 = d_w_1 = req->dst_rect.w; | ||||
| 	d_y_0 = req->dst_rect.y; | ||||
| 	if (req->dst_rect.h % 32 == 3) | ||||
| 		d_h_1 = (req->dst_rect.h - 3) / 2 - 1; | ||||
| 	else | ||||
| 		d_h_1 = (req->dst_rect.h - 1) / 2 - 1; | ||||
| 	d_h_0 = req->dst_rect.h - d_h_1; | ||||
| 	d_y_1 = d_y_0 + d_h_0; | ||||
| 	if (req->dst_rect.h == 3) { | ||||
| 		d_h_1 = 2; | ||||
| 		d_h_0 = 2; | ||||
| 		d_y_1 = d_y_0 + 1; | ||||
| 	} | ||||
| 	/* break source roi */ | ||||
| 	if (splitreq.flags & MDP_ROT_90) { | ||||
| 		s_y_0 = s_y_1 = req->src_rect.y; | ||||
| 		s_h_0 = s_h_1 = req->src_rect.h; | ||||
| 		s_x_0 = req->src_rect.x; | ||||
| 		s_w_1 = (req->src_rect.w * d_h_1) / req->dst_rect.h; | ||||
| 		s_w_0 = req->src_rect.w - s_w_1; | ||||
| 		s_x_1 = s_x_0 + s_w_0; | ||||
| 		if (d_h_1 >= 8 * s_w_1) { | ||||
| 			s_w_1++; | ||||
| 			s_x_1--; | ||||
| 		} | ||||
| 	} else { | ||||
| 		s_x_0 = s_x_1 = req->src_rect.x; | ||||
| 		s_w_0 = s_w_1 = req->src_rect.w; | ||||
| 		s_y_0 = req->src_rect.y; | ||||
| 		s_h_1 = (req->src_rect.h * d_h_1) / req->dst_rect.h; | ||||
| 		s_h_0 = req->src_rect.h - s_h_1; | ||||
| 		s_y_1 = s_y_0 + s_h_0; | ||||
| 		if (d_h_1 >= 8 * s_h_1) { | ||||
| 			s_h_1++; | ||||
| 			s_y_1--; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* blit first region */ | ||||
| 	if (((splitreq.flags & MDP_ROT_MASK) == MDP_ROT_90) || | ||||
| 		((splitreq.flags & MDP_ROT_MASK) == 0x0)) { | ||||
| 		splitreq.src_rect.h = s_h_0; | ||||
| 		splitreq.src_rect.y = s_y_0; | ||||
| 		splitreq.dst_rect.h = d_h_0; | ||||
| 		splitreq.dst_rect.y = d_y_0; | ||||
| 		splitreq.src_rect.x = s_x_0; | ||||
| 		splitreq.src_rect.w = s_w_0; | ||||
| 		splitreq.dst_rect.x = d_x_0; | ||||
| 		splitreq.dst_rect.w = d_w_0; | ||||
| 	} else { | ||||
| 		splitreq.src_rect.h = s_h_0; | ||||
| 		splitreq.src_rect.y = s_y_0; | ||||
| 		splitreq.dst_rect.h = d_h_1; | ||||
| 		splitreq.dst_rect.y = d_y_1; | ||||
| 		splitreq.src_rect.x = s_x_0; | ||||
| 		splitreq.src_rect.w = s_w_0; | ||||
| 		splitreq.dst_rect.x = d_x_1; | ||||
| 		splitreq.dst_rect.w = d_w_1; | ||||
| 	} | ||||
| 	ret = mdp_blit_and_wait(mdp, &splitreq, | ||||
| 		src_file, src_start, src_len, | ||||
| 		dst_file, dst_start, dst_len); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
|  | ||||
| 	/* blit second region */ | ||||
| 	if (((splitreq.flags & MDP_ROT_MASK) == MDP_ROT_90) || | ||||
| 		((splitreq.flags & MDP_ROT_MASK) == 0x0)) { | ||||
| 		splitreq.src_rect.h = s_h_1; | ||||
| 		splitreq.src_rect.y = s_y_1; | ||||
| 		splitreq.dst_rect.h = d_h_1; | ||||
| 		splitreq.dst_rect.y = d_y_1; | ||||
| 		splitreq.src_rect.x = s_x_1; | ||||
| 		splitreq.src_rect.w = s_w_1; | ||||
| 		splitreq.dst_rect.x = d_x_1; | ||||
| 		splitreq.dst_rect.w = d_w_1; | ||||
| 	} else { | ||||
| 		splitreq.src_rect.h = s_h_1; | ||||
| 		splitreq.src_rect.y = s_y_1; | ||||
| 		splitreq.dst_rect.h = d_h_0; | ||||
| 		splitreq.dst_rect.y = d_y_0; | ||||
| 		splitreq.src_rect.x = s_x_1; | ||||
| 		splitreq.src_rect.w = s_w_1; | ||||
| 		splitreq.dst_rect.x = d_x_0; | ||||
| 		splitreq.dst_rect.w = d_w_0; | ||||
| 	} | ||||
| 	ret = mdp_blit_and_wait(mdp, &splitreq, | ||||
| 		src_file, src_start, src_len, | ||||
| 		dst_file, dst_start, dst_len); | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| /* Splits a blit into two vertical stripes.  Used to work around MDP bugs */ | ||||
| int mdp_ppp_blit_split_width(struct mdp_info *mdp, const struct mdp_blit_req *req, | ||||
| 	struct file *src_file, unsigned long src_start, unsigned long src_len, | ||||
| 	struct file *dst_file, unsigned long dst_start, unsigned long dst_len) | ||||
| { | ||||
| 	int ret; | ||||
| 	struct mdp_blit_req splitreq; | ||||
| 	int s_x_0, s_x_1, s_w_0, s_w_1, s_y_0, s_y_1, s_h_0, s_h_1; | ||||
| 	int d_x_0, d_x_1, d_w_0, d_w_1, d_y_0, d_y_1, d_h_0, d_h_1; | ||||
| 	splitreq = *req; | ||||
|  | ||||
| 	/* break dest roi at width*/ | ||||
| 	d_y_0 = d_y_1 = req->dst_rect.y; | ||||
| 	d_h_0 = d_h_1 = req->dst_rect.h; | ||||
| 	d_x_0 = req->dst_rect.x; | ||||
| 	if (req->dst_rect.w % 32 == 6) | ||||
| 		d_w_1 = req->dst_rect.w / 2 - 1; | ||||
| 	else if (req->dst_rect.w % 2 == 0) | ||||
| 		d_w_1 = req->dst_rect.w / 2; | ||||
| 	else if (req->dst_rect.w % 32 == 3) | ||||
| 		d_w_1 = (req->dst_rect.w - 3) / 2 - 1; | ||||
| 	else | ||||
| 		d_w_1 = (req->dst_rect.w - 1) / 2 - 1; | ||||
| 	d_w_0 = req->dst_rect.w - d_w_1; | ||||
| 	d_x_1 = d_x_0 + d_w_0; | ||||
| 	if (req->dst_rect.w == 3) { | ||||
| 		d_w_1 = 2; | ||||
| 		d_w_0 = 2; | ||||
| 		d_x_1 = d_x_0 + 1; | ||||
| 	} | ||||
|  | ||||
| 	/* break src roi at height or width*/ | ||||
| 	if (splitreq.flags & MDP_ROT_90) { | ||||
| 		s_x_0 = s_x_1 = req->src_rect.x; | ||||
| 		s_w_0 = s_w_1 = req->src_rect.w; | ||||
| 		s_y_0 = req->src_rect.y; | ||||
| 		s_h_1 = (req->src_rect.h * d_w_1) / req->dst_rect.w; | ||||
| 		s_h_0 = req->src_rect.h - s_h_1; | ||||
| 		s_y_1 = s_y_0 + s_h_0; | ||||
| 		if (d_w_1 >= 8 * s_h_1) { | ||||
| 			s_h_1++; | ||||
| 			s_y_1--; | ||||
| 		} | ||||
| 	} else { | ||||
| 		s_y_0 = s_y_1 = req->src_rect.y; | ||||
| 		s_h_0 = s_h_1 = req->src_rect.h; | ||||
| 		s_x_0 = req->src_rect.x; | ||||
| 		s_w_1 = (req->src_rect.w * d_w_1) / req->dst_rect.w; | ||||
| 		s_w_0 = req->src_rect.w - s_w_1; | ||||
| 		s_x_1 = s_x_0 + s_w_0; | ||||
| 		if (d_w_1 >= 8 * s_w_1) { | ||||
| 			s_w_1++; | ||||
| 			s_x_1--; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* blit first region */ | ||||
| 	if (((splitreq.flags & MDP_ROT_MASK) == MDP_ROT_270) || | ||||
| 		((splitreq.flags & MDP_ROT_MASK) == 0x0)) { | ||||
| 		splitreq.src_rect.h = s_h_0; | ||||
| 		splitreq.src_rect.y = s_y_0; | ||||
| 		splitreq.dst_rect.h = d_h_0; | ||||
| 		splitreq.dst_rect.y = d_y_0; | ||||
| 		splitreq.src_rect.x = s_x_0; | ||||
| 		splitreq.src_rect.w = s_w_0; | ||||
| 		splitreq.dst_rect.x = d_x_0; | ||||
| 		splitreq.dst_rect.w = d_w_0; | ||||
| 	} else { | ||||
| 		splitreq.src_rect.h = s_h_0; | ||||
| 		splitreq.src_rect.y = s_y_0; | ||||
| 		splitreq.dst_rect.h = d_h_1; | ||||
| 		splitreq.dst_rect.y = d_y_1; | ||||
| 		splitreq.src_rect.x = s_x_0; | ||||
| 		splitreq.src_rect.w = s_w_0; | ||||
| 		splitreq.dst_rect.x = d_x_1; | ||||
| 		splitreq.dst_rect.w = d_w_1; | ||||
| 	} | ||||
|  | ||||
| 	if (unlikely((splitreq.dst_rect.h != 1) && | ||||
| 		((splitreq.dst_rect.h % 32 == 3) || | ||||
| 		(splitreq.dst_rect.h % 32) == 1))) | ||||
| 		ret = mdp_ppp_blit_split_height(mdp, &splitreq, | ||||
| 			src_file, src_start, src_len, | ||||
| 			dst_file, dst_start, dst_len); | ||||
| 	else | ||||
| 		ret = mdp_blit_and_wait(mdp, &splitreq, | ||||
| 			src_file, src_start, src_len, | ||||
| 			dst_file, dst_start, dst_len); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
|  | ||||
| 	/* blit second region */ | ||||
| 	if (((splitreq.flags & MDP_ROT_MASK) == MDP_ROT_270) || | ||||
| 		((splitreq.flags & MDP_ROT_MASK) == 0x0)) { | ||||
| 		splitreq.src_rect.h = s_h_1; | ||||
| 		splitreq.src_rect.y = s_y_1; | ||||
| 		splitreq.dst_rect.h = d_h_1; | ||||
| 		splitreq.dst_rect.y = d_y_1; | ||||
| 		splitreq.src_rect.x = s_x_1; | ||||
| 		splitreq.src_rect.w = s_w_1; | ||||
| 		splitreq.dst_rect.x = d_x_1; | ||||
| 		splitreq.dst_rect.w = d_w_1; | ||||
| 	} else { | ||||
| 		splitreq.src_rect.h = s_h_1; | ||||
| 		splitreq.src_rect.y = s_y_1; | ||||
| 		splitreq.dst_rect.h = d_h_0; | ||||
| 		splitreq.dst_rect.y = d_y_0; | ||||
| 		splitreq.src_rect.x = s_x_1; | ||||
| 		splitreq.src_rect.w = s_w_1; | ||||
| 		splitreq.dst_rect.x = d_x_0; | ||||
| 		splitreq.dst_rect.w = d_w_0; | ||||
| 	} | ||||
|  | ||||
| 	if (unlikely((splitreq.dst_rect.h != 1) && | ||||
| 		((splitreq.dst_rect.h % 32 == 3) || | ||||
| 		(splitreq.dst_rect.h % 32) == 1))) | ||||
| 		ret = mdp_ppp_blit_split_height(mdp, &splitreq, | ||||
| 			src_file, src_start, src_len, | ||||
| 			dst_file, dst_start, dst_len); | ||||
| 	else | ||||
| 		ret = mdp_blit_and_wait(mdp, &splitreq, | ||||
| 			src_file, src_start, src_len, | ||||
| 			dst_file, dst_start, dst_len); | ||||
| 	return ret; | ||||
| } | ||||
| @@ -16,6 +16,7 @@ | ||||
| #define _VIDEO_MSM_MDP_PPP_H_ | ||||
|  | ||||
| #include <linux/types.h> | ||||
| #define  PPP_DUMP_BLITS 0 | ||||
|  | ||||
| struct ppp_regs { | ||||
| 	uint32_t src0; | ||||
| @@ -68,11 +69,10 @@ int mdp_ppp_cfg_scale(const struct mdp_info *mdp, struct ppp_regs *regs, | ||||
| 		      struct mdp_rect *src_rect, struct mdp_rect *dst_rect, | ||||
| 		      uint32_t src_format, uint32_t dst_format); | ||||
| int mdp_ppp_load_blur(const struct mdp_info *mdp); | ||||
| void mdp_dump_blit(struct mdp_blit_req *req); | ||||
|  | ||||
| #ifndef CONFIG_MSM_MDP31 | ||||
| int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req, struct ppp_regs *regs); | ||||
| #else | ||||
|  | ||||
| #if defined(CONFIG_MSM_MDP31) || defined(CONFIG_MSM_MDP302) | ||||
| int mdp_ppp_blit_split_width(struct mdp_info *mdp, const struct mdp_blit_req *req, | ||||
| 	struct file *src_file, unsigned long src_start, unsigned long src_len, | ||||
| 	struct file *dst_file, unsigned long dst_start, unsigned long dst_len); | ||||
| @@ -80,6 +80,9 @@ int mdp_ppp_blit_split_height(struct mdp_info *mdp, const struct mdp_blit_req *r | ||||
| 	struct file *src_file, unsigned long src_start, unsigned long src_len, | ||||
| 	struct file *dst_file, unsigned long dst_start, unsigned long dst_len); | ||||
|  | ||||
| #if defined(CONFIG_MSM_MDP302) | ||||
| int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req, struct ppp_regs *regs); | ||||
| #else | ||||
| static inline int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req, | ||||
| 				 struct ppp_regs *regs) | ||||
| { | ||||
| @@ -87,6 +90,10 @@ static inline int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req, | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #else | ||||
| int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req, struct ppp_regs *regs); | ||||
| #endif | ||||
|  | ||||
| int mdp_get_bytes_per_pixel(int format); | ||||
| int mdp_blit_and_wait(struct mdp_info *mdp, struct mdp_blit_req *req, | ||||
| 	struct file *src_file, unsigned long src_start, unsigned long src_len, | ||||
|   | ||||
| @@ -331,233 +331,4 @@ void mdp_ppp_init_scale(const struct mdp_info *mdp) | ||||
| 		load_table(mdp, scale, 0); | ||||
| } | ||||
|  | ||||
| /* Splits a blit into two horizontal stripes.  Used to work around MDP bugs */ | ||||
| int mdp_ppp_blit_split_height(struct mdp_info *mdp, const struct mdp_blit_req *req, | ||||
| 	struct file *src_file, unsigned long src_start, unsigned long src_len, | ||||
| 	struct file *dst_file, unsigned long dst_start, unsigned long dst_len) | ||||
| { | ||||
| 	int ret; | ||||
| 	struct mdp_blit_req splitreq; | ||||
| 	int s_x_0, s_x_1, s_w_0, s_w_1, s_y_0, s_y_1, s_h_0, s_h_1; | ||||
| 	int d_x_0, d_x_1, d_w_0, d_w_1, d_y_0, d_y_1, d_h_0, d_h_1; | ||||
|  | ||||
| 	splitreq = *req; | ||||
| 	/* break dest roi at height*/ | ||||
| 	d_x_0 = d_x_1 = req->dst_rect.x; | ||||
| 	d_w_0 = d_w_1 = req->dst_rect.w; | ||||
| 	d_y_0 = req->dst_rect.y; | ||||
| 	if (req->dst_rect.h % 32 == 3) | ||||
| 		d_h_1 = (req->dst_rect.h - 3) / 2 - 1; | ||||
| 	else | ||||
| 		d_h_1 = (req->dst_rect.h - 1) / 2 - 1; | ||||
| 	d_h_0 = req->dst_rect.h - d_h_1; | ||||
| 	d_y_1 = d_y_0 + d_h_0; | ||||
| 	if (req->dst_rect.h == 3) { | ||||
| 		d_h_1 = 2; | ||||
| 		d_h_0 = 2; | ||||
| 		d_y_1 = d_y_0 + 1; | ||||
| 	} | ||||
| 	/* break source roi */ | ||||
| 	if (splitreq.flags & MDP_ROT_90) { | ||||
| 		s_y_0 = s_y_1 = req->src_rect.y; | ||||
| 		s_h_0 = s_h_1 = req->src_rect.h; | ||||
| 		s_x_0 = req->src_rect.x; | ||||
| 		s_w_1 = (req->src_rect.w * d_h_1) / req->dst_rect.h; | ||||
| 		s_w_0 = req->src_rect.w - s_w_1; | ||||
| 		s_x_1 = s_x_0 + s_w_0; | ||||
| 		if (d_h_1 >= 8 * s_w_1) { | ||||
| 			s_w_1++; | ||||
| 			s_x_1--; | ||||
| 		} | ||||
| 	} else { | ||||
| 		s_x_0 = s_x_1 = req->src_rect.x; | ||||
| 		s_w_0 = s_w_1 = req->src_rect.w; | ||||
| 		s_y_0 = req->src_rect.y; | ||||
| 		s_h_1 = (req->src_rect.h * d_h_1) / req->dst_rect.h; | ||||
| 		s_h_0 = req->src_rect.h - s_h_1; | ||||
| 		s_y_1 = s_y_0 + s_h_0; | ||||
| 		if (d_h_1 >= 8 * s_h_1) { | ||||
| 			s_h_1++; | ||||
| 			s_y_1--; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* blit first region */ | ||||
| 	if (((splitreq.flags & MDP_ROT_MASK) == MDP_ROT_90) || | ||||
| 		((splitreq.flags & MDP_ROT_MASK) == 0x0)) { | ||||
| 		splitreq.src_rect.h = s_h_0; | ||||
| 		splitreq.src_rect.y = s_y_0; | ||||
| 		splitreq.dst_rect.h = d_h_0; | ||||
| 		splitreq.dst_rect.y = d_y_0; | ||||
| 		splitreq.src_rect.x = s_x_0; | ||||
| 		splitreq.src_rect.w = s_w_0; | ||||
| 		splitreq.dst_rect.x = d_x_0; | ||||
| 		splitreq.dst_rect.w = d_w_0; | ||||
| 	} else { | ||||
| 		splitreq.src_rect.h = s_h_0; | ||||
| 		splitreq.src_rect.y = s_y_0; | ||||
| 		splitreq.dst_rect.h = d_h_1; | ||||
| 		splitreq.dst_rect.y = d_y_1; | ||||
| 		splitreq.src_rect.x = s_x_0; | ||||
| 		splitreq.src_rect.w = s_w_0; | ||||
| 		splitreq.dst_rect.x = d_x_1; | ||||
| 		splitreq.dst_rect.w = d_w_1; | ||||
| 	} | ||||
| 	ret = mdp_blit_and_wait(mdp, &splitreq, | ||||
| 		src_file, src_start, src_len, | ||||
| 		dst_file, dst_start, dst_len); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
|  | ||||
| 	/* blit second region */ | ||||
| 	if (((splitreq.flags & MDP_ROT_MASK) == MDP_ROT_90) || | ||||
| 		((splitreq.flags & MDP_ROT_MASK) == 0x0)) { | ||||
| 		splitreq.src_rect.h = s_h_1; | ||||
| 		splitreq.src_rect.y = s_y_1; | ||||
| 		splitreq.dst_rect.h = d_h_1; | ||||
| 		splitreq.dst_rect.y = d_y_1; | ||||
| 		splitreq.src_rect.x = s_x_1; | ||||
| 		splitreq.src_rect.w = s_w_1; | ||||
| 		splitreq.dst_rect.x = d_x_1; | ||||
| 		splitreq.dst_rect.w = d_w_1; | ||||
| 	} else { | ||||
| 		splitreq.src_rect.h = s_h_1; | ||||
| 		splitreq.src_rect.y = s_y_1; | ||||
| 		splitreq.dst_rect.h = d_h_0; | ||||
| 		splitreq.dst_rect.y = d_y_0; | ||||
| 		splitreq.src_rect.x = s_x_1; | ||||
| 		splitreq.src_rect.w = s_w_1; | ||||
| 		splitreq.dst_rect.x = d_x_0; | ||||
| 		splitreq.dst_rect.w = d_w_0; | ||||
| 	} | ||||
| 	ret = mdp_blit_and_wait(mdp, &splitreq, | ||||
| 		src_file, src_start, src_len, | ||||
| 		dst_file, dst_start, dst_len); | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| /* Splits a blit into two vertical stripes.  Used to work around MDP bugs */ | ||||
| int mdp_ppp_blit_split_width(struct mdp_info *mdp, const struct mdp_blit_req *req, | ||||
| 	struct file *src_file, unsigned long src_start, unsigned long src_len, | ||||
| 	struct file *dst_file, unsigned long dst_start, unsigned long dst_len) | ||||
| { | ||||
| 	int ret; | ||||
| 	struct mdp_blit_req splitreq; | ||||
| 	int s_x_0, s_x_1, s_w_0, s_w_1, s_y_0, s_y_1, s_h_0, s_h_1; | ||||
| 	int d_x_0, d_x_1, d_w_0, d_w_1, d_y_0, d_y_1, d_h_0, d_h_1; | ||||
| 	splitreq = *req; | ||||
|  | ||||
| 	/* break dest roi at width*/ | ||||
| 	d_y_0 = d_y_1 = req->dst_rect.y; | ||||
| 	d_h_0 = d_h_1 = req->dst_rect.h; | ||||
| 	d_x_0 = req->dst_rect.x; | ||||
| 	if (req->dst_rect.w % 32 == 6) | ||||
| 		d_w_1 = req->dst_rect.w / 2 - 1; | ||||
| 	else if (req->dst_rect.w % 2 == 0) | ||||
| 		d_w_1 = req->dst_rect.w / 2; | ||||
| 	else if (req->dst_rect.w % 32 == 3) | ||||
| 		d_w_1 = (req->dst_rect.w - 3) / 2 - 1; | ||||
| 	else | ||||
| 		d_w_1 = (req->dst_rect.w - 1) / 2 - 1; | ||||
| 	d_w_0 = req->dst_rect.w - d_w_1; | ||||
| 	d_x_1 = d_x_0 + d_w_0; | ||||
| 	if (req->dst_rect.w == 3) { | ||||
| 		d_w_1 = 2; | ||||
| 		d_w_0 = 2; | ||||
| 		d_x_1 = d_x_0 + 1; | ||||
| 	} | ||||
|  | ||||
| 	/* break src roi at height or width*/ | ||||
| 	if (splitreq.flags & MDP_ROT_90) { | ||||
| 		s_x_0 = s_x_1 = req->src_rect.x; | ||||
| 		s_w_0 = s_w_1 = req->src_rect.w; | ||||
| 		s_y_0 = req->src_rect.y; | ||||
| 		s_h_1 = (req->src_rect.h * d_w_1) / req->dst_rect.w; | ||||
| 		s_h_0 = req->src_rect.h - s_h_1; | ||||
| 		s_y_1 = s_y_0 + s_h_0; | ||||
| 		if (d_w_1 >= 8 * s_h_1) { | ||||
| 			s_h_1++; | ||||
| 			s_y_1--; | ||||
| 		} | ||||
| 	} else { | ||||
| 		s_y_0 = s_y_1 = req->src_rect.y; | ||||
| 		s_h_0 = s_h_1 = req->src_rect.h; | ||||
| 		s_x_0 = req->src_rect.x; | ||||
| 		s_w_1 = (req->src_rect.w * d_w_1) / req->dst_rect.w; | ||||
| 		s_w_0 = req->src_rect.w - s_w_1; | ||||
| 		s_x_1 = s_x_0 + s_w_0; | ||||
| 		if (d_w_1 >= 8 * s_w_1) { | ||||
| 			s_w_1++; | ||||
| 			s_x_1--; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* blit first region */ | ||||
| 	if (((splitreq.flags & MDP_ROT_MASK) == MDP_ROT_270) || | ||||
| 		((splitreq.flags & MDP_ROT_MASK) == 0x0)) { | ||||
| 		splitreq.src_rect.h = s_h_0; | ||||
| 		splitreq.src_rect.y = s_y_0; | ||||
| 		splitreq.dst_rect.h = d_h_0; | ||||
| 		splitreq.dst_rect.y = d_y_0; | ||||
| 		splitreq.src_rect.x = s_x_0; | ||||
| 		splitreq.src_rect.w = s_w_0; | ||||
| 		splitreq.dst_rect.x = d_x_0; | ||||
| 		splitreq.dst_rect.w = d_w_0; | ||||
| 	} else { | ||||
| 		splitreq.src_rect.h = s_h_0; | ||||
| 		splitreq.src_rect.y = s_y_0; | ||||
| 		splitreq.dst_rect.h = d_h_1; | ||||
| 		splitreq.dst_rect.y = d_y_1; | ||||
| 		splitreq.src_rect.x = s_x_0; | ||||
| 		splitreq.src_rect.w = s_w_0; | ||||
| 		splitreq.dst_rect.x = d_x_1; | ||||
| 		splitreq.dst_rect.w = d_w_1; | ||||
| 	} | ||||
|  | ||||
| 	if (unlikely((splitreq.dst_rect.h != 1) && | ||||
| 		((splitreq.dst_rect.h % 32 == 3) || | ||||
| 		(splitreq.dst_rect.h % 32) == 1))) | ||||
| 		ret = mdp_ppp_blit_split_height(mdp, &splitreq, | ||||
| 			src_file, src_start, src_len, | ||||
| 			dst_file, dst_start, dst_len); | ||||
| 	else | ||||
| 		ret = mdp_blit_and_wait(mdp, &splitreq, | ||||
| 			src_file, src_start, src_len, | ||||
| 			dst_file, dst_start, dst_len); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
|  | ||||
| 	/* blit second region */ | ||||
| 	if (((splitreq.flags & MDP_ROT_MASK) == MDP_ROT_270) || | ||||
| 		((splitreq.flags & MDP_ROT_MASK) == 0x0)) { | ||||
| 		splitreq.src_rect.h = s_h_1; | ||||
| 		splitreq.src_rect.y = s_y_1; | ||||
| 		splitreq.dst_rect.h = d_h_1; | ||||
| 		splitreq.dst_rect.y = d_y_1; | ||||
| 		splitreq.src_rect.x = s_x_1; | ||||
| 		splitreq.src_rect.w = s_w_1; | ||||
| 		splitreq.dst_rect.x = d_x_1; | ||||
| 		splitreq.dst_rect.w = d_w_1; | ||||
| 	} else { | ||||
| 		splitreq.src_rect.h = s_h_1; | ||||
| 		splitreq.src_rect.y = s_y_1; | ||||
| 		splitreq.dst_rect.h = d_h_0; | ||||
| 		splitreq.dst_rect.y = d_y_0; | ||||
| 		splitreq.src_rect.x = s_x_1; | ||||
| 		splitreq.src_rect.w = s_w_1; | ||||
| 		splitreq.dst_rect.x = d_x_0; | ||||
| 		splitreq.dst_rect.w = d_w_0; | ||||
| 	} | ||||
|  | ||||
| 	if (unlikely((splitreq.dst_rect.h != 1) && | ||||
| 		((splitreq.dst_rect.h % 32 == 3) || | ||||
| 		(splitreq.dst_rect.h % 32) == 1))) | ||||
| 		ret = mdp_ppp_blit_split_height(mdp, &splitreq, | ||||
| 			src_file, src_start, src_len, | ||||
| 			dst_file, dst_start, dst_len); | ||||
| 	else | ||||
| 		ret = mdp_blit_and_wait(mdp, &splitreq, | ||||
| 			src_file, src_start, src_len, | ||||
| 			dst_file, dst_start, dst_len); | ||||
| 	return ret; | ||||
| } | ||||
|   | ||||
| @@ -32,6 +32,11 @@ | ||||
| #include <linux/clk.h> | ||||
| #include <linux/debugfs.h> | ||||
| #include <linux/dma-mapping.h> | ||||
| #include <linux/android_pmem.h> | ||||
|  | ||||
| extern void start_drawing_late_resume(struct early_suspend *h); | ||||
| static void msmfb_resume_handler(struct early_suspend *h); | ||||
| static void msmfb_resume(struct work_struct *work); | ||||
|  | ||||
| #define MSMFB_DEBUG 1 | ||||
| #ifdef CONFIG_FB_MSM_LOGO | ||||
| @@ -56,13 +61,12 @@ extern int load_565rle_image(char *filename); | ||||
|  | ||||
| #define DLOG(mask, fmt, args...) \ | ||||
| do { \ | ||||
| 	if (msmfb_debug_mask & mask) \ | ||||
| 	if ((msmfb_debug_mask | SUSPEND_RESUME) & mask) \ | ||||
| 		printk(KERN_INFO "msmfb: "fmt, ##args); \ | ||||
| } while (0) | ||||
|  | ||||
| #define BITS_PER_PIXEL(info) (info->fb->var.bits_per_pixel) | ||||
| #define BYTES_PER_PIXEL(info) (info->fb->var.bits_per_pixel >> 3) | ||||
|  | ||||
| static int msmfb_debug_mask; | ||||
| module_param_named(msmfb_debug_mask, msmfb_debug_mask, int, | ||||
| 		   S_IRUGO | S_IWUSR | S_IWGRP); | ||||
| @@ -96,12 +100,59 @@ struct msmfb_info { | ||||
| 	wait_queue_head_t frame_wq; | ||||
| 	struct workqueue_struct *resume_workqueue; | ||||
| 	struct work_struct resume_work; | ||||
| 	struct work_struct msmfb_resume_work; | ||||
| 	struct msmfb_callback dma_callback; | ||||
| 	struct msmfb_callback vsync_callback; | ||||
| 	struct hrtimer fake_vsync; | ||||
| 	ktime_t vsync_request_time; | ||||
| 	unsigned fb_resumed; | ||||
| }; | ||||
|  | ||||
| #ifdef CONFIG_FB_MSM_OVERLAY | ||||
| #define USE_OVERLAY	1 | ||||
| struct overlay_waitevent{ | ||||
| 	uint32_t waked_up; | ||||
| 	wait_queue_head_t event_wait; | ||||
| }; | ||||
| static struct overlay_waitevent overlay_event; | ||||
| DEFINE_MUTEX(overlay_event_lock); | ||||
| #endif | ||||
|  | ||||
| #if (defined(CONFIG_USB_FUNCTION_PROJECTOR) || defined(CONFIG_USB_ANDROID_PROJECTOR)) | ||||
| static spinlock_t fb_data_lock = SPIN_LOCK_UNLOCKED; | ||||
| static struct msm_fb_info msm_fb_data; | ||||
| int msmfb_get_var(struct msm_fb_info *tmp) | ||||
| { | ||||
| 	unsigned long flags; | ||||
| 	spin_lock_irqsave(&fb_data_lock, flags); | ||||
| 	memcpy(tmp, &msm_fb_data, sizeof(msm_fb_data)); | ||||
| 	spin_unlock_irqrestore(&fb_data_lock, flags); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* projector need this, and very much */ | ||||
| int msmfb_get_fb_area(void) | ||||
| { | ||||
| 	int area; | ||||
| 	unsigned long flags; | ||||
| 	spin_lock_irqsave(&fb_data_lock, flags); | ||||
| 	area = msm_fb_data.msmfb_area; | ||||
| 	spin_unlock_irqrestore(&fb_data_lock, flags); | ||||
| 	return area; | ||||
| } | ||||
|  | ||||
| static void msmfb_set_var(unsigned char *addr, int area) | ||||
| { | ||||
| 	unsigned long flags; | ||||
|  | ||||
| 	spin_lock_irqsave(&fb_data_lock, flags); | ||||
| 	msm_fb_data.fb_addr = addr; | ||||
| 	msm_fb_data.msmfb_area = area; | ||||
| 	spin_unlock_irqrestore(&fb_data_lock, flags); | ||||
|  | ||||
| } | ||||
| #endif | ||||
|  | ||||
| static int msmfb_open(struct fb_info *info, int user) | ||||
| { | ||||
| 	return 0; | ||||
| @@ -248,6 +299,18 @@ static void msmfb_pan_update(struct fb_info *info, uint32_t left, uint32_t top, | ||||
|  | ||||
| 	DLOG(SHOW_UPDATES, "update %d %d %d %d %d %d\n", | ||||
| 		left, top, eright, ebottom, yoffset, pan_display); | ||||
|  | ||||
| #if !defined(CONFIG_MACH_HTCLEO) | ||||
| 	// For some reason we need to remove it here, state is 1, we have to look later to this problem | ||||
| 	if (msmfb->sleeping != AWAKE) | ||||
| 		DLOG(SUSPEND_RESUME, "pan_update in state(%d)\n", msmfb->sleeping); | ||||
| #endif | ||||
|  | ||||
| #if (defined(CONFIG_USB_FUNCTION_PROJECTOR) || defined(CONFIG_USB_ANDROID_PROJECTOR)) | ||||
| 	/* Jay, 8/1/09' */ | ||||
| 	msmfb_set_var(msmfb->fb->screen_base, yoffset); | ||||
| #endif | ||||
|  | ||||
| restart: | ||||
| 	spin_lock_irqsave(&msmfb->update_lock, irq_flags); | ||||
|  | ||||
| @@ -264,8 +327,8 @@ restart: | ||||
|  | ||||
| 	sleeping = msmfb->sleeping; | ||||
| 	/* on a full update, if the last frame has not completed, wait for it */ | ||||
| 	if ((pan_display && msmfb->frame_requested != msmfb->frame_done) || | ||||
| 			    sleeping == UPDATING) { | ||||
| 	if (pan_display && (msmfb->frame_requested != msmfb->frame_done || | ||||
| 			    sleeping == UPDATING)) { | ||||
| 		int ret; | ||||
| 		spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); | ||||
| 		ret = wait_event_interruptible_timeout(msmfb->frame_wq, | ||||
| @@ -365,7 +428,6 @@ static void power_on_panel(struct work_struct *work) | ||||
| 		container_of(work, struct msmfb_info, resume_work); | ||||
| 	struct msm_panel_data *panel = msmfb->panel; | ||||
| 	unsigned long irq_flags; | ||||
|  | ||||
| 	mutex_lock(&msmfb->panel_init_lock); | ||||
| 	DLOG(SUSPEND_RESUME, "turning on panel\n"); | ||||
| 	if (msmfb->sleeping == UPDATING) { | ||||
| @@ -385,6 +447,34 @@ error: | ||||
| 	mutex_unlock(&msmfb->panel_init_lock); | ||||
| } | ||||
|  | ||||
| static BLOCKING_NOTIFIER_HEAD(display_chain_head);                                | ||||
| int register_display_notifier(struct notifier_block *nb)                   | ||||
| {                                                                                 | ||||
| 	return blocking_notifier_chain_register(&display_chain_head, nb);         | ||||
| }                                                                                 | ||||
| static int display_notifier_callback(struct notifier_block *nfb,                  | ||||
| 		unsigned long action,                        | ||||
| 		void *ignored)                               | ||||
| {                                                                                 | ||||
| 	struct msmfb_info *msm_fb = (struct msmfb_info *)ignored; | ||||
| 	 | ||||
| 	switch (action) { | ||||
| 	case NOTIFY_MSM_FB: | ||||
| 		printk(KERN_DEBUG "NOTIFY_MSM_FB\n"); | ||||
| 		msmfb_resume(&msm_fb->early_suspend); | ||||
| 		break; | ||||
| 	case NOTIFY_POWER: | ||||
| 		/* nothing to do */ | ||||
| 		break; | ||||
| 	default: | ||||
| 		printk(KERN_ERR "%s: unknown action in 0x%lx\n", | ||||
| 				__func__, action); | ||||
| 		return NOTIFY_BAD; | ||||
| 	} | ||||
| 	return NOTIFY_OK; | ||||
| }                                                                                 | ||||
|  | ||||
| /* -------------------------------------------------------------------------- */  | ||||
| #ifdef CONFIG_HAS_EARLYSUSPEND | ||||
| /* turn off the panel */ | ||||
| static void msmfb_earlier_suspend(struct early_suspend *h) | ||||
| @@ -401,12 +491,12 @@ static void msmfb_earlier_suspend(struct early_suspend *h) | ||||
| 	spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); | ||||
| 	wait_event_timeout(msmfb->frame_wq, | ||||
| 			   msmfb->frame_requested == msmfb->frame_done, HZ/10); | ||||
|  | ||||
| #ifndef CONFIG_MSM_MDP40 | ||||
| 	mdp->dma(mdp, virt_to_phys(msmfb->black), 0, | ||||
| 		 msmfb->fb->var.xres, msmfb->fb->var.yres, 0, 0, | ||||
| 		 NULL, panel->interface_type); | ||||
| 	mdp->dma_wait(mdp, panel->interface_type); | ||||
|  | ||||
| #endif | ||||
| 	/* turn off the panel */ | ||||
| 	panel->blank(panel); | ||||
| } | ||||
| @@ -417,14 +507,31 @@ static void msmfb_suspend(struct early_suspend *h) | ||||
| 						early_suspend); | ||||
| 	struct msm_panel_data *panel = msmfb->panel; | ||||
| 	/* suspend the panel */ | ||||
| #ifdef CONFIG_FB_MSM_OVERLAY | ||||
| 	/*check whether overlay done*/ | ||||
| 	wait_event_interruptible_timeout( | ||||
| 		overlay_event.event_wait, | ||||
| 		(overlay_event.waked_up == ~USE_OVERLAY), | ||||
| 		10*HZ); | ||||
| 	pr_info("wait event : %X\n", overlay_event.waked_up); | ||||
| #endif | ||||
| 	panel->suspend(panel); | ||||
| 	msmfb->fb_resumed = 0; | ||||
| 	mutex_unlock(&msmfb->panel_init_lock); | ||||
| } | ||||
|  | ||||
| static void msmfb_resume(struct early_suspend *h) | ||||
| static void msmfb_resume_handler(struct early_suspend *h) | ||||
| { | ||||
| 	struct msmfb_info *msmfb = container_of(h, struct msmfb_info, | ||||
| 						early_suspend); | ||||
| 					early_suspend); | ||||
| 	queue_work(msmfb->resume_workqueue, &msmfb->msmfb_resume_work); | ||||
| 	wait_event_interruptible_timeout(msmfb->frame_wq, msmfb->fb_resumed==1,HZ/2); | ||||
| } | ||||
|  | ||||
| static void msmfb_resume(struct work_struct *work) | ||||
| { | ||||
| 	struct msmfb_info *msmfb = | ||||
| 		container_of(work, struct msmfb_info, msmfb_resume_work); | ||||
| 	struct msm_panel_data *panel = msmfb->panel; | ||||
| 	unsigned long irq_flags; | ||||
|  | ||||
| @@ -438,6 +545,9 @@ static void msmfb_resume(struct early_suspend *h) | ||||
| 	msmfb->sleeping = WAKING; | ||||
| 	DLOG(SUSPEND_RESUME, "ready, waiting for full update\n"); | ||||
| 	spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); | ||||
| 	start_drawing_late_resume(NULL); | ||||
| 	msmfb->fb_resumed = 1; | ||||
| 	wake_up(&msmfb->frame_wq); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| @@ -483,7 +593,6 @@ static int msmfb_set_par(struct fb_info *info) | ||||
| 		return -1; | ||||
| 	mdp->set_output_format(mdp, var->bits_per_pixel); | ||||
| 	fix->line_length = var->xres * var->bits_per_pixel / 8; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| @@ -502,16 +611,13 @@ int msmfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) | ||||
| 		       var->reserved[1] >> 16, var->reserved[2] & 0xffff, | ||||
| 		       var->reserved[2] >> 16); | ||||
| #endif | ||||
| //		printk("+PAN_DISP1\n"); | ||||
| 		msmfb_pan_update(info, var->reserved[1] & 0xffff, | ||||
| 				 var->reserved[1] >> 16, | ||||
| 				 var->reserved[2] & 0xffff, | ||||
| 				 var->reserved[2] >> 16, var->yoffset, 1); | ||||
| //		printk("-PAN_DISP1\n"); | ||||
| 	} else { | ||||
| //		printk("+PAN_DISP2\n"); | ||||
| 		msmfb_pan_update(info, 0, 0, info->var.xres, info->var.yres, var->yoffset, 1); | ||||
| //		printk("-PAN_DISP2\n"); | ||||
| 		msmfb_pan_update(info, 0, 0, info->var.xres, info->var.yres, | ||||
| 				 var->yoffset, 1); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| @@ -566,6 +672,91 @@ static int msmfb_blit(struct fb_info *info, | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| #ifdef CONFIG_FB_MSM_OVERLAY | ||||
| static int msmfb_overlay_get(struct fb_info *info, void __user *p) | ||||
| { | ||||
| 	struct mdp_overlay req; | ||||
| 	int ret; | ||||
|  | ||||
| 	if (copy_from_user(&req, p, sizeof(req))) | ||||
| 		return -EFAULT; | ||||
|  | ||||
| 	ret = mdp->overlay_get(mdp, info, &req); | ||||
|  | ||||
| 	if (ret) { | ||||
| 		printk(KERN_ERR "%s: ioctl failed \n", | ||||
| 			__func__); | ||||
| 		return ret; | ||||
| 	} | ||||
| 	if (copy_to_user(p, &req, sizeof(req))) { | ||||
| 		printk(KERN_ERR "%s: copy2user failed \n", | ||||
| 			__func__); | ||||
| 		return -EFAULT; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int msmfb_overlay_set(struct fb_info *info, void __user *p) | ||||
| { | ||||
| 	struct mdp_overlay req; | ||||
| 	int ret; | ||||
|  | ||||
| 	if (copy_from_user(&req, p, sizeof(req))) | ||||
| 		return -EFAULT; | ||||
|  | ||||
| 	ret = mdp->overlay_set(mdp, info, &req); | ||||
| 	if (ret) { | ||||
| 		printk(KERN_ERR "%s:ioctl failed \n", | ||||
| 			__func__); | ||||
| 		return ret; | ||||
| 	} | ||||
|  | ||||
| 	if (copy_to_user(p, &req, sizeof(req))) { | ||||
| 		printk(KERN_ERR "%s: copy2user failed \n", | ||||
| 			__func__); | ||||
| 		return -EFAULT; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int msmfb_overlay_unset(struct fb_info *info, unsigned long *argp) | ||||
| { | ||||
| 	int	ret, ndx; | ||||
|  | ||||
| 	ret = copy_from_user(&ndx, argp, sizeof(ndx)); | ||||
| 	if (ret) { | ||||
| 		printk(KERN_ERR "%s:msmfb_overlay_unset ioctl failed \n", | ||||
| 			__func__); | ||||
| 		return ret; | ||||
| 	} | ||||
|  | ||||
| 	return mdp->overlay_unset(mdp, info, ndx); | ||||
| } | ||||
|  | ||||
| static int msmfb_overlay_play(struct fb_info *info, unsigned long *argp) | ||||
| { | ||||
| 	int	ret; | ||||
| 	struct msmfb_overlay_data req; | ||||
| 	struct file *p_src_file = 0; | ||||
|  | ||||
| 	ret = copy_from_user(&req, argp, sizeof(req)); | ||||
| 	if (ret) { | ||||
| 		printk(KERN_ERR "%s:msmfb_overlay_play ioctl failed \n", | ||||
| 			__func__); | ||||
| 		return ret; | ||||
| 	} | ||||
|  | ||||
| 	ret = mdp->overlay_play(mdp, info, &req, &p_src_file); | ||||
|  | ||||
| 	if (p_src_file) | ||||
| 		put_pmem_file(p_src_file); | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| #endif | ||||
|  | ||||
|  | ||||
| DEFINE_MUTEX(mdp_ppp_lock); | ||||
| @@ -580,14 +771,13 @@ static int msmfb_ioctl(struct fb_info *p, unsigned int cmd, unsigned long arg) | ||||
|  | ||||
| 	switch (cmd) { | ||||
| 	case MSMFB_GRP_DISP: | ||||
| 		printk("GRP_DISP\n"); | ||||
| 		mdp->set_grp_disp(mdp, arg); | ||||
| 		break; | ||||
| 	case MSMFB_BLIT: | ||||
| #if PRINT_BLIT_TIME | ||||
| 		t1 = ktime_get(); | ||||
| #endif | ||||
| 		ret =  msmfb_blit(p, argp); | ||||
| 		ret = msmfb_blit(p, argp); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| #if PRINT_BLIT_TIME | ||||
| @@ -596,6 +786,38 @@ static int msmfb_ioctl(struct fb_info *p, unsigned int cmd, unsigned long arg) | ||||
| 		       ktime_to_ns(t2) - ktime_to_ns(t1)); | ||||
| #endif | ||||
| 		break; | ||||
| #ifdef CONFIG_FB_MSM_OVERLAY | ||||
| 	case MSMFB_OVERLAY_GET: | ||||
| 		printk("CONFIG_FB_MSM_OVERLAY\n"); | ||||
| 		//down(&mdp_ppp_lock); | ||||
| 		ret = msmfb_overlay_get(p, argp); | ||||
| 		//up(&mdp_ppp_lock); | ||||
| 		break; | ||||
| 	case MSMFB_OVERLAY_SET: | ||||
| 		printk("MSMFB_OVERLAY_SET\n"); | ||||
| 		//down(&mdp_ppp_lock); | ||||
| 		ret = msmfb_overlay_set(p, argp); | ||||
| 		mutex_lock(&overlay_event_lock); | ||||
| 		overlay_event.waked_up = USE_OVERLAY; | ||||
| 		mutex_unlock(&overlay_event_lock); | ||||
| 		//up(&mdp_ppp_lock); | ||||
| 		break; | ||||
| 	case MSMFB_OVERLAY_UNSET: | ||||
| 		printk("MSMFB_OVERLAY_UNSET\n"); | ||||
| 		//down(&mdp_ppp_lock); | ||||
| 		ret = msmfb_overlay_unset(p, argp); | ||||
| 		mutex_lock(&overlay_event_lock); | ||||
| 		overlay_event.waked_up = ~USE_OVERLAY; | ||||
| 		wake_up(&overlay_event.event_wait); | ||||
| 		mutex_unlock(&overlay_event_lock); | ||||
| 		//up(&mdp_ppp_lock); | ||||
| 		break; | ||||
| 	case MSMFB_OVERLAY_PLAY: | ||||
| 		//down(&mdp_ppp_lock); | ||||
| 		ret = msmfb_overlay_play(p, argp); | ||||
| 		//up(&mdp_ppp_lock); | ||||
| 		break; | ||||
| #endif | ||||
| 	default: | ||||
| 			printk(KERN_INFO "msmfb unknown ioctl: %d\n", cmd); | ||||
| 			return -EINVAL; | ||||
| @@ -658,6 +880,8 @@ static struct file_operations debug_fops = { | ||||
| }; | ||||
| #endif | ||||
|  | ||||
| #define BITS_PER_PIXEL 16 | ||||
|  | ||||
| static void setup_fb_info(struct msmfb_info *msmfb) | ||||
| { | ||||
| 	struct fb_info *fb_info = msmfb->fb; | ||||
| @@ -673,13 +897,14 @@ static void setup_fb_info(struct msmfb_info *msmfb) | ||||
| 	fb_info->fix.type = FB_TYPE_PACKED_PIXELS; | ||||
| 	fb_info->fix.visual = FB_VISUAL_TRUECOLOR; | ||||
| 	fb_info->fix.line_length = msmfb->xres * 2; | ||||
|  | ||||
| 	fb_info->var.xres = msmfb->xres; | ||||
| 	fb_info->var.yres = msmfb->yres; | ||||
| 	fb_info->var.width = msmfb->panel->fb_data->width; | ||||
| 	fb_info->var.height = msmfb->panel->fb_data->height; | ||||
| 	fb_info->var.xres_virtual = msmfb->xres; | ||||
| 	fb_info->var.yres_virtual = msmfb->yres * 2; | ||||
| 	fb_info->var.bits_per_pixel = 16; | ||||
| 	fb_info->var.bits_per_pixel = BITS_PER_PIXEL; | ||||
| 	fb_info->var.accel_flags = 0; | ||||
|  | ||||
| 	fb_info->var.yoffset = 0; | ||||
| @@ -688,8 +913,11 @@ static void setup_fb_info(struct msmfb_info *msmfb) | ||||
| 		/* set the param in the fixed screen, so userspace can't | ||||
| 		 * change it. This will be used to check for the | ||||
| 		 * capability. */ | ||||
|  | ||||
| 		/* FIX ME: every panel support partial update? | ||||
| 		fb_info->fix.reserved[0] = 0x5444; | ||||
| 		fb_info->fix.reserved[1] = 0x5055; | ||||
| 		*/ | ||||
|  | ||||
| 		/* This preloads the value so that if userspace doesn't | ||||
| 		 * change it, it will be a full update */ | ||||
| @@ -717,6 +945,14 @@ static void setup_fb_info(struct msmfb_info *msmfb) | ||||
| 	PP[0] = 0; | ||||
| 	for (r = 1; r < 16; r++) | ||||
| 		PP[r] = 0xffffffff; | ||||
|  | ||||
| 	/* Jay add, 7/1/09' */ | ||||
| #if (defined(CONFIG_USB_FUNCTION_PROJECTOR) || defined(CONFIG_USB_ANDROID_PROJECTOR)) | ||||
| 	msm_fb_data.xres = msmfb->xres; | ||||
| 	msm_fb_data.yres = msmfb->yres; | ||||
| 	printk(KERN_INFO "setup_fb_info msmfb->xres %d, msmfb->yres %d\n", | ||||
| 				msmfb->xres,msmfb->yres); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| static int setup_fbmem(struct msmfb_info *msmfb, struct platform_device *pdev) | ||||
| @@ -781,12 +1017,17 @@ static int msmfb_probe(struct platform_device *pdev) | ||||
| 	if (ret) | ||||
| 		goto error_setup_fbmem; | ||||
|  | ||||
| #if (defined(CONFIG_USB_FUNCTION_PROJECTOR) || defined(CONFIG_USB_ANDROID_PROJECTOR)) | ||||
| 	/* Jay, 8/1/09' */ | ||||
| 	msmfb_set_var(msmfb->fb->screen_base, 0); | ||||
| #endif | ||||
|  | ||||
| 	setup_fb_info(msmfb); | ||||
|  | ||||
| 	spin_lock_init(&msmfb->update_lock); | ||||
| 	mutex_init(&msmfb->panel_init_lock); | ||||
| 	init_waitqueue_head(&msmfb->frame_wq); | ||||
| 	msmfb->resume_workqueue = create_workqueue("panel_on"); | ||||
| 	msmfb->resume_workqueue = create_rt_workqueue("panel_on"); | ||||
| 	if (msmfb->resume_workqueue == NULL) { | ||||
| 		printk(KERN_ERR "failed to create panel_on workqueue\n"); | ||||
| 		ret = -ENOMEM; | ||||
| @@ -799,8 +1040,9 @@ static int msmfb_probe(struct platform_device *pdev) | ||||
| 	wake_lock_init(&msmfb->idle_lock, WAKE_LOCK_IDLE, "msmfb_idle_lock"); | ||||
|  | ||||
| #ifdef CONFIG_HAS_EARLYSUSPEND | ||||
| 	INIT_WORK(&msmfb->msmfb_resume_work, msmfb_resume); | ||||
| 	msmfb->early_suspend.suspend = msmfb_suspend; | ||||
| 	msmfb->early_suspend.resume = msmfb_resume; | ||||
| 	msmfb->early_suspend.resume = msmfb_resume_handler; | ||||
| 	msmfb->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; | ||||
| 	register_early_suspend(&msmfb->early_suspend); | ||||
|  | ||||
| @@ -822,7 +1064,6 @@ static int msmfb_probe(struct platform_device *pdev) | ||||
| 	hrtimer_init(&msmfb->fake_vsync, CLOCK_MONOTONIC, | ||||
| 		     HRTIMER_MODE_REL); | ||||
|  | ||||
|  | ||||
| 	msmfb->fake_vsync.function = msmfb_fake_vsync; | ||||
|  | ||||
| 	ret = register_framebuffer(fb); | ||||
| @@ -831,6 +1072,13 @@ static int msmfb_probe(struct platform_device *pdev) | ||||
|  | ||||
| 	msmfb->sleeping = WAKING; | ||||
|  | ||||
| #ifdef CONFIG_FB_MSM_OVERLAY | ||||
| 	/*init wait event*/ | ||||
| 	init_waitqueue_head(&overlay_event.event_wait); | ||||
| 	/*init waked_up value*/ | ||||
| 	overlay_event.waked_up = ~USE_OVERLAY; | ||||
| #endif | ||||
|  | ||||
| #ifdef CONFIG_FB_MSM_LOGO | ||||
| 	if (!load_565rle_image(INIT_IMAGE_FILE)) { | ||||
| 		/* Flip buffer */ | ||||
| @@ -842,6 +1090,8 @@ static int msmfb_probe(struct platform_device *pdev) | ||||
| 				 msmfb->yres, 0, 1); | ||||
| 	} | ||||
| #endif | ||||
| 	/* Jay, 29/12/08' */ | ||||
| 	display_notifier(display_notifier_callback, NOTIFY_MSM_FB); | ||||
| 	return 0; | ||||
|  | ||||
| error_register_framebuffer: | ||||
| @@ -854,9 +1104,35 @@ error_setup_fbmem: | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| static void msmfb_shutdown(struct platform_device *pdev) | ||||
| { | ||||
| 	struct msm_panel_data *panel = pdev->dev.platform_data; | ||||
| 	struct fb_info *fb; | ||||
| 	struct msmfb_info *msmfb; | ||||
|  | ||||
| 	printk(KERN_INFO "%s\n", __func__); | ||||
| 	fb = registered_fb[0]; | ||||
| 	if (!fb) { | ||||
| 		printk(KERN_ERR "fb0 unavailable.\n"); | ||||
| 		return; | ||||
| 	} | ||||
| 	msmfb = fb->par; | ||||
|  | ||||
| 	mdp->dma(mdp, virt_to_phys(msmfb->black), 0, | ||||
| 			msmfb->fb->var.xres, msmfb->fb->var.yres, 0, 0, | ||||
| 			NULL, panel->interface_type); | ||||
|  | ||||
| 	if (panel->blank) | ||||
| 		panel->blank(panel); | ||||
|  | ||||
| 	if (panel->shutdown) | ||||
| 		panel->shutdown(panel); | ||||
| } | ||||
|  | ||||
| static struct platform_driver msm_panel_driver = { | ||||
| 	/* need to write remove */ | ||||
| 	.probe = msmfb_probe, | ||||
| 	.shutdown = msmfb_shutdown, | ||||
| 	.driver = {.name = "msm_panel"}, | ||||
| }; | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user