From 3db92cb1bf34a5fe1b3533dce788adbdfbfe24c3 Mon Sep 17 00:00:00 2001 From: Jon Benson Date: Sat, 30 Oct 2010 17:47:26 +1100 Subject: [PATCH] Revert "Added video driver cleanup and tweaks by gauner1986. Thanks to huanyu for isolating them." This reverts commit 935a9ce79780605671c8a80e65a9a0dd3746081a. --- drivers/video/msm/Kconfig | 52 +- drivers/video/msm/Makefile | 20 +- drivers/video/msm/gpu/kgsl/kgsl.c | 62 +- drivers/video/msm/gpu/kgsl/kgsl.h | 4 +- drivers/video/msm/gpu/kgsl/kgsl_mmu.c | 3 + drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c | 46 +- drivers/video/msm/hdmi/Makefile | 9 + drivers/video/msm/hdmi/edid.c | 695 +++++++++++ drivers/video/msm/hdmi/fb-hdmi.c | 716 ++++++++++++ drivers/video/msm/hdmi/hdmi_lcdc.c | 633 ++++++++++ drivers/video/msm/hdmi/include/edid.h | 82 ++ drivers/video/msm/hdmi/include/fb-hdmi.h | 27 + drivers/video/msm/hdmi/include/sil902x.h | 399 +++++++ drivers/video/msm/hdmi/include/tpi.h | 411 +++++++ drivers/video/msm/hdmi/silicon-image/Makefile | 7 + .../video/msm/hdmi/silicon-image/av_config.c | 365 ++++++ .../msm/hdmi/silicon-image/debug-sil902x.c | 109 ++ drivers/video/msm/hdmi/silicon-image/hdcp.c | 354 ++++++ drivers/video/msm/hdmi/silicon-image/tpi.c | 960 ++++++++++++++++ drivers/video/msm/hdmi/transmitter.c | 1019 +++++++++++++++++ drivers/video/msm/mddi.c | 229 +++- drivers/video/msm/mddi_client_epson.c | 267 +++++ drivers/video/msm/mddi_client_novb9f6_5582.c | 269 +++++ drivers/video/msm/mddi_hw.h | 24 +- drivers/video/msm/mdp.c | 331 ++++-- drivers/video/msm/mdp_hw.h | 140 ++- drivers/video/msm/mdp_lcdc.c | 199 +++- drivers/video/msm/mdp_ppp.c | 276 ++++- drivers/video/msm/mdp_ppp.h | 13 +- drivers/video/msm/mdp_ppp31.c | 229 ---- drivers/video/msm/msm_fb.c | 318 ++++- 31 files changed, 7759 insertions(+), 509 deletions(-) create mode 100644 drivers/video/msm/hdmi/Makefile create mode 100644 drivers/video/msm/hdmi/edid.c create mode 100644 drivers/video/msm/hdmi/fb-hdmi.c create mode 100644 drivers/video/msm/hdmi/hdmi_lcdc.c create mode 100644 drivers/video/msm/hdmi/include/edid.h create mode 100644 drivers/video/msm/hdmi/include/fb-hdmi.h create mode 100644 drivers/video/msm/hdmi/include/sil902x.h create mode 100644 drivers/video/msm/hdmi/include/tpi.h create mode 100644 drivers/video/msm/hdmi/silicon-image/Makefile create mode 100644 drivers/video/msm/hdmi/silicon-image/av_config.c create mode 100644 drivers/video/msm/hdmi/silicon-image/debug-sil902x.c create mode 100644 drivers/video/msm/hdmi/silicon-image/hdcp.c create mode 100644 drivers/video/msm/hdmi/silicon-image/tpi.c create mode 100644 drivers/video/msm/hdmi/transmitter.c create mode 100644 drivers/video/msm/mddi_client_epson.c create mode 100644 drivers/video/msm/mddi_client_novb9f6_5582.c diff --git a/drivers/video/msm/Kconfig b/drivers/video/msm/Kconfig index 831fdd7e..1ca3622c 100644 --- a/drivers/video/msm/Kconfig +++ b/drivers/video/msm/Kconfig @@ -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 diff --git a/drivers/video/msm/Makefile b/drivers/video/msm/Makefile index 40bc6c9b..bb2447d1 100644 --- a/drivers/video/msm/Makefile +++ b/drivers/video/msm/Makefile @@ -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/ diff --git a/drivers/video/msm/gpu/kgsl/kgsl.c b/drivers/video/msm/gpu/kgsl/kgsl.c index 64c54cf4..7d36973e 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl.c +++ b/drivers/video/msm/gpu/kgsl/kgsl.c @@ -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) { diff --git a/drivers/video/msm/gpu/kgsl/kgsl.h b/drivers/video/msm/gpu/kgsl/kgsl.h index c445f75d..f4ec3c96 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl.h +++ b/drivers/video/msm/gpu/kgsl/kgsl.h @@ -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; diff --git a/drivers/video/msm/gpu/kgsl/kgsl_mmu.c b/drivers/video/msm/gpu/kgsl/kgsl_mmu.c index d4862201..7ffc8b50 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_mmu.c +++ b/drivers/video/msm/gpu/kgsl/kgsl_mmu.c @@ -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); diff --git a/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c b/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c index 8a1f2859..89dd24e5 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c +++ b/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c @@ -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) diff --git a/drivers/video/msm/hdmi/Makefile b/drivers/video/msm/hdmi/Makefile new file mode 100644 index 00000000..a6734d5d --- /dev/null +++ b/drivers/video/msm/hdmi/Makefile @@ -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/ diff --git a/drivers/video/msm/hdmi/edid.c b/drivers/video/msm/hdmi/edid.c new file mode 100644 index 00000000..101afa1d --- /dev/null +++ b/drivers/video/msm/hdmi/edid.c @@ -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 +#include +#include + +#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; +} + diff --git a/drivers/video/msm/hdmi/fb-hdmi.c b/drivers/video/msm/hdmi/fb-hdmi.c new file mode 100644 index 00000000..7daf6d1a --- /dev/null +++ b/drivers/video/msm/hdmi/fb-hdmi.c @@ -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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_HTC_HEADSET_MGR +#include +#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); diff --git a/drivers/video/msm/hdmi/hdmi_lcdc.c b/drivers/video/msm/hdmi/hdmi_lcdc.c new file mode 100644 index 00000000..2a9247fb --- /dev/null +++ b/drivers/video/msm/hdmi/hdmi_lcdc.c @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#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); diff --git a/drivers/video/msm/hdmi/include/edid.h b/drivers/video/msm/hdmi/include/edid.h new file mode 100644 index 00000000..1b7e4587 --- /dev/null +++ b/drivers/video/msm/hdmi/include/edid.h @@ -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 diff --git a/drivers/video/msm/hdmi/include/fb-hdmi.h b/drivers/video/msm/hdmi/include/fb-hdmi.h new file mode 100644 index 00000000..3252ec12 --- /dev/null +++ b/drivers/video/msm/hdmi/include/fb-hdmi.h @@ -0,0 +1,27 @@ +#ifndef _FB_HDMI_H_ +#define _FB_HDMI_H_ + +#include +#include + +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 diff --git a/drivers/video/msm/hdmi/include/sil902x.h b/drivers/video/msm/hdmi/include/sil902x.h new file mode 100644 index 00000000..00edd9d5 --- /dev/null +++ b/drivers/video/msm/hdmi/include/sil902x.h @@ -0,0 +1,399 @@ +#ifndef __SIL902X_H_ +#define __SIL902X_H_ + +#include +#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 diff --git a/drivers/video/msm/hdmi/include/tpi.h b/drivers/video/msm/hdmi/include/tpi.h new file mode 100644 index 00000000..db0271fd --- /dev/null +++ b/drivers/video/msm/hdmi/include/tpi.h @@ -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 diff --git a/drivers/video/msm/hdmi/silicon-image/Makefile b/drivers/video/msm/hdmi/silicon-image/Makefile new file mode 100644 index 00000000..37cda94e --- /dev/null +++ b/drivers/video/msm/hdmi/silicon-image/Makefile @@ -0,0 +1,7 @@ +sil-objs = \ + tpi.o \ + hdcp.o \ + av_config.o \ + debug-sil902x.o + +obj-$(CONFIG_MSM_HDMI) += sil.o diff --git a/drivers/video/msm/hdmi/silicon-image/av_config.c b/drivers/video/msm/hdmi/silicon-image/av_config.c new file mode 100644 index 00000000..2be7d950 --- /dev/null +++ b/drivers/video/msm/hdmi/silicon-image/av_config.c @@ -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 +#include +#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; +} diff --git a/drivers/video/msm/hdmi/silicon-image/debug-sil902x.c b/drivers/video/msm/hdmi/silicon-image/debug-sil902x.c new file mode 100644 index 00000000..1c61b305 --- /dev/null +++ b/drivers/video/msm/hdmi/silicon-image/debug-sil902x.c @@ -0,0 +1,109 @@ +#include +#include + +#include +#include + +#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 diff --git a/drivers/video/msm/hdmi/silicon-image/hdcp.c b/drivers/video/msm/hdmi/silicon-image/hdcp.c new file mode 100644 index 00000000..474a84c5 --- /dev/null +++ b/drivers/video/msm/hdmi/silicon-image/hdcp.c @@ -0,0 +1,354 @@ +#include +#include +#include +#include + +#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 + diff --git a/drivers/video/msm/hdmi/silicon-image/tpi.c b/drivers/video/msm/hdmi/silicon-image/tpi.c new file mode 100644 index 00000000..c8d3cf3f --- /dev/null +++ b/drivers/video/msm/hdmi/silicon-image/tpi.c @@ -0,0 +1,960 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +// FIXME: remove this if unnecessary in the future +#ifdef CONFIG_HTC_HEADSET_MGR +#include +#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 diff --git a/drivers/video/msm/hdmi/transmitter.c b/drivers/video/msm/hdmi/transmitter.c new file mode 100644 index 00000000..5b094b82 --- /dev/null +++ b/drivers/video/msm/hdmi/transmitter.c @@ -0,0 +1,1019 @@ +/* + * Copyright (C) 2009 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "include/fb-hdmi.h" +#include "include/sil902x.h" + +#ifdef CONFIG_HTC_HEADSET_MGR +#include +#endif + +#if 1 +#define HDMI_DBG(s...) printk("[hdmi/tx]" s) +#else +#define HDMI_DBG(s...) do {} while (0) +#endif + +#define HDMI_NAME "SiL902x-hdmi" +//#define HDMI_DEBUGFS + +static struct class *hdmi_class; + +enum { + ESTABLISHED_TIMING_OFFSET = 35, + LONG_DESCR_LEN = 18, + NUM_DETAILED_DESC = 4, + NUM_STANDARD_TIMING = 8, +}; + +#if 1 +int hdmi_read(struct i2c_client *client, u8 cmd) +#else +#define hdmi_read(client, cmd) _hdmi_read(client, cmd, __func__) +int _hdmi_read(struct i2c_client *client, u8 cmd, const char *caller) +#endif +{ + int ret = -EIO, retry = 10; + + while (retry--) { + ret = i2c_smbus_read_byte_data(client, cmd); + if (ret >= 0) + break; + msleep(1); + } +/* + if (retry!=9) + HDMI_DBG("%s, retry=%d, caller=%s\n", __func__, 10-retry, + caller); +*/ + + return ret; +} + +int tpi_readb(struct hdmi_info *hdmi, u8 reg) +{ + int i, ret = -EIO, retrial = 10, timeout = 1; + struct i2c_client *client = hdmi->client; + + for (i = 1 ; i < retrial ; i++) { + ret = i2c_smbus_read_byte_data(client, reg); + if (ret >= 0) + break; + msleep(timeout++); + } + return ret; +} + +int tpi_readb_oneshoot(struct hdmi_info *hdmi, u8 reg) +{ + return i2c_smbus_read_byte_data(hdmi->client, reg); +} + +#if 1 +int hdmi_write_byte(struct i2c_client *client, u8 reg, u8 val) +#else +#define hdmi_write_byte(client, reg, val) \ + _hdmi_write_byte(client, reg, val, __func__) +int _hdmi_write_byte(struct i2c_client *client, u8 reg, u8 val, const char *caller) +#endif +{ + int ret = -EIO, retry = 10; + + while (retry--) { + ret = i2c_smbus_write_byte_data(client, reg, val); + if (ret == 0) + break; + msleep(1); + } +/* + if (retry!=9) HDMI_DBG("%s, retry=%d, caller=%s\n", __func__, + 10 - retry, caller); +*/ + + return ret; +} + +int tpi_writeb(struct hdmi_info *hdmi, u8 reg, u8 val) +{ + int i, ret = -EIO, retrial = 10, timeout = 1; + struct i2c_client *client = hdmi->client; + + for (i = 1 ; i < retrial ; i++) { + ret = i2c_smbus_write_byte_data(client, reg, val); + if (ret == 0) + break; + msleep(timeout); + } + return ret; +} + +int tpi_writeb_oneshot(struct hdmi_info *hdmi, u8 reg, u8 val) +{ + return i2c_smbus_write_byte_data(hdmi->client, reg, val); +} + +int hdmi_enable_int(struct i2c_client *client) +{ + u8 data; + + HDMI_DBG("%s\n", __func__); + data = hdmi_read(client, HDMI_INT_EN); + return hdmi_write_byte(client, HDMI_INT_EN, data | 0x01); +} + +int hdmi_disable_int(struct i2c_client *client) +{ + u8 data; + + HDMI_DBG("%s\n", __func__); + data = hdmi_read(client, HDMI_INT_EN); + return hdmi_write_byte(client, HDMI_INT_EN, data & 0xfe); +} + +/* + * Tx is brought to low-power state, off audio codec. + * i2c alive. Still be able to response to INT. + */ +int hdmi_standby(struct hdmi_info *hdmi) +{ + u8 data; + int ret; + struct i2c_client *client = hdmi->client; + + HDMI_DBG("%s\n", __func__); +#if 0 + /* D2 sleep mode */ + data = hdmi_read(client, HDMI_POWER); + return hdmi_write_byte(client, HDMI_POWER, (data & 0xfc) | 0x02); +#else + if (SLEEP == hdmi->sleeping) + return 0; + /* D3 sleep mode */ + hdmi->sleeping = SLEEP; + hdmi->cable_connected = false; + data = hdmi_write_byte(client, 0x3c, hdmi_read(client, 0x3c) | 1); + data = hdmi_write_byte(client, 0x3c, hdmi_read(client, 0x3c) & ~2); + HDMI_DBG("%s: INT_EN=%02x\n", __func__, hdmi_read(client, 0x3c)); + data = hdmi_read(client, HDMI_POWER); + data |= 4; + ret = hdmi_write_byte(client, HDMI_POWER, data ); + if (ret) + dev_err(&client->dev, + "error on entering D3 sleep mode: into cold mode\n"); +#if 0 + ret = hdmi_write_byte(client, HDMI_POWER, 7); +#else + tpi_writeb_oneshot(hdmi, HDMI_POWER, 7); +#endif +/* + if (ret) + dev_err(&client->dev, + "error on entering D3 sleep mode: set D3 mode\n"); +*/ +#endif + return ret; +} + +int hdmi_wakeup(struct hdmi_info *hdmi) +{ + int err = -EIO; + int ret; + u8 data; + struct i2c_client *client = hdmi->client; + + HDMI_DBG("%s\n", __func__); +#if 0 + data = hdmi_read(client, HDMI_POWER); + err = hdmi_write_byte(client, HDMI_POWER, data & 0xfc); + if (err) + goto exit; +#else + /* Exiting D3 sleep mode */ + ret = hdmi_write_byte(client, 0xc7, 0); + if (ret) + dev_err(&client->dev, + "error on exiting D3 sleep mode: 0xc7=0\n"); + + data = hdmi_read(client, HDMI_POWER); + data = ( data & 0xfc ) ; + ret = hdmi_write_byte(client, HDMI_POWER, data ); + if (ret) + dev_err(&client->dev, + "error on exiting D3 sleep mode: 0x1e=0\n"); + /* Enable insternal TMDS source termination */ + hdmi_write_byte(client, 0xbc, 0x01); + hdmi_write_byte(client, 0xbd, 0x82); + data = hdmi_read(client, 0xbe); + hdmi_write_byte(client, 0xbe, data | 0x01); + + hdmi->sleeping = AWAKE; +#endif + +/* + data = hdmi_read(client, HDMI_SYS_CTL); + dev_info(&client->dev, "%s, HDMI_SYS_CTL=0x%x\n", __func__, data); + err = hdmi_write_byte(client, HDMI_SYS_CTL, 0x01); + if (err) + goto exit; +*/ + return 0; +exit: + dev_err(&client->dev, "%s: fail, err = %d\n", __func__, err); + return err; +} + +static int +hdmi_check_res(struct hdmi_device *hdmi_device, struct fb_var_screeninfo *var) +{ + if (((var->xres == 1280) && (var->yres == 720)) || + ((var->xres == 800) && (var->yres == 600)) || + ((var->xres == 720) && (var->yres == 576)) || + ((var->xres == 720) && (var->yres == 480)) || + ((var->xres == 640) && (var->yres == 480))) { + dev_info(&hdmi_device->dev, "resolution check successfully\n"); + /* check pixel clock also */ + return 0; + } + + return -EINVAL; +} + +static struct msm_lcdc_timing hdmi_lcdc_timing[] = { + [hd_720p] = { + .clk_rate = 74250000, + .hsync_pulse_width = 40, + .hsync_back_porch = 220, + .hsync_front_porch = 110, + .hsync_skew = 0, + .vsync_pulse_width = 5, + .vsync_back_porch = 20, + .vsync_front_porch = 5, + .vsync_act_low = 0, + .hsync_act_low = 0, + .den_act_low = 0, + }, + [svga] = { + .clk_rate = 40000000, + .hsync_pulse_width = 128, + .hsync_back_porch = 88, + .hsync_front_porch = 40, + .hsync_skew = 0, + .vsync_pulse_width = 4, + .vsync_back_porch = 23, + .vsync_front_porch = 1, + .vsync_act_low = 0, + .hsync_act_low = 0, + .den_act_low = 0, + }, + [pal] = { + .clk_rate = 27027000, + .hsync_pulse_width = 64, + .hsync_back_porch = 68, + .hsync_front_porch = 12, + .hsync_skew = 0, + .vsync_pulse_width = 5, + .vsync_back_porch = 39, + .vsync_front_porch = 5, + .vsync_act_low = 1, + .hsync_act_low = 1, + .den_act_low = 0, + }, + [edtv] = { + .clk_rate = 27027000, + .hsync_pulse_width = 62, + .hsync_back_porch = 60, + .hsync_front_porch = 16, + .hsync_skew = 0, + .vsync_pulse_width = 6, + .vsync_back_porch = 30, + .vsync_front_porch = 9, +#if 1 + .vsync_act_low = 1, + .hsync_act_low = 1, +#else + .vsync_act_low = 0, + .hsync_act_low = 0, +#endif + + .den_act_low = 0, + }, + [vga] = { + .clk_rate = 25175000, + .hsync_pulse_width = 96, + .hsync_back_porch = 48, + .hsync_front_porch = 16, + .hsync_skew = 0, + .vsync_pulse_width = 2, + //.vsync_pulse_width = 3, + .vsync_back_porch = 33, + .vsync_front_porch = 10, + .vsync_act_low = 1, + .hsync_act_low = 1, + .den_act_low = 0, + }, +}; + +static struct msm_lcdc_timing * +hdmi_set_res(struct hdmi_device *hdmi_device, struct fb_var_screeninfo *var) +{ + struct hdmi_info *info = container_of(hdmi_device, struct hdmi_info, + hdmi_dev); + + printk(KERN_DEBUG "%s, info->res=%d=(%d x %d)\n", + __func__, info->res, var->xres, var->yres); + if ((var->xres == 1280) && (var->yres == 720)) + info->res = hd_720p; + else if ((var->xres == 800) && (var->yres == 600)) + info->res = svga; + else if ((var->xres == 720) && (var->yres == 576)) + info->res = pal; + else if ((var->xres == 720) && (var->yres == 480)) + info->res = edtv; + else if ((var->xres == 640) && (var->yres == 480)) + info->res = vga; + else + return ERR_PTR(-EINVAL); +/* + if (info->user_playing) + avc_send_avi_info_frames(info); +*/ + return &hdmi_lcdc_timing[info->res]; +} + +static int hdmi_get_cable_state(struct hdmi_device *hdmi_device, int *connect) +{ +#if 0 + struct hdmi_info *info = container_of(hdmi_device, struct hdmi_info, + hdmi_dev); + struct i2c_client *client = info->client; + u8 status; + + *connect = 0; + status = hdmi_read(client, HDMI_INT_STAT); + if (status & HOT_PLUG_STATE) + *connect = 1; +#else + struct hdmi_info *hdmi = + container_of(hdmi_device, struct hdmi_info, hdmi_dev); + *connect = hdmi->cable_connected; +#endif + HDMI_DBG("%s, state=%s\n", __func__, *connect ? "on" : "off" ); + return 0; +} + +static int +hdmi_get_established_timing(struct hdmi_device *hdmi_device, u8 *byte) +{ + struct hdmi_info *info = container_of(hdmi_device, struct hdmi_info, + hdmi_dev); + + HDMI_DBG("%s\n", __func__); + memcpy(byte, &info->edid_buf[ESTABLISHED_TIMING_OFFSET], 3); + return 0; +} + +#if 0 +// FIXME: remove the parameter: data +static u8 hdmi_request_ddc(struct i2c_client *client, int request, u8 data) +{ + int retry = 10; + + HDMI_DBG("%s, request=%d\n", __func__, request); + if (request) { + data = hdmi_read(client, HDMI_SYS_CTL); + hdmi_write_byte(client, HDMI_SYS_CTL, (data | 0x04)); + msleep(1); + hdmi_write_byte(client, HDMI_SYS_CTL, (data | 0x06)); + msleep(1); + } else { + hdmi_write_byte(client, HDMI_SYS_CTL, (data & 0xf9)); + hdmi_write_byte(client, HDMI_SYS_CTL, (data & 0xf9)); + /* make sure bit [2:1] = 00 */ + data = hdmi_read(client, HDMI_SYS_CTL); + while ((data & 0x03) & retry--) + msleep(1); + } + return data; +} + +#else +// FIXME: remove the static varible. if caller need to presev the reg, +// it should use hdmi_read() first. +static u8 hdmi_request_ddc(struct i2c_client *client, int request) +{ + int retry = 10; + static u8 val = 0; + u8 tmp; + + HDMI_DBG("%s, request=%d\n", __func__, request); + + if (request) { + val = hdmi_read(client, HDMI_SYS_CTL); + hdmi_write_byte(client, HDMI_SYS_CTL, (val | 0x04)); + msleep(1); + hdmi_write_byte(client, HDMI_SYS_CTL, (val | 0x06)); + msleep(1); + + } else { + do { + hdmi_write_byte(client, HDMI_SYS_CTL, (val & 0xf9)); + tmp = hdmi_read(client, HDMI_SYS_CTL); + msleep(1); + /* make sure bit [2:1] = 00 */ + } while ((tmp & 0x06) & retry--) ; + } + + return 0; +} +#endif + +static uint8_t timing_id[][3] = { + { 0x81, 0xc0, 1 << 6 }, /* 1280x720 */ + { 0x3b, 0x80, 1 << 5 }, /* 720x576 */ + { 0x3b, 0x00, 1 << 4 }, /* 720x480 */ +}; + +//---------------------------------------------------------------------- +static irqreturn_t hdmi_irq_handler(int irq, void *data) +{ + struct hdmi_info *hdmi = (struct hdmi_info *) data; + HDMI_DBG("%s\n", __func__); + + disable_irq_nosync(hdmi->client->irq); + hdmi->isr_enabled = false; + hdmi->first = true; + if (!hdmi->cable_connected) { + hdmi->timer.expires = jiffies + INTERVAL_HDCP_POLLING; + add_timer(&hdmi->timer); + } + + return IRQ_HANDLED; +} +/* ---------------------------------------------------------------- */ +extern bool hdmifb_suspending; +static int hdmi_panel_blank(struct msm_lcdc_panel_ops *ops) +{ + struct hdmi_info *info = container_of(ops, struct hdmi_info, + hdmi_lcdc_ops); + + HDMI_DBG("%s\n", __func__); + + info->user_playing = false; + info->video_streaming= false; +#if 0 + /* if called from suspending */ + if (hdmifb_suspending) { + /* to avoid timer been revoked after standby */ + HDMI_DBG("suspending=true, disable timer\n"); + cancel_work_sync(&info->polling_work); + del_timer(&info->timer); + + HDMI_DBG("%s\n", __func__); + mutex_lock(&info->lock); + hdmi_standby(info); + info->power(2); + mutex_unlock(&info->lock); + } +#endif + return 0; +} + +static int hdmi_panel_unblank(struct msm_lcdc_panel_ops *ops) +{ + struct hdmi_info *info = container_of(ops, struct hdmi_info, + hdmi_lcdc_ops); + struct i2c_client *client = info->client; + + HDMI_DBG("%s\n", __func__); + clk_set_rate(info->ebi1_clk, 120000000); + if (info->suspending == true) { + HDMI_DBG("%s :actived before panel_init\n", __func__); + msleep(500); + } + + info->user_playing = true; + + return 0; +} + +static int hdmi_panel_init(struct msm_lcdc_panel_ops *ops) +{ + u8 conn; + struct hdmi_info *hd = container_of(ops, struct hdmi_info, + hdmi_lcdc_ops); + struct i2c_client *client = hd->client; + + HDMI_DBG("%s\n", __func__); + + if (hd->hdmi_gpio_on) + hd->hdmi_gpio_on(); + /* Turn-on 5V to ensure hot-plug detection */ + hd->power(5); + +#if 0 + /* For D2 sleep mode */ + hd->power(1); + + ret = hdmi_write_byte(client, HDMI_EN_REG, 0x00); + if (ret < 0) + goto fail; + + hdmi_disable_int(client); + + data = hdmi_read(client, HDMI_POWER); + if (data & 0xfc) { + dev_info(&client->dev, "power state = %d\n", data & 0xfc); + } else { + dev_info(&client->dev, "bring HDMI back\n"); + hdmi_enable_int(client); + HDMI_DBG("hotplug state=%d\n", hd->cable_connected); + } +#else + if (hd->polling) { + mutex_lock(&hd->lock); + hd->power(3); + hdmi_wakeup(hd); + hd->first = true; + mod_timer(&hd->timer, jiffies + INTERVAL_HDCP_POLLING); + conn = hdmi_read(client, HDMI_INT_STAT) & HOT_PLUG_STATE; + tpi_init(hd); +#ifdef CONFIG_HTC_HEADSET_MGR + switch_send_event(BIT_HDMI_AUDIO, conn); +#endif + mutex_unlock(&hd->lock); + } + hd->suspending = false; +#endif + return 0; +/* +fail: + return ret; +*/ +} + +static int hdmi_panel_uninit(struct msm_lcdc_panel_ops *ops) +{ + struct hdmi_info *info = container_of(ops, struct hdmi_info, + hdmi_lcdc_ops); + HDMI_DBG("%s\n", __func__); + + if (info->hdmi_gpio_off) + info->hdmi_gpio_off(); +#if 0 + /* For D2 sleep mode */ + info->power(0); +#endif + + if (hdmifb_suspending) { + /* to avoid timer been revoked after standby */ + HDMI_DBG("suspending=true, disable timer\n"); + cancel_work_sync(&info->polling_work); + del_timer(&info->timer); + flush_scheduled_work(); + + HDMI_DBG("%s\n", __func__); + mutex_lock(&info->lock); + hdmi_standby(info); + info->power(4); + mutex_unlock(&info->lock); + } + info->suspending = true; + + return 0; +} + +int avc_set_video_parm(struct hdmi_info *hdmi); +int avc_set_blank_screen(struct hdmi_info *hdmi); +void hdmi_pre_change(struct hdmi_info *hdmi) { + if (hdmi->sleeping == SLEEP) + return; + mutex_lock(&hdmi->polling_lock); + HDMI_DBG("%s\n", __func__); + avc_set_blank_screen(hdmi); + hdcp_off(hdmi); + mutex_unlock(&hdmi->polling_lock); +} + +void hdmi_post_change(struct hdmi_info *info, struct fb_var_screeninfo *var) +{ + u8 data[4]; + int i, ret, retry = 10; + struct msm_lcdc_timing *timing; + unsigned h_total, v_total, h_curr, v_curr; + + if (info->sleeping == SLEEP) + return; + + mutex_lock(&info->polling_lock); + HDMI_DBG("%s\n", __func__); + timing = &hdmi_lcdc_timing[info->res]; + + h_total = var->xres + timing->hsync_pulse_width + + timing->hsync_back_porch + timing->hsync_front_porch; + v_total = var->yres + timing->vsync_pulse_width + + timing->vsync_back_porch + timing->vsync_front_porch; + /* Waiting for video stream until steady */ + for (i = 0; i < retry ; i++) { + /* TODO: error handling. */ + /* Read current horizontal/vertical info of video */ + data[0] = hdmi_read(info->client, 0x6a); + data[1] = hdmi_read(info->client, 0x6b); + data[2] = hdmi_read(info->client, 0x6c); + data[3] = hdmi_read(info->client, 0x6d); + h_curr = ((int)data[1]) << 8 | data[0]; + v_curr = ((int)data[3]) << 8 | data[2]; + if (h_curr == h_total && v_curr == v_total) + break; + msleep(17); + } + + avc_set_video_parm(info); + avc_send_avi_info_frames(info); + info->video_streaming = true; + + mutex_unlock(&info->polling_lock); +} + +static struct msm_fb_data hdmi_lcdc_fb_data = { +#if 1 + .xres = 1280, + .yres = 720, +#else + .xres = 720, + .yres = 480, +#endif + .width = 94, + .height = 57, + .output_format = 0, +}; + +static struct msm_lcdc_platform_data hdmi_lcdc_platform_data = { + .timing = &hdmi_lcdc_timing[hd_720p], + .fb_id = 0, + .fb_data = &hdmi_lcdc_fb_data, +}; + +static struct platform_device hdmi_lcdc_device = { + .name = "msm_mdp_hdmi", + .id = -1, +}; + +int register_hdmi_client(struct class_interface *interface) +{ + if (!hdmi_class) { + pr_err("mdp: no hdmi_class when register hdmi client\n"); + return -ENODEV; + } + interface->class = hdmi_class; + return class_interface_register(interface); +} + +#if defined(OLD_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_edid_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + static char line[80], buffer[80*8*4]; + static char hextab[] = "0123456789abcdef"; + 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; + + 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); + + return count; +} +#endif + +static ssize_t hdmi_cable_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + int n; + char buffer[80]; + struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; + + n = scnprintf(buffer, 80, "%d\n", hdmi->cable_connected); + n++; + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static ssize_t hdmi_sleeping_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + int n; + char buffer[80]; + struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; + + n = scnprintf(buffer, 80, "%d\n", hdmi->sleeping); + n++; + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +int hdmifb_get_mode(void); +static ssize_t hdmi_fb_mode_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + int n; + char buffer[80]; + struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; + + n = scnprintf(buffer, 80, "%d\n", hdmifb_get_mode()); + n++; + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static ssize_t hdmi_isr_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + int n; + char buffer[80]; + struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; + + n = scnprintf(buffer, 80, "%d\n", hdmi->isr_enabled); + n++; + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static struct file_operations hdmi_fops[] = { + { + .open = hdmi_dbgfs_open, + .read = hdmi_edid_read, + }, + { /* cable*/ + .open = hdmi_dbgfs_open, + .read = hdmi_cable_read, + }, + { /* sleeping */ + .open = hdmi_dbgfs_open, + .read = hdmi_sleeping_read, + }, + { /* fb_mode */ + .open = hdmi_dbgfs_open, + .read = hdmi_fb_mode_read, + }, + { /* isr_enabled */ + .open = hdmi_dbgfs_open, + .read = hdmi_isr_read, + }, + +}; + +static int hdmi_debugfs_init(struct hdmi_info *hdmi) +{ + struct dentry *dent_hdmi; + int ret; + + spin_lock_init(&hdmi_dbgfs_lock); + dent_hdmi = debugfs_create_dir("hdmi", 0); + if (IS_ERR(dent_hdmi)) + return PTR_ERR(dent_hdmi); + debugfs_create_file("edid", 0644, dent_hdmi, hdmi, &hdmi_fops[0]); + debugfs_create_file("cable", 0444, dent_hdmi, hdmi, &hdmi_fops[1]); + debugfs_create_file("sleeping", 0444, dent_hdmi, hdmi, &hdmi_fops[2]); + debugfs_create_file("fb_mode", 0444, dent_hdmi, hdmi, &hdmi_fops[3]); + debugfs_create_file("isr", 0444, dent_hdmi, hdmi, &hdmi_fops[4]); + + return 0; +} +#endif + +static int __init hdmi_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct hdmi_info *hd; + struct hdmi_platform_data *pdata; + int ret = -EIO; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "No supported I2C func\n"); + ret = -ENOTSUPP; + goto exit; + } + + hd = kzalloc(sizeof(*hd), GFP_KERNEL); + if (hd == NULL) { + ret = -ENOMEM; + goto exit; + } + + hd->client = client; + i2c_set_clientdata(client, hd); + mutex_init(&hd->lock); + mutex_init(&hd->lock2); + mutex_init(&hd->polling_lock); + + hd->ebi1_clk = clk_get(NULL, "ebi1_clk"); + if (IS_ERR(hd->ebi1_clk)) { + dev_err(&client->dev, "get ebi1 clk fail\n"); + goto fail_get_ebi1; + } + + pdata = client->dev.platform_data; + if (unlikely(!pdata) || unlikely(!pdata->power)) { + dev_err(&client->dev, "No platform data\n"); + ret = -ENXIO; + goto fail_power; + } else { + if (pdata->hdmi_gpio_on) + pdata->hdmi_gpio_on(); + hd->power = pdata->power; + ret = hd->power(1); + if (ret) { + dev_err(&client->dev, "hdmi power on failed\n"); + ret = -EIO; + goto fail_power; + } + } + + ret = hdmi_write_byte(client, HDMI_EN_REG, 0x00); + if (ret < 0) { + ret = -EIO; + goto fail_hdmi_init; + } + + ret = hdmi_read(client, HDMI_IDENTIFY); + if (ret < 0) { + ret = -EIO; + goto fail_hdmi_init; + } else if (ret != 0xb0) { + dev_err(&client->dev, "can not recognize, 0x%x\n", ret); + ret = -ENXIO; + goto fail_hdmi_init; + } + + hdmi_disable_int(client); + + hd->user_playing = false; + tpi_prepare(hd); + ret = request_irq(client->irq, hdmi_irq_handler, IRQF_TRIGGER_LOW, + client->name, hd); + if (ret) { + /* HDMI did not care if interrupt fail */ + dev_err(&client->dev, "request irq fail, err = %d\n", ret); + } else { + ret = hdmi_enable_int(client); + if (ret) { + free_irq(client->irq, hd); + ret = -ENOTSUPP; + } + } + + dev_info(&client->dev, "hdmi is on line with irq %s\n", + ret ? "Disabled" : "Enabled"); + + /* set up "panel" */ + hd->hdmi_lcdc_ops.init = hdmi_panel_init; + hd->hdmi_lcdc_ops.uninit = hdmi_panel_uninit; + hd->hdmi_lcdc_ops.blank = hdmi_panel_blank; + hd->hdmi_lcdc_ops.unblank = hdmi_panel_unblank; + hd->hdmi_gpio_on = pdata->hdmi_gpio_on; + hd->hdmi_gpio_off = pdata->hdmi_gpio_off; + + hdmi_lcdc_platform_data.panel_ops = &hd->hdmi_lcdc_ops; + hdmi_lcdc_platform_data.fb_resource = &pdata->hdmi_res; + hdmi_lcdc_device.dev.platform_data = &hdmi_lcdc_platform_data; + ret = platform_device_register(&hdmi_lcdc_device); + if (ret) + goto fail_hdmi_init; + + hd->hdmi_dev.check_res = hdmi_check_res; + hd->hdmi_dev.set_res = hdmi_set_res; + hd->hdmi_dev.get_cable_state = hdmi_get_cable_state; + hd->hdmi_dev.get_establish_timing = hdmi_get_established_timing; + + hd->hdmi_dev.dev.parent = &client->dev; + hd->hdmi_dev.dev.class = hdmi_class; + //snprintf(hd->hdmi_dev.dev.bus_id, BUS_ID_SIZE, "hdmi%d", 0); + dev_set_name(&hd->hdmi_dev.dev, "hdmi%d", 0); + ret = device_register(&hd->hdmi_dev.dev); + if (ret) + dev_err(&client->dev, "device register fail\n"); + +#if defined(HDMI_DEBUGFS) + hdmi_debugfs_init(hd); +#endif + /* check any pending interrupt */ + hdmi_irq_handler(client->irq, hd); + return 0; + +fail_hdmi_init: +fail_get_ebi1: + clk_put(hd->ebi1_clk); +fail_power: + kfree(hd); +exit: + dev_err(&client->dev, "%s fail, err = %d\n", __func__, ret); + return ret; +} + +/* -------------------------------------------------------------------- */ + +static const struct i2c_device_id hdmi_id[] = { + {HDMI_NAME, 0}, + { } +}; + +static struct i2c_driver hdmi_driver = { + .probe = hdmi_probe, + /*.remove = hdmi_remove,*/ + .id_table = hdmi_id, + .driver = { + .name = HDMI_NAME, + }, +}; + +static int __init hdmi_init(void) +{ + hdmi_class = class_create(THIS_MODULE, "msm_hdmi"); + if (IS_ERR(hdmi_class)) { + printk(KERN_ERR "Error creating hdmi class\n"); + return PTR_ERR(hdmi_class); + } + return i2c_add_driver(&hdmi_driver); +} + +static void __exit hdmi_exit(void) +{ + i2c_del_driver(&hdmi_driver); +} + +module_init(hdmi_init); +module_exit(hdmi_exit); + +MODULE_DESCRIPTION("Sil902x hdmi driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/msm/mddi.c b/drivers/video/msm/mddi.c index 22bf4ec7..0c39572c 100644 --- a/drivers/video/msm/mddi.c +++ b/drivers/video/msm/mddi.c @@ -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; diff --git a/drivers/video/msm/mddi_client_epson.c b/drivers/video/msm/mddi_client_epson.c new file mode 100644 index 00000000..577b09c8 --- /dev/null +++ b/drivers/video/msm/mddi_client_epson.c @@ -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 +#include +#include +#include +#include +#include +#include +#include + +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); + diff --git a/drivers/video/msm/mddi_client_novb9f6_5582.c b/drivers/video/msm/mddi_client_novb9f6_5582.c new file mode 100644 index 00000000..defd92de --- /dev/null +++ b/drivers/video/msm/mddi_client_novb9f6_5582.c @@ -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 +#include +#include +#include +#include +#include +#include +#include + +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); + diff --git a/drivers/video/msm/mddi_hw.h b/drivers/video/msm/mddi_hw.h index 45cc01fc..16e80126 100644 --- a/drivers/video/msm/mddi_hw.h +++ b/drivers/video/msm/mddi_hw.h @@ -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 { diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c index ad07c945..5e21cf86 100644 --- a/drivers/video/msm/mdp.c +++ b/drivers/video/msm/mdp.c @@ -17,7 +17,6 @@ #include #include -#include #include #include #include @@ -29,11 +28,11 @@ #include #include -#include #include #include "mdp_hw.h" #include "mdp_ppp.h" +#include 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; diff --git a/drivers/video/msm/mdp_hw.h b/drivers/video/msm/mdp_hw.h index d80be50e..c9784f2b 100644 --- a/drivers/video/msm/mdp_hw.h +++ b/drivers/video/msm/mdp_hw.h @@ -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)< #include - +#include #include #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) { diff --git a/drivers/video/msm/mdp_ppp.c b/drivers/video/msm/mdp_ppp.c index 7912a90f..334e2ebb 100644 --- a/drivers/video/msm/mdp_ppp.c +++ b/drivers/video/msm/mdp_ppp.c @@ -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; +} \ No newline at end of file diff --git a/drivers/video/msm/mdp_ppp.h b/drivers/video/msm/mdp_ppp.h index dda21eca..357a7f06 100644 --- a/drivers/video/msm/mdp_ppp.h +++ b/drivers/video/msm/mdp_ppp.h @@ -16,6 +16,7 @@ #define _VIDEO_MSM_MDP_PPP_H_ #include +#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, diff --git a/drivers/video/msm/mdp_ppp31.c b/drivers/video/msm/mdp_ppp31.c index 9e0a3a60..cc7b513c 100644 --- a/drivers/video/msm/mdp_ppp31.c +++ b/drivers/video/msm/mdp_ppp31.c @@ -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; -} diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c index b9fc778e..aa27389d 100644 --- a/drivers/video/msm/msm_fb.c +++ b/drivers/video/msm/msm_fb.c @@ -32,6 +32,11 @@ #include #include #include +#include + +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"}, };