Revert "Added video driver cleanup and tweaks by gauner1986. Thanks to huanyu for isolating them."

This reverts commit 935a9ce79780605671c8a80e65a9a0dd3746081a.
This commit is contained in:
Jon Benson 2010-10-30 17:47:26 +11:00
parent 94b9f0bea1
commit 3db92cb1bf
31 changed files with 7759 additions and 509 deletions

View File

@ -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

View File

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

View File

@ -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) {

View File

@ -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;

View File

@ -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);

View File

@ -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)

View File

@ -0,0 +1,9 @@
msm_hdmi-objs = \
transmitter.o \
hdmi_lcdc.o \
fb-hdmi.o \
edid.o
obj-$(CONFIG_MSM_HDMI) += msm_hdmi.o
obj-$(CONFIG_MSM_HDMI) += silicon-image/

View File

@ -0,0 +1,695 @@
/*
* Copyright (C) 2009 HTC
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* Common function for accessing/debugging EDID data.
* Reference:
http://en.wikipedia.org/wiki/Extended_display_identification_data
*/
#include <linux/device.h>
#include <linux/htc_hdmi.h>
#include <linux/debugfs.h>
#include "include/fb-hdmi.h"
#include "include/sil902x.h"
#if 1
#define EDID_DBG(s...) printk("[hdmi/edid]" s)
#else
#define EDID_DBG(s...) do {} while (0)
#endif
static struct video_mode established_timing_db[] = {
{800, 600, 60, ASPECT(4, 3), PROGRESSIVE, false, "800x600 @ 60 Hz"},
{800, 600, 56, ASPECT(4, 3), PROGRESSIVE, false, "800x600 @ 56 Hz"},
{640, 480, 75, ASPECT(4, 3), PROGRESSIVE, false, "800x600 @ 75 Hz"},
{640, 480, 72, ASPECT(4, 3), PROGRESSIVE, false, "640x480 @ 72 Hz"},
{640, 480, 67, ASPECT(4, 3), PROGRESSIVE, false, "640x480 @ 67 Hz"},
{640, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, "640x480 @ 60 Hz"},
{720, 400, 88, ASPECT(4, 3), PROGRESSIVE, false, "720x400 @ 88 Hz"},
{720, 400, 70, ASPECT(4, 3), PROGRESSIVE, false, "720x400 @ 70 Hz"},
{1280, 1024, 75, ASPECT(4, 3), PROGRESSIVE, false, "1280x1024@75 Hz"},
{1024, 768, 75, ASPECT(4, 3), PROGRESSIVE, false, "1024x768@75 Hz"},
{1024, 768, 70, ASPECT(4, 3), PROGRESSIVE, false, "1024x768@70 Hz"},
{1024, 768, 60, ASPECT(4, 3), PROGRESSIVE, false, "1024x768@60 Hz"},
{1024, 768, 87, ASPECT(4, 3), INTERLACE, false,
"1024x768@87 Hz (Interlaced)"},
{832, 624, 75, ASPECT(4, 3), PROGRESSIVE, false, "832x624@75 Hz"},
{800, 600, 75, ASPECT(4, 3), PROGRESSIVE, false, "800x600@75 Hz"},
{800, 600, 72, ASPECT(4, 3), PROGRESSIVE, false, "800x600@72 Hz"},
{1152, 870, 75, ASPECT(4, 3), PROGRESSIVE, false, "1152x870 @ 75 Hz"},
};
static struct video_mode standard_timing_db[8];
static struct video_mode additional_timing_db[] = {
{640, 480, 60, ASPECT(4, 3), PROGRESSIVE, false,
" 1 DMT0659 4:3 640x480p @ 59.94/60Hz"},
{720, 480, 60, ASPECT(4, 3), PROGRESSIVE, false,
" 2 480p 4:3 720x480p @ 59.94/60Hz"},
{720, 480, 60, ASPECT(16, 9), PROGRESSIVE, false,
" 3 480pH 16:9 720x480p @ 59.94/60Hz"},
{1280, 720, 60, ASPECT(16, 9), PROGRESSIVE, false,
" 4 720p 16:9 1280x720p @ 59.94/60Hz"},
{1920, 1080, 60, ASPECT(4, 3), INTERLACE, false,
" 5 1080i 16:9 1920x1080i @ 59.94/60Hz"},
{720, 480, 60, ASPECT(4, 3), INTERLACE, false,
" 6 480i 4:3 720(1440)x480i @ 59.94/60Hz"},
{720, 480, 60, ASPECT(16, 9), INTERLACE, false,
" 7 480iH 16:9 720(1440)x480i @ 59.94/60Hz"},
{720, 240, 60, ASPECT(4, 3), PROGRESSIVE, false,
" 8 240p 4:3 720(1440)x240p @ 59.94/60Hz"},
{720, 480, 60, ASPECT(16, 9), PROGRESSIVE, false,
" 9 240pH 16:9 720(1440)x240p @ 59.94/60Hz"},
{2880, 480, 60, ASPECT(4, 3), INTERLACE, false,
"10 480i4x 4:3 (2880)x480i @ 59.94/60Hz"},
{2880, 480, 60, ASPECT(16, 9), INTERLACE, false,
"11 480i4xH 16:9 (2880)x480i @ 59.94/60Hz"},
{2880, 240, 60, ASPECT(4, 3), PROGRESSIVE, false,
"12 240p4x 4:3 (2880)x240p @ 59.94/60Hz"},
{2880, 240, 60, ASPECT(16, 9), PROGRESSIVE, false,
"13 240p4xH 16:9 (2880)x240p @ 59.94/60Hz"},
{1440, 480, 60, ASPECT(4, 3), PROGRESSIVE, false,
"14 480p2x 4:3 1440x480p @ 59.94/60Hz"},
{1440, 480, 60, ASPECT(16, 9), PROGRESSIVE, false,
"15 480p2xH 16:9 1440x480p @ 59.94/60Hz"},
{1920, 1080, 60, ASPECT(16, 9), PROGRESSIVE, false,
"16 1080p 16:9 1920x1080p @ 59.94/60Hz"},
{720, 576, 50, ASPECT(4, 3), PROGRESSIVE, false,
"17 576p 4:3 720x576p @ 50Hz"},
{720, 576, 50, ASPECT(16, 9), PROGRESSIVE, false,
"18 576pH 16:9 720x576p @ 50Hz"},
{1280, 720, 50, ASPECT(16, 9), PROGRESSIVE, false,
"19 720p50 16:9 1280x720p @ 50Hz"},
{1920, 1080, 50, ASPECT(16, 9), INTERLACE, false,
"20 1080i25 16:9 1920x1080i @ 50Hz*"},
{1440, 576, 50, ASPECT(4, 3), INTERLACE, false,
"21 576i 4:3 720(1440)x576i @ 50Hz"},
{1440, 576, 50, ASPECT(4, 3), PROGRESSIVE, false,
"22 576iH 16:9 720(1440)x576i @ 50Hz"},
{720, 288, 50, ASPECT(4, 3), PROGRESSIVE, false,
"23 288p 4:3 720(1440)x288p @ 50Hz"},
{720, 288, 50, ASPECT(16, 9), PROGRESSIVE, false,
"24 288pH 16:9 720(1440)x288p @ 50Hz"},
{2880, 576, 50, ASPECT(4, 3), INTERLACE, false,
"25 576i4x 4:3 (2880)x576i @ 50Hz"},
{2880, 576, 50, ASPECT(16, 9), INTERLACE, false,
"26 576i4xH 16:9 (2880)x576i @ 50Hz"},
{2880, 288, 50, ASPECT(4, 3), PROGRESSIVE, false,
"27 288p4x 4:3 (2880)x288p @ 50Hz"},
{2880, 288, 50, ASPECT(16, 9), PROGRESSIVE, false,
"28 288p4xH 16:9 (2880)x288p @ 50Hz"},
{1440, 576, 50, ASPECT(4, 3), PROGRESSIVE, false,
"29 576p2x 4:3 1440x576p @ 50Hz"},
{1440, 576, 50, ASPECT(16, 9), PROGRESSIVE, false,
"30 576p2xH 16:9 1440x576p @ 50Hz"},
{1920, 1080, 50, ASPECT(16, 9), PROGRESSIVE, false,
"31 1080p50 16:9 1920x1080p @ 50Hz"},
{1920, 1080, 24, ASPECT(16, 9), PROGRESSIVE, false,
"32 1080p24 16:9 1920x1080p @ 23.98/24Hz"},
{1920, 1080, 25, ASPECT(16, 9), PROGRESSIVE, false,
"33 1080p25 16:9 1920x1080p @ 25Hz"},
{1920, 1080, 30, ASPECT(16, 9), PROGRESSIVE, false,
"34 1080p30 16:9 1920x1080p @ 29.97/30Hz"},
{2880, 480, 60, ASPECT(4, 3), PROGRESSIVE, false,
"35 480p4x 4:3 (2880)x480p @ 59.94/60Hz"},
{2880, 480, 60, ASPECT(16, 9), PROGRESSIVE, false,
"36 480p4xH 16:9 (2880)x480p @ 59.94/60Hz"},
{2880, 576, 50, ASPECT(4, 3), PROGRESSIVE, false,
"37 576p4x 4:3 (2880)x576p @ 50Hz"},
{2880, 576, 50, ASPECT(16, 9), PROGRESSIVE, false,
"38 576p4xH 16:9 (2880)x576p @ 50Hz"},
{1920, 1080, 50, ASPECT(16, 9), INTERLACE, false,
"39 108Oi25 16:9 1920x1080i(1250 Total) @ 50Hz*"},
{1920, 1080, 100, ASPECT(16, 9), INTERLACE, false,
"40 1080i50 16:9 1920x1080i @ 100Hz"},
{1280, 720, 100, ASPECT(16, 9), PROGRESSIVE, false,
"41 720p100 16:9 1280x720p @ 100Hz"},
{720, 576, 100, ASPECT(4, 3), PROGRESSIVE, false,
"42 576p100 4:3 720x576p @ 100Hz"},
{720, 576, 100, ASPECT(16, 9), PROGRESSIVE, false,
"43 576p100H 16:9 720x576p @ 100Hz"},
{720, 576, 100, ASPECT(4, 3), INTERLACE, false,
"44 576i50 4:3 720(1440)x576i @ 100Hz"},
{720, 576, 100, ASPECT(16, 9), INTERLACE, false,
"45 576i50H 16:9 720(1440)x576i @ 100Hz"},
{1920, 1080, 120, ASPECT(16, 9), INTERLACE, false,
"46 1080i60 16:9 1920x1080i @ 119.88/120Hz"},
{1280, 720, 120, ASPECT(16, 9), PROGRESSIVE, false,
"47 720p120 16:9 1280x720p @ 119.88/120Hz"},
{720, 480, 120, ASPECT(4, 3), PROGRESSIVE, false,
"48 480p119 4:3 720x480p @ 119.88/120Hz"},
{720, 480, 120, ASPECT(16, 9), PROGRESSIVE, false,
"49 480p119H 16:9 720x480p @ 119.88/120Hz"},
{720, 480, 120, ASPECT(4, 3), INTERLACE, false,
"50 480i59 4:3 720(1440)x480i @ 119.88/120Hz"},
{720, 480, 120, ASPECT(16, 9), INTERLACE, false,
"51 480i59H 16:9 720(1440)x480i @ 119.88/120Hz"},
{720, 576, 200, ASPECT(4, 3), PROGRESSIVE, false,
"52 576p200 4:3 720x576p @ 200Hz"},
{720, 576, 200, ASPECT(16, 9), PROGRESSIVE, false,
"53 576p200H 16:9 720x576p @ 200Hz"},
{720, 576, 200, ASPECT(4, 3), INTERLACE, false,
"54 576i100 4:3 720(1440)x576i @ 200Hz"},
{720, 576, 200, ASPECT(16, 9), INTERLACE, false,
"55 576i100H 16:9 720(1440)x576i @ 200Hz"},
{720, 480, 240, ASPECT(4, 3), PROGRESSIVE, false,
"56 480p239 4:3 720x480p @ 239.76/240Hz"},
{720, 480, 240, ASPECT(16, 9), PROGRESSIVE, false,
"57 480p239H 16:9 720x480p @ 239.76/240Hz"},
{720, 480, 240, ASPECT(4, 3), INTERLACE, false,
"58 480i119 4:3 720(1440)x480i @ 239.76/240Hz"},
{720, 480, 240, ASPECT(16, 9), INTERLACE, false,
"59 480i119H 16:9 720(1440)x480i @ 239.76/240Hz"},
{1280, 720, 24, ASPECT(16, 9), PROGRESSIVE, false,
"60 720p24 16:9 1280x720p @ 23.98/24Hz"},
{1280, 720, 25, ASPECT(16, 9), PROGRESSIVE, false,
"61 720p25 16:9 1280x720p @ 25Hz"},
{1280, 720, 30, ASPECT(16, 9), PROGRESSIVE, false,
"62 720p30 16:9 1280x720p @ 29.97/30Hz"},
{1920, 1080, 120, ASPECT(16, 9), PROGRESSIVE, false,
"63 1080p120 16:9 1920x1080 @ 119.88/120Hz"},
};
/* device supported modes in CEA */
enum {
CEA_MODE_640X480P_60HZ_4_3 = 0,
CEA_MODE_720X480P_60HZ_4_3 = 1,
CEA_MODE_720X480P_60HZ_16_9 = 2,
CEA_MODE_1280X720P_60HZ_16_9 = 3,
CEA_MODE_720X576P_50HZ_4_3 = 16,
CEA_MODE_720X576P_50HZ_16_9 = 17,
};
/* device supported modes in established timing */
enum {
ESTABLISHED_MODE_800X600_60HZ = 0,
ESTABLISHED_MODE_640X480_60HZ = 5,
};
int init_edid_info(struct edid_info_struct *edid_info)
{
edid_info->is_valid = false;
mutex_init(&edid_info->access_lock);
return 0;
}
/* Byte 35-37 of block-0 */
static char *established_timing_str[] = {
"800x600 @ 60 Hz",
"800x600 @ 56 Hz",
"640x480 @ 75 Hz",
"640x480 @ 72 Hz",
"640x480 @ 67 Hz",
"640x480 @ 60 Hz",
"720x400 @ 88 Hz",
"720x400 @ 70 Hz",
"1280x1024@75 Hz",
"1024x768@75 Hz",
"1024x768@70 Hz",
"1024x768@60 Hz",
"1024x768@87 Hz (Interlaced)",
"832x624@75 Hz",
"800x600@75 Hz",
"800x600@72 Hz",
"",
"",
"",
"",
"",
"",
"",
"1152x870 @ 75 Hz",
};
/* E-EDID Video data block: */
static char *vdb_modes_str[] = {
" 1 DMT0659 4:3 640x480p @ 59.94/60Hz",
" 2 480p 4:3 720x480p @ 59.94/60Hz",
" 3 480pH 16:9 720x480p @ 59.94/60Hz",
" 4 720p 16:9 1280x720p @ 59.94/60Hz",
" 5 1080i 16:9 1920x1080i @ 59.94/60Hz",
" 6 480i 4:3 720(1440)x480i @ 59.94/60Hz",
" 7 480iH 16:9 720(1440)x480i @ 59.94/60Hz",
" 8 240p 4:3 720(1440)x240p @ 59.94/60Hz",
" 9 240pH 16:9 720(1440)x240p @ 59.94/60Hz",
"10 480i4x 4:3 (2880)x480i @ 59.94/60Hz",
"11 480i4xH 16:9 (2880)x480i @ 59.94/60Hz",
"12 240p4x 4:3 (2880)x240p @ 59.94/60Hz",
"13 240p4xH 16:9 (2880)x240p @ 59.94/60Hz",
"14 480p2x 4:3 1440x480p @ 59.94/60Hz",
"15 480p2xH 16:9 1440x480p @ 59.94/60Hz",
"16 1080p 16:9 1920x1080p @ 59.94/60Hz",
"17 576p 4:3 720x576p @ 50Hz",
"18 576pH 16:9 720x576p @ 50Hz",
"19 720p50 16:9 1280x720p @ 50Hz",
"20 1080i25 16:9 1920x1080i @ 50Hz*",
"21 576i 4:3 720(1440)x576i @ 50Hz",
"22 576iH 16:9 720(1440)x576i @ 50Hz",
"23 288p 4:3 720(1440)x288p @ 50Hz",
"24 288pH 16:9 720(1440)x288p @ 50Hz",
"25 576i4x 4:3 (2880)x576i @ 50Hz",
"26 576i4xH 16:9 (2880)x576i @ 50Hz",
"27 288p4x 4:3 (2880)x288p @ 50Hz",
"28 288p4xH 16:9 (2880)x288p @ 50Hz",
"29 576p2x 4:3 1440x576p @ 50Hz",
"30 576p2xH 16:9 1440x576p @ 50Hz",
"31 1080p50 16:9 1920x1080p @ 50Hz",
"32 1080p24 16:9 1920x1080p @ 23.98/24Hz",
"33 1080p25 16:9 1920x1080p @ 25Hz",
"34 1080p30 16:9 1920x1080p @ 29.97/30Hz",
"35 480p4x 4:3 (2880)x480p @ 59.94/60Hz",
"36 480p4xH 16:9 (2880)x480p @ 59.94/60Hz",
"37 576p4x 4:3 (2880)x576p @ 50Hz",
"38 576p4xH 16:9 (2880)x576p @ 50Hz",
"39 108Oi25 16:9 1920x1080i(1250 Total) @ 50Hz*",
"40 1080i50 16:9 1920x1080i @ 100Hz",
"41 720p100 16:9 1280x720p @ 100Hz",
"42 576p100 4:3 720x576p @ 100Hz",
"43 576p100H 16:9 720x576p @ 100Hz",
"44 576i50 4:3 720(1440)x576i @ 100Hz",
"45 576i50H 16:9 720(1440)x576i @ 100Hz",
"46 1080i60 16:9 1920x1080i @ 119.88/120Hz",
"47 720p120 16:9 1280x720p @ 119.88/120Hz",
"48 480p119 4:3 720x480p @ 119.88/120Hz",
"49 480p119H 16:9 720x480p @ 119.88/120Hz",
"50 480i59 4:3 720(1440)x480i @ 119.88/120Hz",
"51 480i59H 16:9 720(1440)x480i @ 119.88/120Hz",
"52 576p200 4:3 720x576p @ 200Hz",
"53 576p200H 16:9 720x576p @ 200Hz",
"54 576i100 4:3 720(1440)x576i @ 200Hz",
"55 576i100H 16:9 720(1440)x576i @ 200Hz",
"56 480p239 4:3 720x480p @ 239.76/240Hz",
"57 480p239H 16:9 720x480p @ 239.76/240Hz",
"58 480i119 4:3 720(1440)x480i @ 239.76/240Hz",
"59 480i119H 16:9 720(1440)x480i @ 239.76/240Hz",
"60 720p24 16:9 1280x720p @ 23.98/24Hz",
"61 720p25 16:9 1280x720p @ 25Hz",
"62 720p30 16:9 1280x720p @ 29.97/30Hz",
"63 1080p120 16:9 1920x1080 @ 119.88/120Hz",
};
int edid_dump_video_modes(u8 *edid_buf)
{
int i, v1, v2, width, height, ret, aspect;
char *str_aspect[] = { "16:10", "4:3", "5:4", "16:9" };
switch (edid_buf[0]) {
case 0:
pr_info("block type 0: supported mode:\n");
v1 = edid_buf[35] | edid_buf[36] << 8 | edid_buf[37] << 16;
/* Established timing */
pr_info("established timing: {%02x, %02x, %02x}\n",
edid_buf[35], edid_buf[36], edid_buf[37]);
for (i = 0 ; i < 18; i++ ) {
v1 >>= 1;
if (v1 & 1) pr_info("%s\n", established_timing_str[i]);
};
pr_info("Standard timing identification:\n");
/* Standard timing identification */
for (i = 0; i < 8; i++) {
v1 = edid_buf[38+i*2];
v2 = edid_buf[38+i*2+1];
width = v1 * 8 + 248;
aspect = v2 >> 6;
switch (aspect) {
case 0: height = width * 10 / 16; break;
case 1: height = width * 3 / 4; break;
case 2: height = width * 4 / 5; break;
case 3: height = width * 9 / 16; break;
}
pr_info("%dx%d, %s, %d Hz\n", width, height,
str_aspect[aspect], (v2 & ~(3 << 6)) + 60);
}
ret = 0;
break;
case 2:
pr_info("block type 2: supported mode:\n");
pr_info("edid_buf[4]=%x\n", edid_buf[4]);
for( i = 0; i < (edid_buf[4] & 0x1f); i++) {
pr_info("%s\n", vdb_modes_str[edid_buf[5+i] & 0x7f]);
}
ret = 0;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
bool edid_do_checksum(u8 *data)
{
int i;
u8 sum = 0;
for (i = 0; i < EDID_BLOCK_SIZE; i++)
sum += data[i];
EDID_DBG("%s: result=%s\n", __func__, sum ? "fail" : "pass");
return sum ? false : true;
}
static bool edid_check_header(u8 *data)
{
int ret, i = 0;
/* EDID 8 bytes header */
static u8 header[] = {0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0};
for (i = 0; i < ARRAY_SIZE(header); i++)
if (data[i] != header[i])
break;
ret = (i == ARRAY_SIZE(header)) ? true : false;
EDID_DBG("%s: result=%s\n", __func__, ret ? "pass" : "fail");
return ret;
}
bool edid_check_hdmi_sink(struct hdmi_info *hdmi, int block)
{
int ret = false, index = 4;
u8 *data = &hdmi->edid_buf[block * 128];
/* block offset where long descriptors start */
int long_desc_offset = data[LONG_DESCR_PTR_IDX];
int tag_code, data_block_len;
while (index < long_desc_offset) {
tag_code = (data[index] >> 5) & 0x7;
data_block_len = data[index++] & 0x1f;
if (tag_code == VENDOR_SPEC_D_BLOCK &&
data[index] == 0x03 &&
data[index + 1] == 0x0c &&
data[index + 2] == 0x00) {
ret = true;
break;
} else
index += data_block_len;
}
hdmi->edid_info.hdmi_sink = ret;
EDID_DBG("%s: ret=%s\n", __func__, ret ? "yes" : "no");
return ret;
}
struct edid_black_list_info {
u8 mfr_model[4];
u8 prefer_modes[3];
};
struct edid_black_list_info edid_black_list[] = {
{ {0x4c, 0x2d, 0xa5, 0x02}, {0, 0, 0x40} }, // 720p only
{ {0x4c, 0x2d, 0x0d, 0x05}, {0, 0, 0x40} }, // 720p only
//{ {0x5a, 0x63, 0x20, 0x2b}, {0, 0, 0x40} }, // Viewsonic test
};
/* By comparing the Manufacture(0x8, 0x9) and Model field(0xa, 0xb) of EDID,
* to check if the attached TV is a known less-compatibile one.
*/
int edid_fixup_compatibility_list(struct hdmi_info *hdmi)
{
int i, ret = -1;
/* FIXME: magic numbers...*/
for (i = 0; i < ARRAY_SIZE(edid_black_list); i++) {
if (!memcmp(hdmi->edid_buf + 8, edid_black_list[i].mfr_model, 4)){
#if 0
EDID_DBG("%s: found in blacklist %d\n", __func__, i);
EDID_DBG("%s: old timing = {%02x, %02x, %02x}\n",
__func__,
hdmi->edid_buf[35], hdmi->edid_buf[36],
hdmi->edid_buf[37]);
memcpy(hdmi->edid_buf + 35,
edid_black_list[i].prefer_modes, 3);
EDID_DBG("%s: new timing = {%02x, %02x, %02x}\n",
__func__,
hdmi->edid_buf[35], hdmi->edid_buf[36],
hdmi->edid_buf[37]);
#else
EDID_DBG("%s: found in compatibility %d\n", __func__, i);
memcpy(hdmi->edid_buf + 35,
edid_black_list[i].prefer_modes, 3);
#endif
ret = i;
break;
}
}
return ret;
}
u8 edid_simple_parsing(struct hdmi_info *hdmi)
{
u8 *edid_buf = hdmi->edid_buf;
int i, index, ret = -EINVAL;
struct edid_info_struct *edid_info = &hdmi->edid_info;
unsigned v1, width, height, aspect;
unsigned extensions;
EDID_DBG("%s\n", __func__);
// FIXME: integrate with edid_check()
if (!edid_do_checksum(edid_buf)) {
pr_err("%s: checksum error\n", __func__);
//return EDID_CHECKSUM_ERROR;
}
if (!edid_check_header(edid_buf)) {
pr_err("%s: incorrect header\n", __func__);
return INCORRECT_EDID_HEADER;
}
edid_info->under_scan = ((edid_buf[MISC_SUPPORT_IDX]) >> 7) & 0x1;
edid_info->basic_audio = ((edid_buf[MISC_SUPPORT_IDX]) >> 6) & 0x1;
edid_info->ycbcr_4_4_4 = ((edid_buf[MISC_SUPPORT_IDX]) >> 5) & 0x1;
edid_info->ycbcr_4_2_2 = ((edid_buf[MISC_SUPPORT_IDX]) >> 4) & 0x1;
// FIXME: 0x7e
extensions = edid_buf[0x7e];
EDID_DBG("%s: extensions=%d\n", __func__, extensions);
if (!extensions) {
hdmi->edid_info.hdmi_sink = false;
return NO_861_EXTENSIONS;
}
//return;
/* reset all supported */
for (i = 0 ; i < ARRAY_SIZE(additional_timing_db); i++)
additional_timing_db[i].supported = false;
for (i = 0 ; i < ARRAY_SIZE(established_timing_db); i++)
established_timing_db[i].supported = false;
/* Block 0: established timing */
pr_info("established timing: {%02x, %02x, %02x}\n",
edid_buf[35], edid_buf[36], edid_buf[37]);
v1 = edid_buf[35] | edid_buf[36] << 8 | (!!edid_buf[37]) << 16;
for (i = 0 ; i < 17; i++ ) // 17 bits defined in established timing
established_timing_db[i].supported = ((v1 >>= 1) & 1) ;
#if 0
/* standard timing identification */
for (i = 0; i < 8; i++) {
width = edid_buf[38 + i * 2] * 8 + 248;
v1 = edid_buf[38 + i * 2 + 1];
switch (v1 >> 6) {
case 0: height = width * 10 / 16; aspect = ASPECT(16, 10); break;
case 1: height = width * 3 / 4; aspect = ASPECT(4, 3); break;
case 2: height = width * 4 / 5; aspect = ASPECT(5, 4); break;
case 3: height = width * 9 / 16; aspect = ASPECT(16, 9); break;
}
standard_timing_db[i].width = width;
standard_timing_db[i].height = height;
standard_timing_db[i].aspect = aspect;
standard_timing_db[i].refresh_rate = (v1 & ~(3 << 6)) + 60;
standard_timing_db[i].supported = true;
}
#endif
if (extensions == 1) {
EDID_DBG("Block-1: additional timing\n");
/* Block-1: additional timing */
for( i = 0; i < (edid_buf[128 + 4] & 0x1f); i++) {
index = edid_buf[128 + 5 + i] & 0x7f;
additional_timing_db[index-1].supported = true;
EDID_DBG("%s\n", additional_timing_db[index-1].descrption);
}
edid_check_hdmi_sink(hdmi, 1);
} else {
EDID_DBG("Block-2: additional timing\n");
for( i = 0; i < (edid_buf[384 + 4] & 0x1f); i++) {
index = edid_buf[384 + 5 + i] & 0x7f;
additional_timing_db[index-1].supported = true;
EDID_DBG("%s\n", additional_timing_db[index-1].descrption);
}
edid_check_hdmi_sink(hdmi, 3);
}
edid_buf[35] = 0;
edid_buf[36] = 0;
edid_buf[37] = 0;
/* edid_buf[37] bit4: 480p, bit5: 576p, bit6: 720p */
if (additional_timing_db[CEA_MODE_720X480P_60HZ_4_3].supported ||
additional_timing_db[CEA_MODE_720X480P_60HZ_16_9].supported) {
EDID_DBG("decide to support 480P\n");
edid_buf[37] |= (1<<4);
}
if (additional_timing_db[CEA_MODE_720X576P_50HZ_4_3].supported ||
additional_timing_db[CEA_MODE_720X576P_50HZ_16_9].supported) {
EDID_DBG("decide to support 576P\n");
edid_buf[37] |= (1<<5);
}
if (additional_timing_db[CEA_MODE_1280X720P_60HZ_16_9].supported) {
EDID_DBG("decide to support 720P\n");
edid_buf[37] |= (1<<6);
}
if (established_timing_db[ESTABLISHED_MODE_800X600_60HZ].supported) {
EDID_DBG("decide to support 800x600\n");
edid_buf[36] |= (1<<6);
}
if (established_timing_db[ESTABLISHED_MODE_640X480_60HZ].supported) {
EDID_DBG("decide to support 640x480\n");
edid_buf[35] |= (1<<5);
}
edid_fixup_compatibility_list(hdmi);
return ret;
}
// FIXME: modify the checking routines into inline function.
bool edid_is_video_mode_supported(struct video_mode *vmode)
{
int i;
struct video_mode *vmode_db;
vmode_db = established_timing_db;
for (i = 0, vmode_db = established_timing_db;
i < ARRAY_SIZE(established_timing_db); i++)
if ( (vmode->width == vmode_db[i].width) &&
(vmode->height == vmode_db[i].height) &&
(vmode->refresh_rate == vmode_db[i].refresh_rate ) &&
(vmode_db[i].interlaced == PROGRESSIVE) &&
(vmode_db[i].supported == true ))
return true;
for (i = 0, vmode_db = standard_timing_db;
i < ARRAY_SIZE(standard_timing_db); i++)
if ( (vmode->width == vmode_db[i].width) &&
(vmode->height == vmode_db[i].height) &&
(vmode->refresh_rate == vmode_db[i].refresh_rate ) &&
(vmode_db[i].interlaced == PROGRESSIVE) &&
(vmode_db[i].supported == true ))
return true;
for (i = 0, vmode_db = additional_timing_db;
i < ARRAY_SIZE(additional_timing_db); i++)
if ( (vmode->width == vmode_db[i].width) &&
(vmode->height == vmode_db[i].height) &&
(vmode->refresh_rate == vmode_db[i].refresh_rate ) &&
(vmode_db[i].interlaced == PROGRESSIVE) &&
(vmode_db[i].supported == true ))
return true;
return false;
}
bool edid_check_sink_type(struct hdmi_info *hdmi)
{
EDID_DBG("%s: ret=%d\n", __func__, hdmi->edid_info.hdmi_sink);
return hdmi->edid_info.hdmi_sink;
}
int edid_dump_hex(u8 *src, int src_size, char *output, int output_size)
{
char line[80];
static char hextab[] = "0123456789abcdef";
int i, j, n = 0, v, len, offset, line_size;
len = src_size;
memset(line, ' ', 79);
line[79] = '\0';
offset = strlen("0000 | ");
line_size = offset + 3 * 16 + 1;
for (i = 0; i < len / 16 ; i++) {
scnprintf(line, offset + 1, "%04x | ", (i << 4));
for (j = 0; j < 16 ; j++) {
v = src[i * 16 + j];
line[offset + j * 3] = hextab[v / 16];
line[offset + j * 3 + 1] = hextab[v % 16];
}
line[line_size - 1] = '\n';
strncpy(output+ n, line, line_size);
if ((n + line_size) > output_size)
break;
else
n += line_size;
}
return n;
}
/*============================================================================*/
static ssize_t edid_dbg_open(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
return 0;
}
static char hex_buff[2048];
static ssize_t edid_buffered_read(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
int n;
int extensions;
struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
extensions = hdmi->edid_buf[0x7e] + 1;
edid_dump_video_modes(hdmi->edid_buf);// fixme: crashed
if (extensions == 1)
edid_dump_video_modes(hdmi->edid_buf + 128);
else
edid_dump_video_modes(hdmi->edid_buf + 384);
//edid_simple_parsing(hdmi); // FIXME: crashed...
n = edid_dump_hex(hdmi->edid_buf, (hdmi->edid_buf[0x7e] + 1) * 128,
hex_buff, 2048);
return simple_read_from_buffer(buf, count, ppos, hex_buff, n);
}
static struct file_operations edid_debugfs_fops[] = {
{
.open = edid_dbg_open,
.read = edid_buffered_read,
.write = NULL,
}
};
// TODO: error handling
int edid_debugfs_init(struct hdmi_info *hdmi)
{
//int ret;
struct dentry *edid_dent;
edid_dent = debugfs_create_dir("edid", hdmi->debug_dir);
if (IS_ERR(edid_dent))
return PTR_ERR(edid_dent);
debugfs_create_file("hex_dump", 0444, edid_dent, hdmi,
&edid_debugfs_fops[0]);
return 0;
}

View File

@ -0,0 +1,716 @@
/*
* Copyright (C) 2009 HTC
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Referenced from drivers/video/msm/msm_fb.c, Google Incorporated.
*
*/
#define DEBUG
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/fb.h>
#include <linux/delay.h>
#include <linux/freezer.h>
#include <linux/wait.h>
#include <linux/wakelock.h>
#include <linux/earlysuspend.h>
#include <linux/msm_mdp.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <mach/msm_fb.h>
#include <mach/board.h>
#include <linux/workqueue.h>
#include <linux/clk.h>
#include <linux/debugfs.h>
#include <linux/dma-mapping.h>
#include <linux/htc_hdmi.h>
#ifdef CONFIG_HTC_HEADSET_MGR
#include <mach/htc_headset_mgr.h>
#endif
#include "include/fb-hdmi.h"
#include "include/sil902x.h"
#if 1
#define HDMI_DBG(s...) printk("[hdmi/fb]" s)
#else
#define HDMI_DBG(s...) do {} while (0)
#endif
struct update_info_t {
int left;
int top;
int eright; /* exclusive */
int ebottom; /* exclusive */
unsigned yoffset;
};
struct hdmifb_info {
struct fb_info *fb;
struct msm_panel_data *panel;
struct notifier_block fb_hdmi_event;
struct msmfb_callback dma_callback;
struct msmfb_callback vsync_callback;
struct update_info_t update_info;
struct early_suspend earlier_suspend;
struct early_suspend early_suspend;
spinlock_t update_lock;
int xres;
int yres;
unsigned long state;
atomic_t use_count;
};
static struct mdp_device *mdp;
static struct hdmi_device *hdmi;
static unsigned PP[16];
void hdmi_pre_change(struct hdmi_info *hdmi);
void hdmi_post_change(struct hdmi_info *info, struct fb_var_screeninfo *var);
static int hdmifb_open(struct fb_info *info, int user)
{
return 0;
}
static int hdmifb_release(struct fb_info *info, int user)
{
return 0;
}
static int hdmifb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
u32 size;
if (mdp->check_output_format(mdp, var->bits_per_pixel))
return -EINVAL;
if (hdmi->check_res(hdmi, var))
return -EINVAL;
size = var->xres_virtual * var->yres_virtual *
(var->bits_per_pixel >> 3);
if (size > info->fix.smem_len)
return -EINVAL;
return 0;
}
static int hdmifb_set_par(struct fb_info *info)
{
struct fb_var_screeninfo *var = &info->var;
struct fb_fix_screeninfo *fix = &info->fix;
struct hdmifb_info *hdmi_fb = info->par;
struct msm_panel_data *panel = hdmi_fb->panel;
struct msm_lcdc_timing *timing;
struct hdmi_info *hinfo = container_of(hdmi, struct hdmi_info,
hdmi_dev);
HDMI_DBG("%s\n", __func__);
/* we only support RGB ordering for now */
if (var->bits_per_pixel == 32 || var->bits_per_pixel == 24) {
var->red.offset = 0;
var->red.length = 8;
var->green.offset = 8;
var->green.length = 8;
var->blue.offset = 16;
var->blue.length = 8;
} else if (var->bits_per_pixel == 16) {
var->red.offset = 11;
var->red.length = 5;
var->green.offset = 5;
var->green.length = 6;
var->blue.offset = 0;
var->blue.length = 5;
} else
return -1;
HDMI_DBG("set res (%d, %d)\n", var->xres, var->yres);
timing = hdmi->set_res(hdmi, var);
panel->adjust_timing(panel, timing, var->xres, var->yres);
hdmi_post_change(hinfo, var);
mdp->set_output_format(mdp, var->bits_per_pixel);
hdmi_fb->xres = var->xres;
hdmi_fb->yres = var->yres;
fix->line_length = var->xres * var->bits_per_pixel / 8;
return 0;
}
/* core update function */
static void
hdmifb_pan_update(struct fb_info *info, uint32_t left, uint32_t top,
uint32_t eright, uint32_t ebottom, uint32_t yoffset)
{
struct hdmifb_info *hdmi_fb = info->par;
struct msm_panel_data *panel = hdmi_fb->panel;
unsigned long irq_flags;
/* printk(KERN_DEBUG "%s\n", __func__); */
if ((test_bit(fb_enabled, &hdmi_fb->state) == 0) ||
(test_bit(hdmi_enabled, &hdmi_fb->state) == 0))
return;
spin_lock_irqsave(&hdmi_fb->update_lock, irq_flags);
hdmi_fb->update_info.left = left;
hdmi_fb->update_info.top = top;
hdmi_fb->update_info.eright = eright;
hdmi_fb->update_info.ebottom = ebottom;
hdmi_fb->update_info.yoffset = yoffset;
spin_unlock_irqrestore(&hdmi_fb->update_lock, irq_flags);
panel->request_vsync(panel, &hdmi_fb->vsync_callback);
}
/* fb ops, fb_pan_display */
static int
hdmifb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
{
/* full update */
hdmifb_pan_update(info, 0, 0, info->var.xres, info->var.yres,
var->yoffset);
return 0;
}
static void hdmifb_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
{
cfb_fillrect(p, rect);
hdmifb_pan_update(p, rect->dx, rect->dy, rect->dx + rect->width,
rect->dy + rect->height, 0);
}
static void hdmifb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
{
cfb_copyarea(p, area);
hdmifb_pan_update(p, area->dx, area->dy, area->dx + area->width,
area->dy + area->height, 0);
}
static void hdmifb_imageblit(struct fb_info *p, const struct fb_image *image)
{
cfb_imageblit(p, image);
hdmifb_pan_update(p, image->dx, image->dy, image->dx + image->width,
image->dy + image->height, 0);
}
static int hdmifb_change_mode(struct fb_info *info, unsigned int mode)
{
struct hdmifb_info *hdmi_fb = info->par;
/* printk(KERN_DEBUG "%s mode = %d\n", __func__, mode); */
if (mode)
set_bit(hdmi_mode, &hdmi_fb->state);
else
clear_bit(hdmi_mode, &hdmi_fb->state);
return 0;
}
struct hdmifb_info *_hdmi_fb;
int hdmifb_get_mode(void)
{
return test_bit(hdmi_mode, &_hdmi_fb->state);
}
bool hdmifb_suspending = false;
static int hdmifb_pause(struct fb_info *fb, unsigned int mode)
{
int ret = 0;
struct hdmifb_info *hdmi_fb = fb->par;
struct msm_panel_data *panel = hdmi_fb->panel;
struct hdmi_info *info = container_of(hdmi, struct hdmi_info,
hdmi_dev);
pr_info("%s: %d %s\n", __func__, atomic_read(&hdmi_fb->use_count),
mode == 1 ? "pause" : "resume");
if (mode == 1) {
hdmifb_suspending = false;
HDMI_DBG("%s: hdmifb_suspending = false\n", __func__);
/* pause */
if (atomic_read(&hdmi_fb->use_count) == 0)
goto done;
if (atomic_dec_return(&hdmi_fb->use_count) == 0) {
hdmi_pre_change(info);
ret = panel->blank(panel);
clear_bit(hdmi_enabled, &hdmi_fb->state);
#ifdef CONFIG_HTC_HEADSET_MGR
switch_send_event(BIT_HDMI_AUDIO, 0);
#endif
}
} else if (mode == 0) {
/* resume */
if (atomic_inc_return(&hdmi_fb->use_count) == 1) {
hdmi_pre_change(info);
ret = panel->unblank(panel);
/*
// set timing again to prevent TV been out of range
var = &fb->var;
timing = hdmi->set_res(hdmi, var);
panel->adjust_timing(panel, timing, var->xres, var->yres);
hdmi_post_change(info, var);
*/
set_bit(hdmi_enabled, &hdmi_fb->state);
#ifdef CONFIG_HTC_HEADSET_MGR
switch_send_event(BIT_HDMI_AUDIO, 1);
#endif
}
} else
ret = -EINVAL;
done:
return ret;
}
static int hdmifb_blit(struct fb_info *info, void __user *p)
{
struct mdp_blit_req req;
struct mdp_blit_req_list req_list;
int i;
int ret;
if (copy_from_user(&req_list, p, sizeof(req_list)))
return -EFAULT;
for (i = 0; i < req_list.count; i++) {
struct mdp_blit_req_list *list =
(struct mdp_blit_req_list *)p;
if (copy_from_user(&req, &list->req[i], sizeof(req)))
return -EFAULT;
req.flags |= MDP_DITHER;
ret = mdp->blit(mdp, info, &req);
if (ret)
return ret;
}
return 0;
}
enum ioctl_cmd_index {
CMD_SET_MODE,
CMD_GET_MODE,
CMD_DISABLE,
CMD_ENABLE,
CMD_GET_STATE,
CMD_BLIT,
CMD_CABLE_STAT,
CMD_ESTABLISH_TIMING,
};
static char *cmd_str[] = {
"HDMI_SET_MODE",
"HDMI_GET_MODE",
"HDMI_DISABLE",
"HDMI_ENABLE",
"HDMI_GET_STATE",
"HDMI_BLIT",
"HDMI_CABLE_STAT",
"HDMI_ESTABLISH_TIMING",
};
static int hdmifb_ioctl(struct fb_info *p, unsigned int cmd, unsigned long arg)
{
struct hdmifb_info *hdmi_fb = p->par;
void __user *argp = (void __user *)arg;
unsigned int val;
int ret = -EINVAL;
struct hdmi_info *hinfo = container_of(hdmi, struct hdmi_info,
hdmi_dev);
/*
if (cmd != HDMI_BLIT)
HDMI_DBG("%s, cmd=%d=%s\n", __func__, cmd - HDMI_SET_MODE,
cmd_str[cmd-HDMI_SET_MODE]);
*/
switch (cmd) {
case HDMI_SET_MODE:
get_user(val, (unsigned __user *) arg);
//pr_info("[hdmi] SET_MODE: %d\n", val);
ret = hdmifb_change_mode(p, val);
break;
case HDMI_GET_MODE:
/*
pr_info("[hdmi] GET_MODE: %d\n",
test_bit(hdmi_mode, &hdmi_fb->state));
*/
ret = put_user(test_bit(hdmi_mode, &hdmi_fb->state),
(unsigned __user *) arg);
break;
case HDMI_DISABLE:
get_user(val, (unsigned __user *) arg);
ret = hdmifb_pause(p, 1);
break;
case HDMI_ENABLE:
get_user(val, (unsigned __user *) arg);
ret = hdmifb_pause(p, 0);
break;
case HDMI_GET_STATE:
ret = put_user(test_bit(hdmi_enabled, &hdmi_fb->state),
(unsigned __user *) arg);
break;
case HDMI_BLIT:
if (test_bit(hdmi_enabled, &hdmi_fb->state))
ret = hdmifb_blit(p, argp);
else
ret = -EPERM;
break;
case HDMI_CABLE_STAT: {
int connect;
ret = hdmi->get_cable_state(hdmi, &connect);
ret = put_user(connect, (unsigned __user *) arg);
break;
}
case HDMI_ESTABLISH_TIMING: {
u8 tmp[3];
hdmi->get_establish_timing(hdmi, tmp);
ret = copy_to_user((unsigned __user *) arg, tmp, 3);
if (ret)
ret = -EFAULT;
break;
}
case HDMI_GET_EDID:
ret = copy_to_user((unsigned __user *) arg,
hinfo->edid_buf, 512);
break;
case HDMI_GET_DISPLAY_INFO: {
struct display_info dinfo;
u8 *ptr = hinfo->edid_buf;
dinfo.visible_width =
(((u32)ptr[68] & 0xf0) << 4) | ptr[66];
dinfo.visible_height =
(((u32)ptr[68] & 0x0f) << 8) | ptr[67];
dinfo.resolution_width =
(((u32)ptr[58] & 0xf0) << 4) | ptr[56];
dinfo.resolution_height =
(((u32)ptr[61] & 0xf0) << 4) | ptr[59];
ret = copy_to_user((unsigned __user *) arg,
&dinfo, sizeof(dinfo));
break;
}
default:
printk(KERN_ERR "hdmi: unknown cmd, cmd = %d\n", cmd);
}
return ret;
}
static struct fb_ops hdmi_fb_ops = {
.owner = THIS_MODULE,
.fb_open = hdmifb_open,
.fb_release = hdmifb_release,
.fb_check_var = hdmifb_check_var,
.fb_set_par = hdmifb_set_par,
.fb_pan_display = hdmifb_pan_display,
.fb_fillrect = hdmifb_fillrect,
.fb_copyarea = hdmifb_copyarea,
.fb_imageblit = hdmifb_imageblit,
.fb_ioctl = hdmifb_ioctl,
};
#ifdef CONFIG_HAS_EARLYSUSPEND
static void hdmifb_suspend(struct early_suspend *h)
{
struct hdmifb_info *hdmi_fb = container_of(h, struct hdmifb_info,
early_suspend);
struct msm_panel_data *panel = hdmi_fb->panel;
HDMI_DBG("%s, use_count=%d\n", __func__,
atomic_read(&hdmi_fb->use_count));
hdmifb_suspending = true;
HDMI_DBG("%s: hdmifb_suspending = true\n", __func__);
if (atomic_read(&hdmi_fb->use_count) &&
false == test_bit(hdmi_enabled, &hdmi_fb->state)
) {
if (panel->blank)
panel->blank(panel);
}
if (panel->suspend)
panel->suspend(panel);
clear_bit(hdmi_enabled, &hdmi_fb->state);
clear_bit(fb_enabled, &hdmi_fb->state);
}
static void hdmifb_resume(struct early_suspend *h)
{
struct hdmifb_info *hdmi_fb = container_of(h, struct hdmifb_info,
early_suspend);
struct msm_panel_data *panel = hdmi_fb->panel;
HDMI_DBG("%s\n", __func__);
if (panel->resume)
panel->resume(panel);
atomic_set(&hdmi_fb->use_count, 0);
set_bit(fb_enabled, &hdmi_fb->state);
}
#endif
#define BITS_PER_PIXEL 16
static void setup_fb_info(struct hdmifb_info *hdmi_fb)
{
struct fb_info *fb_info = hdmi_fb->fb;
int r;
/* finish setting up the fb_info struct */
strncpy(fb_info->fix.id, "hdmi_fb", 16);
fb_info->fix.ypanstep = 1;
fb_info->fbops = &hdmi_fb_ops;
fb_info->flags = FBINFO_DEFAULT;
fb_info->fix.type = FB_TYPE_PACKED_PIXELS;
fb_info->fix.visual = FB_VISUAL_TRUECOLOR;
fb_info->fix.line_length = hdmi_fb->xres * 2;
fb_info->var.xres = hdmi_fb->xres;
fb_info->var.yres = hdmi_fb->yres;
fb_info->var.width = hdmi_fb->panel->fb_data->width;
fb_info->var.height = hdmi_fb->panel->fb_data->height;
fb_info->var.xres_virtual = hdmi_fb->xres;
fb_info->var.yres_virtual = hdmi_fb->yres * 2;
fb_info->var.bits_per_pixel = BITS_PER_PIXEL;
fb_info->var.accel_flags = 0;
fb_info->var.yoffset = 0;
fb_info->var.red.offset = 11;
fb_info->var.red.length = 5;
fb_info->var.red.msb_right = 0;
fb_info->var.green.offset = 5;
fb_info->var.green.length = 6;
fb_info->var.green.msb_right = 0;
fb_info->var.blue.offset = 0;
fb_info->var.blue.length = 5;
fb_info->var.blue.msb_right = 0;
r = fb_alloc_cmap(&fb_info->cmap, 16, 0);
fb_info->pseudo_palette = PP;
PP[0] = 0;
for (r = 1; r < 16; r++)
PP[r] = 0xffffffff;
}
static int
setup_fbmem(struct hdmifb_info *hdmi_fb, struct platform_device *pdev)
{
struct fb_info *fb = hdmi_fb->fb;
struct resource *res;
unsigned long size = hdmi_fb->xres * hdmi_fb->yres *
(BITS_PER_PIXEL >> 3) * 2;
unsigned char *fbram;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -EINVAL;
/* check the resource is large enough to fit the fb */
if (resource_size(res) < size) {
printk(KERN_ERR "allocated resource(%d) is too small(%lu)"
"for fb\n", resource_size(res), size);
return -ENOMEM;
}
fb->fix.smem_start = res->start;
fb->fix.smem_len = resource_size(res);
fbram = ioremap(res->start, resource_size(res));
if (fbram == 0) {
printk(KERN_ERR "hdmi_fb: cannot allocate fbram!\n");
return -ENOMEM;
}
fb->screen_base = fbram;
memset(fbram, 0, resource_size(res));
printk(KERN_DEBUG "HDMI FB: 0x%x 0x%x\n", res->start, res->end);
return 0;
}
/* Called from dma interrupt handler, must not sleep */
static void hdmi_handle_dma(struct msmfb_callback *callback)
{
/* printk(KERN_DEBUG "%s\n", __func__); */
}
/* Called from vsync interrupt handler, must not sleep */
static void hdmi_handle_vsync(struct msmfb_callback *callback)
{
uint32_t x, y, w, h;
unsigned yoffset;
unsigned addr;
unsigned long irq_flags;
struct fb_info *mirror_fb = registered_fb[0], *fb_hdmi;
struct hdmifb_info *hdmi = container_of(callback, struct hdmifb_info,
vsync_callback);
struct msm_panel_data *panel = hdmi->panel;
spin_lock_irqsave(&hdmi->update_lock, irq_flags);
x = hdmi->update_info.left;
y = hdmi->update_info.top;
w = hdmi->update_info.eright - x;
h = hdmi->update_info.ebottom - y;
yoffset = hdmi->update_info.yoffset;
hdmi->update_info.left = hdmi->xres + 1;
hdmi->update_info.top = hdmi->yres + 1;
hdmi->update_info.eright = 0;
hdmi->update_info.ebottom = 0;
if (unlikely(w > hdmi->xres || h > hdmi->yres ||
w == 0 || h == 0)) {
printk(KERN_INFO "invalid update: %d %d %d "
"%d\n", x, y, w, h);
goto error;
}
spin_unlock_irqrestore(&hdmi->update_lock, irq_flags);
addr = ((hdmi->xres * (yoffset + y) + x) * 2);
if (test_bit(hdmi_mode, &hdmi->state) == 0) {
mdp->dma(mdp, addr + mirror_fb->fix.smem_start,
hdmi->xres * 2, w, h, x, y, &hdmi->dma_callback,
panel->interface_type);
} else {
fb_hdmi = hdmi->fb;
mdp->dma(mdp, addr + fb_hdmi->fix.smem_start,
hdmi->xres * 2, w, h, x, y, &hdmi->dma_callback,
panel->interface_type);
}
return;
error:
spin_unlock_irqrestore(&hdmi->update_lock, irq_flags);
}
static int hdmifb_probe(struct platform_device *pdev)
{
struct fb_info *info;
struct hdmifb_info *hdmi_fb;
struct msm_panel_data *panel = pdev->dev.platform_data;
int ret;
printk(KERN_DEBUG "%s\n", __func__);
if (!panel) {
pr_err("hdmi_fb_probe: no platform data\n");
return -EINVAL;
}
if (!panel->fb_data) {
pr_err("hdmi_fb_probe: no fb_data\n");
return -EINVAL;
}
info = framebuffer_alloc(sizeof(struct hdmifb_info), &pdev->dev);
if (!info)
return -ENOMEM;
hdmi_fb = info->par;
_hdmi_fb = hdmi_fb;
hdmi_fb->fb = info;
hdmi_fb->panel = panel;
set_bit(hdmi_mode, &hdmi_fb->state);
hdmi_fb->dma_callback.func = hdmi_handle_dma;
hdmi_fb->vsync_callback.func = hdmi_handle_vsync;
hdmi_fb->xres = panel->fb_data->xres;
hdmi_fb->yres = panel->fb_data->yres;
spin_lock_init(&hdmi_fb->update_lock);
ret = setup_fbmem(hdmi_fb, pdev);
if (ret)
goto error_setup_fbmem;
setup_fb_info(hdmi_fb);
ret = register_framebuffer(info);
if (ret)
goto error_register_fb;
printk(KERN_INFO "hdmi_fb %d * %d initialed\n",
hdmi_fb->xres, hdmi_fb->yres);
#ifdef CONFIG_HAS_EARLYSUSPEND
hdmi_fb->early_suspend.suspend = hdmifb_suspend;
hdmi_fb->early_suspend.resume = hdmifb_resume;
hdmi_fb->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
register_early_suspend(&hdmi_fb->early_suspend);
#endif
/* blank panel explicitly because we turn on clk on initial */
if (panel->blank)
panel->blank(panel);
set_bit(fb_enabled, &hdmi_fb->state);
return 0;
error_register_fb:
error_setup_fbmem:
framebuffer_release(hdmi_fb->fb);
printk(KERN_ERR "msm probe fail with %d\n", ret);
return ret;
}
static struct platform_driver hdmi_frame_buffer = {
.probe = hdmifb_probe,
.driver = {.name = "msm_hdmi"},
};
static int hdmifb_add_mdp_device(struct device *dev,
struct class_interface *class_intf)
{
/* might need locking if mulitple mdp devices */
if (mdp)
return 0;
mdp = container_of(dev, struct mdp_device, dev);
return platform_driver_register(&hdmi_frame_buffer);
}
static void hdmifb_remove_mdp_device(struct device *dev,
struct class_interface *class_intf)
{
/* might need locking if mulitple mdp devices */
if (dev != &mdp->dev)
return;
platform_driver_unregister(&hdmi_frame_buffer);
mdp = NULL;
}
static struct class_interface hdmi_fb_interface = {
.add_dev = &hdmifb_add_mdp_device,
.remove_dev = &hdmifb_remove_mdp_device,
};
static int hdmifb_add_hdmi_device(struct device *dev,
struct class_interface *class_intf)
{
dev_dbg(dev, "%s\n", __func__);
if (hdmi)
return 0;
hdmi = container_of(dev, struct hdmi_device, dev);
return 0;
}
static struct class_interface hdmi_interface = {
.add_dev = hdmifb_add_hdmi_device,
};
static int __init hdmifb_init(void)
{
int rc;
rc = register_mdp_client(&hdmi_fb_interface);
if (rc)
return rc;
rc = register_hdmi_client(&hdmi_interface);
if (rc)
return rc;
return 0;
}
module_init(hdmifb_init);

View File

@ -0,0 +1,633 @@
/* drivers/video/msm/hdmi_lcdc.c
*
* Copyright (c) 2009 Google Inc.
* Copyright (c) 2009 HTC
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/io.h>
#include <asm/mach-types.h>
#include <mach/msm_fb.h>
#include "../mdp_hw.h"
#include "../../../../arch/arm/mach-msm/proc_comm.h"
#include "../../../../arch/arm/mach-msm/clock.h"
#if 1
#define HDMI_DBG(s...) printk("[hdmi/lcdc]" s)
#else
#define HDMI_DBG(s...) do {} while (0)
#endif
struct mdp_lcdc_info {
struct mdp_info *mdp;
struct clk *mdp_clk;
struct clk *pclk;
struct clk *pad_pclk;
struct msm_panel_data fb_panel_data;
struct platform_device fb_pdev;
struct msm_lcdc_platform_data *pdata;
uint32_t fb_start;
struct msmfb_callback frame_start_cb;
wait_queue_head_t vsync_waitq;
int got_vsync;
struct {
uint32_t clk_rate;
uint32_t hsync_ctl;
uint32_t vsync_period;
uint32_t vsync_pulse_width;
uint32_t display_hctl;
uint32_t display_vstart;
uint32_t display_vend;
uint32_t hsync_skew;
uint32_t polarity;
} parms;
atomic_t blank_count;
struct mutex blank_lock;
};
struct mdp_lcdc_info *_lcdc;
static struct mdp_device *mdp_dev;
#define panel_to_lcdc(p) container_of((p), struct mdp_lcdc_info, fb_panel_data)
/* FIXME: arrange the clock manipulating to proper place,
integrate with the counter of fb_hdmi
*/
int lcdc_enable_video(void)
{
//struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
struct mdp_lcdc_info *lcdc = _lcdc;
struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops;
mutex_lock(&lcdc->blank_lock);
if (atomic_read(&lcdc->blank_count))
goto end_enable_video;
HDMI_DBG("%s: enable clocks\n", __func__);
clk_enable(lcdc->mdp_clk);
clk_enable(lcdc->pclk);
clk_enable(lcdc->pad_pclk);
/* TODO: need pre-test to see if it make any influence to HDCP,
* if ebi1_clk doesn't enabled here.
*/
//panel_ops->unblank(panel_ops);
mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN);
atomic_inc(&lcdc->blank_count);
HDMI_DBG("%s, blank_count=%d\n", __func__,
atomic_read(&lcdc->blank_count));
end_enable_video:
mutex_unlock(&lcdc->blank_lock);
return 0;
}
int lcdc_disable_video(void)
{
struct mdp_lcdc_info *lcdc = _lcdc;
struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops;
mutex_lock(&lcdc->blank_lock);
if (atomic_read(&lcdc->blank_count) == 0)
goto disable_video_done;
if (atomic_dec_return(&lcdc->blank_count) == 0) {
HDMI_DBG("%s: disable clocks\n", __func__);
panel_ops->blank(panel_ops);
mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN);
clk_disable(lcdc->pclk);
clk_disable(lcdc->pad_pclk);
clk_disable(lcdc->mdp_clk);
}
disable_video_done:
mutex_unlock(&lcdc->blank_lock);
HDMI_DBG("%s, blank_count=%d\n", __func__,
atomic_read(&lcdc->blank_count));
return 0;
}
static int lcdc_unblank(struct msm_panel_data *fb_panel)
{
struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops;
HDMI_DBG("%s\n", __func__);
#if 0
HDMI_DBG("%s: enable clocks\n", __func__);
clk_enable(lcdc->mdp_clk);
clk_enable(lcdc->pclk);
clk_enable(lcdc->pad_pclk);
panel_ops->unblank(panel_ops);
mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN);
atomic_set(&lcdc->blank_count, 1);
#else
lcdc_enable_video();
/* TODO: need pre-test to see if it make any influence to HDCP,
* if ebi1_clk enabled here.
*/
panel_ops->unblank(panel_ops);
#endif
return 0;
}
static int lcdc_blank(struct msm_panel_data *fb_panel)
{
struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
//struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops;
#if 0
mutex_lock(&lcdc->blank_lock);
if (atomic_read(&lcdc->blank_count) == 0)
goto blank_done;
if (atomic_dec_return(&lcdc->blank_count) == 0) {
HDMI_DBG("%s: disable clocks\n", __func__);
panel_ops->blank(panel_ops);
mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN);
clk_disable(lcdc->pclk);
clk_disable(lcdc->pad_pclk);
clk_disable(lcdc->mdp_clk);
}
blank_done:
mutex_unlock(&lcdc->blank_lock);
HDMI_DBG("%s, blank_count=%d\n", __func__,
atomic_read(&lcdc->blank_count));
#else
lcdc_disable_video();
#endif
return 0;
}
static int lcdc_suspend(struct msm_panel_data *fb_panel)
{
struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops;
//pr_info("%s: suspending\n", __func__);
HDMI_DBG("%s\n", __func__);
if (panel_ops->uninit)
panel_ops->uninit(panel_ops);
lcdc_disable_video();
return 0;
}
static int lcdc_resume(struct msm_panel_data *fb_panel)
{
struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops;
//pr_info("%s: resuming\n", __func__);
HDMI_DBG("%s\n", __func__);
if (panel_ops->init) {
if (panel_ops->init(panel_ops) < 0)
printk(KERN_ERR "LCD init fail!\n");
}
return 0;
}
static int
lcdc_adjust_timing(struct msm_panel_data *fb_panel,
struct msm_lcdc_timing *timing, u32 xres, u32 yres)
{
struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
unsigned int hsync_period;
unsigned int hsync_start_x;
unsigned int hsync_end_x;
unsigned int vsync_period;
unsigned int display_vstart;
unsigned int display_vend;
uint32_t dma_cfg;
clk_set_rate(lcdc->pclk, timing->clk_rate);
clk_set_rate(lcdc->pad_pclk, timing->clk_rate);
HDMI_DBG("%s, clk=%d, xres=%d, yres=%d,\n", __func__,
clk_get_rate(lcdc->pclk), xres, yres);
hsync_period = (timing->hsync_pulse_width + timing->hsync_back_porch +
xres + timing->hsync_front_porch);
hsync_start_x = (timing->hsync_pulse_width + timing->hsync_back_porch);
hsync_end_x = hsync_period - timing->hsync_front_porch - 1;
vsync_period = (timing->vsync_pulse_width + timing->vsync_back_porch +
yres + timing->vsync_front_porch);
vsync_period *= hsync_period;
display_vstart = timing->vsync_pulse_width + timing->vsync_back_porch;
display_vstart *= hsync_period;
display_vstart += timing->hsync_skew;
display_vend = timing->vsync_front_porch * hsync_period;
display_vend = vsync_period - display_vend + timing->hsync_skew - 1;
/* register values we pre-compute at init time from the timing
* information in the panel info */
lcdc->parms.hsync_ctl = (((hsync_period & 0xfff) << 16) |
(timing->hsync_pulse_width & 0xfff));
lcdc->parms.vsync_period = vsync_period & 0xffffff;
lcdc->parms.vsync_pulse_width = (timing->vsync_pulse_width *
hsync_period) & 0xffffff;
lcdc->parms.display_hctl = (((hsync_end_x & 0xfff) << 16) |
(hsync_start_x & 0xfff));
lcdc->parms.display_vstart = display_vstart & 0xffffff;
lcdc->parms.display_vend = display_vend & 0xffffff;
lcdc->parms.hsync_skew = timing->hsync_skew & 0xfff;
lcdc->parms.polarity = ((timing->hsync_act_low << 0) |
(timing->vsync_act_low << 1) |
(timing->den_act_low << 2));
lcdc->parms.clk_rate = timing->clk_rate;
mdp_writel(lcdc->mdp, lcdc->parms.hsync_ctl, MDP_LCDC_HSYNC_CTL);
mdp_writel(lcdc->mdp, lcdc->parms.vsync_period, MDP_LCDC_VSYNC_PERIOD);
mdp_writel(lcdc->mdp, lcdc->parms.vsync_pulse_width,
MDP_LCDC_VSYNC_PULSE_WIDTH);
mdp_writel(lcdc->mdp, lcdc->parms.display_hctl, MDP_LCDC_DISPLAY_HCTL);
mdp_writel(lcdc->mdp, lcdc->parms.display_vstart,
MDP_LCDC_DISPLAY_V_START);
mdp_writel(lcdc->mdp, lcdc->parms.display_vend, MDP_LCDC_DISPLAY_V_END);
mdp_writel(lcdc->mdp, lcdc->parms.hsync_skew, MDP_LCDC_HSYNC_SKEW);
mdp_writel(lcdc->mdp, 0, MDP_LCDC_BORDER_CLR);
mdp_writel(lcdc->mdp, 0x0, MDP_LCDC_UNDERFLOW_CTL);
mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_HCTL);
mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_START);
mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_END);
mdp_writel(lcdc->mdp, lcdc->parms.polarity, MDP_LCDC_CTL_POLARITY);
printk("solomon: polarity=%04x\n", mdp_readl(lcdc->mdp, MDP_LCDC_CTL_POLARITY));
/* config the dma_p block that drives the lcdc data */
mdp_writel(lcdc->mdp, lcdc->fb_start, MDP_DMA_P_IBUF_ADDR);
mdp_writel(lcdc->mdp, (((yres & 0x7ff) << 16) |
(xres & 0x7ff)),
MDP_DMA_P_SIZE);
/* TODO: pull in the bpp info from somewhere else? */
mdp_writel(lcdc->mdp, xres * 2,
MDP_DMA_P_IBUF_Y_STRIDE);
mdp_writel(lcdc->mdp, 0, MDP_DMA_P_OUT_XY);
dma_cfg = (DMA_PACK_ALIGN_LSB |
DMA_PACK_PATTERN_RGB |
DMA_DITHER_EN);
dma_cfg |= DMA_OUT_SEL_LCDC;
dma_cfg |= DMA_IBUF_FORMAT_RGB565;
dma_cfg |= DMA_DSTC0G_8BITS | DMA_DSTC1B_8BITS | DMA_DSTC2R_8BITS;
mdp_writel(lcdc->mdp, dma_cfg, MDP_DMA_P_CONFIG);
return 0;
}
static int lcdc_hw_init(struct mdp_lcdc_info *lcdc)
{
struct msm_panel_data *fb_panel = &lcdc->fb_panel_data;
uint32_t dma_cfg;
unsigned int clk_id, clk_rate;
clk_enable(lcdc->mdp_clk);
clk_enable(lcdc->pclk);
clk_enable(lcdc->pad_pclk);
clk_set_rate(lcdc->pclk, lcdc->parms.clk_rate);
clk_set_rate(lcdc->pad_pclk, lcdc->parms.clk_rate);
printk(KERN_DEBUG "pclk = %ld, pad_pclk = %ld\n",
clk_get_rate(lcdc->pclk),
clk_get_rate(lcdc->pad_pclk));
/* write the lcdc params */
mdp_writel(lcdc->mdp, lcdc->parms.hsync_ctl, MDP_LCDC_HSYNC_CTL);
mdp_writel(lcdc->mdp, lcdc->parms.vsync_period, MDP_LCDC_VSYNC_PERIOD);
mdp_writel(lcdc->mdp, lcdc->parms.vsync_pulse_width,
MDP_LCDC_VSYNC_PULSE_WIDTH);
mdp_writel(lcdc->mdp, lcdc->parms.display_hctl, MDP_LCDC_DISPLAY_HCTL);
mdp_writel(lcdc->mdp, lcdc->parms.display_vstart,
MDP_LCDC_DISPLAY_V_START);
mdp_writel(lcdc->mdp, lcdc->parms.display_vend, MDP_LCDC_DISPLAY_V_END);
mdp_writel(lcdc->mdp, lcdc->parms.hsync_skew, MDP_LCDC_HSYNC_SKEW);
mdp_writel(lcdc->mdp, 0, MDP_LCDC_BORDER_CLR);
mdp_writel(lcdc->mdp, 0, MDP_LCDC_UNDERFLOW_CTL);
mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_HCTL);
mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_START);
mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_END);
mdp_writel(lcdc->mdp, lcdc->parms.polarity, MDP_LCDC_CTL_POLARITY);
printk("solomon: polarity=%04x\n", mdp_readl(lcdc->mdp, MDP_LCDC_CTL_POLARITY));
/* config the dma_p block that drives the lcdc data */
mdp_writel(lcdc->mdp, lcdc->fb_start, MDP_DMA_P_IBUF_ADDR);
mdp_writel(lcdc->mdp, (((fb_panel->fb_data->yres & 0x7ff) << 16) |
(fb_panel->fb_data->xres & 0x7ff)),
MDP_DMA_P_SIZE);
/* TODO: pull in the bpp info from somewhere else? */
mdp_writel(lcdc->mdp, fb_panel->fb_data->xres * 2,
MDP_DMA_P_IBUF_Y_STRIDE);
mdp_writel(lcdc->mdp, 0, MDP_DMA_P_OUT_XY);
dma_cfg = (DMA_PACK_ALIGN_LSB |
DMA_PACK_PATTERN_RGB |
DMA_DITHER_EN);
dma_cfg |= DMA_OUT_SEL_LCDC;
dma_cfg |= DMA_IBUF_FORMAT_RGB565;
dma_cfg |= DMA_DSTC0G_8BITS | DMA_DSTC1B_8BITS | DMA_DSTC2R_8BITS;
mdp_writel(lcdc->mdp, dma_cfg, MDP_DMA_P_CONFIG);
/* Send customized command to ARM9 for escalating DMA_P as tier-1
* of AXI bus.
* Ref: SR#272509
*/
clk_id = USB_PHY_CLK;
clk_rate = 0x1;
msm_proc_comm(PCOM_CLKCTL_RPC_MIN_RATE, &clk_id, &clk_rate);
return 0;
}
static void lcdc_wait_vsync(struct msm_panel_data *panel)
{
struct mdp_lcdc_info *lcdc = panel_to_lcdc(panel);
int ret;
ret = wait_event_timeout(lcdc->vsync_waitq, lcdc->got_vsync, HZ / 2);
if (ret == 0)
pr_err("%s: timeout waiting for VSYNC\n", __func__);
lcdc->got_vsync = 0;
}
static void lcdc_request_vsync(struct msm_panel_data *fb_panel,
struct msmfb_callback *vsync_cb)
{
struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
/* the vsync callback will start the dma */
vsync_cb->func(vsync_cb);
lcdc->got_vsync = 0;
mdp_out_if_req_irq(mdp_dev, MSM_LCDC_INTERFACE, MDP_LCDC_FRAME_START,
&lcdc->frame_start_cb);
lcdc_wait_vsync(fb_panel);
}
static void lcdc_clear_vsync(struct msm_panel_data *fb_panel)
{
struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
lcdc->got_vsync = 0;
mdp_out_if_req_irq(mdp_dev, MSM_LCDC_INTERFACE, 0, NULL);
}
/* called in irq context with mdp lock held, when mdp gets the
* MDP_LCDC_FRAME_START interrupt */
static void lcdc_frame_start(struct msmfb_callback *cb)
{
struct mdp_lcdc_info *lcdc;
lcdc = container_of(cb, struct mdp_lcdc_info, frame_start_cb);
lcdc->got_vsync = 1;
wake_up(&lcdc->vsync_waitq);
}
static void lcdc_dma_start(void *priv, uint32_t addr, uint32_t stride,
uint32_t width, uint32_t height, uint32_t x,
uint32_t y)
{
struct mdp_lcdc_info *lcdc = priv;
struct mdp_info *mdp = lcdc->mdp;
#if 0
if (mdp->dma_config_dirty) {
mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN);
mdelay(20);
mdp_dev->configure_dma(mdp_dev);
mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN);
}
#endif
mdp_writel(lcdc->mdp, stride, MDP_DMA_P_IBUF_Y_STRIDE);
mdp_writel(lcdc->mdp, addr, MDP_DMA_P_IBUF_ADDR);
}
static void precompute_timing_parms(struct mdp_lcdc_info *lcdc)
{
struct msm_lcdc_timing *timing = lcdc->pdata->timing;
struct msm_fb_data *fb_data = lcdc->pdata->fb_data;
unsigned int hsync_period;
unsigned int hsync_start_x;
unsigned int hsync_end_x;
unsigned int vsync_period;
unsigned int display_vstart;
unsigned int display_vend;
hsync_period = (timing->hsync_pulse_width + timing->hsync_back_porch +
fb_data->xres + timing->hsync_front_porch);
hsync_start_x = (timing->hsync_pulse_width + timing->hsync_back_porch);
hsync_end_x = hsync_period - timing->hsync_front_porch - 1;
vsync_period = (timing->vsync_pulse_width + timing->vsync_back_porch +
fb_data->yres + timing->vsync_front_porch);
vsync_period *= hsync_period;
display_vstart = timing->vsync_pulse_width + timing->vsync_back_porch;
display_vstart *= hsync_period;
display_vstart += timing->hsync_skew;
display_vend = timing->vsync_front_porch * hsync_period;
display_vend = vsync_period - display_vend + timing->hsync_skew - 1;
/* register values we pre-compute at init time from the timing
* information in the panel info */
lcdc->parms.hsync_ctl = (((hsync_period & 0xfff) << 16) |
(timing->hsync_pulse_width & 0xfff));
lcdc->parms.vsync_period = vsync_period & 0xffffff;
lcdc->parms.vsync_pulse_width = (timing->vsync_pulse_width *
hsync_period) & 0xffffff;
lcdc->parms.display_hctl = (((hsync_end_x & 0xfff) << 16) |
(hsync_start_x & 0xfff));
lcdc->parms.display_vstart = display_vstart & 0xffffff;
lcdc->parms.display_vend = display_vend & 0xffffff;
lcdc->parms.hsync_skew = timing->hsync_skew & 0xfff;
lcdc->parms.polarity = ((timing->hsync_act_low << 0) |
(timing->vsync_act_low << 1) |
(timing->den_act_low << 2));
lcdc->parms.clk_rate = timing->clk_rate;
}
static int hdmi_lcdc_probe(struct platform_device *pdev)
{
struct msm_lcdc_platform_data *pdata = pdev->dev.platform_data;
struct mdp_lcdc_info *lcdc;
int ret = 0;
printk(KERN_DEBUG "%s\n", __func__);
if (!pdata) {
pr_err("%s: no LCDC platform data found\n", __func__);
return -EINVAL;
}
_lcdc = lcdc = kzalloc(sizeof(struct mdp_lcdc_info), GFP_KERNEL);
if (!lcdc)
return -ENOMEM;
/* We don't actually own the clocks, the mdp does. */
lcdc->mdp_clk = clk_get(mdp_dev->dev.parent, "mdp_clk");
if (IS_ERR(lcdc->mdp_clk)) {
pr_err("%s: failed to get mdp_clk\n", __func__);
ret = PTR_ERR(lcdc->mdp_clk);
goto err_get_mdp_clk;
}
lcdc->pclk = clk_get(mdp_dev->dev.parent, "lcdc_pclk_clk");
if (IS_ERR(lcdc->pclk)) {
pr_err("%s: failed to get lcdc_pclk\n", __func__);
ret = PTR_ERR(lcdc->pclk);
goto err_get_pclk;
}
lcdc->pad_pclk = clk_get(mdp_dev->dev.parent, "lcdc_pad_pclk_clk");
if (IS_ERR(lcdc->pad_pclk)) {
pr_err("%s: failed to get lcdc_pad_pclk\n", __func__);
ret = PTR_ERR(lcdc->pad_pclk);
goto err_get_pad_pclk;
}
init_waitqueue_head(&lcdc->vsync_waitq);
mutex_init(&lcdc->blank_lock);
lcdc->pdata = pdata;
lcdc->frame_start_cb.func = lcdc_frame_start;
platform_set_drvdata(pdev, lcdc);
mdp_out_if_register(mdp_dev, MSM_LCDC_INTERFACE, lcdc, MDP_DMA_P_DONE,
lcdc_dma_start);
precompute_timing_parms(lcdc);
lcdc->fb_start = pdata->fb_resource->start;
lcdc->mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
lcdc->fb_panel_data.suspend = lcdc_suspend;
lcdc->fb_panel_data.resume = lcdc_resume;
lcdc->fb_panel_data.wait_vsync = lcdc_wait_vsync;
lcdc->fb_panel_data.request_vsync = lcdc_request_vsync;
lcdc->fb_panel_data.clear_vsync = lcdc_clear_vsync;
lcdc->fb_panel_data.blank = lcdc_blank;
lcdc->fb_panel_data.unblank = lcdc_unblank;
lcdc->fb_panel_data.adjust_timing = lcdc_adjust_timing;
lcdc->fb_panel_data.fb_data = pdata->fb_data;
lcdc->fb_panel_data.interface_type = MSM_LCDC_INTERFACE;
ret = lcdc_hw_init(lcdc);
atomic_set(&lcdc->blank_count, 1);
if (ret) {
pr_err("%s: Cannot initialize the mdp_lcdc\n", __func__);
goto err_hw_init;
}
lcdc->fb_pdev.name = "msm_hdmi";
lcdc->fb_pdev.id = pdata->fb_id;
lcdc->fb_pdev.resource = pdata->fb_resource;
lcdc->fb_pdev.num_resources = 1;
lcdc->fb_pdev.dev.platform_data = &lcdc->fb_panel_data;
ret = platform_device_register(&lcdc->fb_pdev);
if (ret) {
pr_err("%s: Cannot register msm_panel pdev\n", __func__);
goto err_plat_dev_reg;
}
pr_info("%s: initialized\n", __func__);
return 0;
err_plat_dev_reg:
err_hw_init:
platform_set_drvdata(pdev, NULL);
clk_put(lcdc->pad_pclk);
err_get_pad_pclk:
clk_put(lcdc->pclk);
err_get_pclk:
clk_put(lcdc->mdp_clk);
err_get_mdp_clk:
kfree(lcdc);
return ret;
}
static int hdmi_lcdc_remove(struct platform_device *pdev)
{
struct mdp_lcdc_info *lcdc = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
clk_put(lcdc->pclk);
clk_put(lcdc->pad_pclk);
kfree(lcdc);
return 0;
}
static struct platform_driver mdp_lcdc_driver = {
.probe = hdmi_lcdc_probe,
.remove = hdmi_lcdc_remove,
.driver = {
.name = "msm_mdp_hdmi",
.owner = THIS_MODULE,
},
};
static int mdp_lcdc_add_mdp_device(struct device *dev,
struct class_interface *class_intf)
{
/* might need locking if mulitple mdp devices */
if (mdp_dev)
return 0;
mdp_dev = container_of(dev, struct mdp_device, dev);
return platform_driver_register(&mdp_lcdc_driver);
}
static void mdp_lcdc_remove_mdp_device(struct device *dev,
struct class_interface *class_intf)
{
/* might need locking if mulitple mdp devices */
if (dev != &mdp_dev->dev)
return;
platform_driver_unregister(&mdp_lcdc_driver);
mdp_dev = NULL;
}
static struct class_interface mdp_lcdc_interface = {
.add_dev = &mdp_lcdc_add_mdp_device,
.remove_dev = &mdp_lcdc_remove_mdp_device,
};
static int __init mdp_lcdc_init(void)
{
return register_mdp_client(&mdp_lcdc_interface);
}
module_init(mdp_lcdc_init);

View File

@ -0,0 +1,82 @@
#ifndef _EDID_H_
#define _EDID_H_
#define MAX_VIDEO_MODES 32
struct edid_info_struct {
bool is_valid;
struct mutex access_lock;
bool under_scan;
bool basic_audio;
bool ycbcr_4_4_4;
bool ycbcr_4_2_2;
bool hdmi_sink;
};
#define EDID_BLOCK_SIZE 128
#define EDID_HDR_NO_OF_FF 0x06
#define EDID_BLOCK_0_OFFSET 0x00
#define EDID_BLOCK_1_OFFSET 0x80
#define EDID_BLOCK_SIZE 128
#define EDID_HDR_NO_OF_FF 0x06
#define NUM_OF_EXTEN_ADDR 0x7E
#define EDID_TAG_ADDR 0x00
#define EDID_REV_ADDR 0x01
#define EDID_TAG_IDX 0x02
#define LONG_DESCR_PTR_IDX 0x02
#define MISC_SUPPORT_IDX 0x03
#define ESTABLISHED_TIMING_INDEX 35
#define NUM_OF_STANDARD_TIMINGS 8
#define STANDARD_TIMING_OFFSET 38
#define LONG_DESCR_LENi 18
#define NUM_OF_DETAILED_DESCRIPTORS 4
#define DETAILED_TIMING_OFFSET 0x36
/* Offsets within a Long Descriptors Block */
#define PIX_CLK_OFFSET 0
#define H_ACTIVE_OFFSET 2
#define H_BLANKING_OFFSET 3
#define V_ACTIVE_OFFSET 5
#define V_BLANKING_OFFSET 6
#define H_SYNC_OFFSET 8
#define H_SYNC_PW_OFFSET 9
#define V_SYNC_OFFSET 10
#define V_SYNC_PW_OFFSET 10
#define H_IMAGE_SIZE_OFFSET 12
#define V_IMAGE_SIZE_OFFSET 13
#define H_BORDER_OFFSET 15
#define V_BORDER_OFFSET 16
#define FLAGS_OFFSET 17
#define AR16_10 0
#define AR4_3 1
#define AR5_4 2
#define AR16_9 3
#define EDID_EXTENSION_TAG 0x02
#define EDID_REV_THREE 0x03
#define EDID_DATA_START 0x04
#define EDID_BLOCK_0 0x00
#define EDID_BLOCK_2_3 0x01
#define AUDIO_DESCR_SIZE 3
/* Data Block Tag Codes */
#define AUDIO_D_BLOCK 0x01
#define VIDEO_D_BLOCK 0x02
#define VENDOR_SPEC_D_BLOCK 0x03
#define SPKR_ALLOC_D_BLOCK 0x04
#define USE_EXTENDED_TAG 0x07
/* Extended Data Block Tag Codes */
#define COLORIMETRY_D_BLOCK 0x05
#define VIDEO_CAPABILITY_D_BLOCK 0x00
#define HDMI_SIGNATURE_LEN 0x03
#define CEC_PHYS_ADDR_LEN 0x02
#endif

View File

@ -0,0 +1,27 @@
#ifndef _FB_HDMI_H_
#define _FB_HDMI_H_
#include <linux/device.h>
#include <linux/fb.h>
enum hd_res {
hd_720p = 0, /* 1280 * 720 */
svga, /* 800 * 600 */
pal, /* 720 * 576 */
edtv, /* 720 * 480 */
vga, /* 640 * 480 */
};
struct msm_lcdc_timing;
struct hdmi_device {
struct device dev;
int (*check_res)(struct hdmi_device *, struct fb_var_screeninfo *);
struct msm_lcdc_timing *(*set_res)(struct hdmi_device *,
struct fb_var_screeninfo *);
int (*get_cable_state)(struct hdmi_device *, int *);
int (*get_establish_timing)(struct hdmi_device *, u8 *);
};
struct class_interface;
int register_hdmi_client(struct class_interface *class_intf);
#endif

View File

@ -0,0 +1,399 @@
#ifndef __SIL902X_H_
#define __SIL902X_H_
#include <mach/msm_fb.h>
#include "edid.h"
struct hdmi_info {
struct hdmi_device hdmi_dev;
struct i2c_client *client;
struct msm_lcdc_panel_ops hdmi_lcdc_ops;
struct work_struct work;
struct delayed_work hdmi_delay_work;
struct mutex lock;
struct mutex lock2;
struct clk *ebi1_clk;
int (*power)(int on);
void (*hdmi_gpio_on)(void);
void (*hdmi_gpio_off)(void);
enum hd_res res;
// FIXME: move to edid_info_struct
u8 edid_buf[128 * 4];
enum {
SLEEP,
AWAKE,
} sleeping;
bool polling;
bool cable_connected;
bool isr_enabled;
bool first;
struct completion hotplug_completion;
struct timer_list timer;
struct work_struct polling_work;
struct dentry *debug_dir;
struct edid_info_struct edid_info;
struct mutex polling_lock;
bool suspending;
bool user_playing;
bool video_streaming;
};
enum {
HDMI_PIXEL_DATA = 0x08,
HDMI_AVI_INFO_FRAME = 0x0c,
HDMI_AUDIO_INFO_FRAME = 0xbf,
HDMI_SYS_CTL = 0x1a,
HDMI_POWER = 0x1e,
HDMI_IDENTIFY = 0x1b,
HDMI_INT_EN = 0x3c,
HDMI_INT_STAT = 0x3d,
HDMI_EN_REG = 0xc7,
};
/* flag bitmap for register HDMI_INT_STAT */
enum {
HOT_PLUG_PENDING = (1U << 0),
RX_PENDING = (1U << 1),
HOT_PLUG_STATE = (1U << 2),
RX_STATE = (1U << 3),
AUDIO_ERR = (1U << 4),
SECURITY_STATE = (1U << 5),
HDCP_VALUE = (1U << 6),
HDCP_AUTH = (1U << 7),
};
enum ErrorMessages {
INIT_SYSTEM_SUCCESSFUL, // 0
BLACK_BOX_OPEN_FAILURE,
BLACK_BOX_OPENED_SUCCESSFULLY,
HW_RESET_FAILURE,
TPI_ENABLE_FAILURE,
INTERRUPT_EN_FAILURE,
INTERRUPT_POLLING_FAILED,
NO_SINK_CONNECTED,
DDC_BUS_REQ_FAILURE,
HDCP_FAILURE,
HDCP_OK, // 10
RX_AUTHENTICATED,
SINK_DOES_NOT_SUPPORT_HDCP,
TX_DOES_NOT_SUPPORT_HDCP,
ILLEGAL_AKSV,
SET_PROTECTION_FAILURE,
REVOKED_KEYS_FOUND,
REPEATER_AUTHENTICATED,
INT_STATUS_READ_FAILURE,
PROTECTION_OFF_FAILED,
PROTECTION_ON_FAILED, // 20
INTERRUPT_POLLING_OK,
EDID_PARSING_FAILURE,
VIDEO_SETUP_FAILURE,
TPI_READ_FAILURE,
TPI_WRITE_FAILURE,
INIT_VIDEO_FAILURE,
DE_CANNOT_BE_SET_WITH_EMBEDDED_SYNC,
SET_EMBEDDED_SYC_FAILURE,
V_MODE_NOT_SUPPORTED,
AUD_MODE_NOT_SUPPORTED, // 30
I2S_NOT_SET,
EDID_READ_FAILURE,
EDID_CHECKSUM_ERROR,
INCORRECT_EDID_HEADER,
EDID_EXT_TAG_ERROR,
EDID_REV_ADDR_ERROR,
EDID_V_DESCR_OVERFLOW,
INCORRECT_EDID_FILE,
UNKNOWN_EDID_TAG_CODE,
NO_DETAILED_DESCRIPTORS_IN_THIS_EDID_BLOCK, // 40
CONFIG_DATA_VALID,
CONFIG_DATA_INVALID,
GPIO_ACCESS_FAILED,
GPIO_CONFIG_ERROR,
HP_EVENT_GOING_TO_SERVICE_LOOP,
EDID_PARSED_OK,
VIDEO_MODE_SET_OK,
AUDIO_MODE_SET_OK,
I2S_MAPPING_SUCCESSFUL,
I2S_INPUT_CONFIG_SUCCESSFUL, // 50
I2S_HEADER_SET_SUCCESSFUL,
INTERRUPT_POLLING_SUCCESSFUL,
HPD_LOOP_EXITED_SUCCESSFULY,
HPD_LOOP_FAILED,
SINK_CONNECTED,
HP_EVENT_RETURNING_FROM_SERVICE_LOOP,
AVI_INFOFRAMES_SETTING_FAILED,
TMDS_ENABLING_FAILED,
DE_SET_OK,
DE_SET_FAILED,
NO_861_EXTENSIONS,
GO_OK,
IMAGE_PKTS_UPDATED_OK,
MONITORING_BLOCKED,
LINK_NORMAL,
LINK_LOST,
RENEGOTIATION_REQUIRED,
LINK_SUSPENDED,
EDID_SHORT_DESCRIPTORS_PARSED_OK,
EDID_LONG_DESCRIPTORS_PARSED_OK,
DDC_BUS_RELEASE_FAILURE,
FAILED_GETTING_BKSV,
PLL_SETUP_FAILUE,
ERR_RX_QUEUE_FULL,
ERR_TX_QUEUE_FULL,
GBD_SET_SUCCESSFULLY,
BACKDOOR_SETTING_FAILED,
ERR_TX_QUEUE_EMPTY
};
#define BIT_0 0x01
#define BIT_1 0x02
#define BIT_2 0x04
#define BIT_3 0x08
#define BIT_4 0x10
#define BIT_5 0x20
#define BIT_6 0x40
#define BIT_7 0x80
#define INTERVAL_HDCP_POLLING (HZ / 25)
#define REQUEST_RELEASE_DDC_BEFORE_HDCP
#define T_HDCP_ACTIVATION 500
#define T_HDCP_DEACTIVATION 200
#define T_HPD_DELAY 10
#define TPI_INTERRUPT_EN 0x3c
#define ALL 0xff
#define DUMMY 0xFD
#define SiI_DEVICE_ID 0xB0
#define T_DDC_ACCESS 50
// TPI Control Masks
// =================
#define BIT_OUTPUT_MODE 0x01
#define BIT_DDC_BUS_GRANT 0x02
#define BIT_DDC_BUS_REQ 0x04
#define BIT_TMDS_OUTPUT 0x10
#define TPI_INTERNAL_PAGE_REG 0xBC
#define TPI_REGISTER_OFFSET_REG 0xBD
#define TPI_REGISTER_VALUE_REG 0xBE
/* HDCP Control Masks */
#define BIT_PROTECT_LEVEL 0x01
#define BIT_PROTECT_TYPE 0x02
#define BIT_REPEATER 0x08
#define BIT_LOCAL_PROTECT 0x40
#define BIT_EXT_PROTECT 0x80
#define BITS_LINK_LOST 0x10
#define BITS_RENEGOTIATION 0x20
#define BIT_TMDS_OUTPUT 0x10
#define BIT_AUDIO_MUTE 0x10
#define TPI_HDCP_REVISION_DATA_REG (0x30)
#define HDCP_MAJOR_REVISION_MASK (BIT_7 | BIT_6 | BIT_5 | BIT_4)
#define HDCP_MAJOR_REVISION_VALUE (0x10)
#define HDCP_MINOR_REVISION_MASK (BIT_3 | BIT_2 | BIT_1 | BIT_0)
#define HDCP_MINOR_REVISION_VALUE (0x02)
#define HDCP_REVISION 0x12
#define SET_PROT_ATTEMPTS 0x05
#define AKSV_SIZE 5
#define BYTE_SIZE 8
#define NUM_OF_ONES_IN_KSV 20
// Interrupt Masks
//================
#define HOT_PLUG_EVENT 0x01
#define RX_SENSE_EVENT 0x02
#define TPI_HOT_PLUG_STATE 0x04
#define RX_SENSE_STATE 0x08
#define AUDIO_ERROR_EVENT 0x10
#define SECURITY_CHANGE_EVENT 0x20
#define V_READY_EVENT 0x40
#define HDCP_CHANGE_EVENT 0x80
#define NON_MASKABLE_INT 0xFF
/* Protection Levels */
#define NO_PROTECTION 0x00
#define LOCAL_PROTECTION 0x01
#define EXTENDED_PROTECTION 0x03
#define LINK_NORMAL 0
#define MAX_V_DESCRIPTORS 20
#define MAX_A_DESCRIPTORS 10
#define MAX_SPEAKER_CONFIGURATIONS 4
#define HDMI_DEBUGFS_ROOT "hdmi"
/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ///
/*\
| | HDCP Implementation
| |
| | HDCP link security logic is implemented in certain transmitters; unique
| | keys are embedded in each chip as part of the solution. The security
| | scheme is fully automatic and handled completely by the hardware.
\*/
/// HDCP Query Data Register ============================================== ///
#define TPI_HDCP_QUERY_DATA_REG (0x29)
#define EXTENDED_LINK_PROTECTION_MASK (BIT_7)
#define EXTENDED_LINK_PROTECTION_NONE (0x00)
#define EXTENDED_LINK_PROTECTION_SECURE (0x80)
#define LOCAL_LINK_PROTECTION_MASK (BIT_6)
#define LOCAL_LINK_PROTECTION_NONE (0x00)
#define LOCAL_LINK_PROTECTION_SECURE (0x40)
#define LINK_STATUS_MASK (BIT_5 | BIT_4)
#define LINK_STATUS_NORMAL (0x00)
#define LINK_STATUS_LINK_LOST (0x10)
#define LINK_STATUS_RENEGOTIATION_REQ (0x20)
#define LINK_STATUS_LINK_SUSPENDED (0x30)
#define HDCP_REPEATER_MASK (BIT_3)
#define HDCP_REPEATER_NO (0x00)
#define HDCP_REPEATER_YES (0x08)
#define CONNECTOR_TYPE_MASK (BIT_2 | BIT_0)
#define CONNECTOR_TYPE_DVI (0x00)
#define CONNECTOR_TYPE_RSVD (0x01)
#define CONNECTOR_TYPE_HDMI (0x04)
#define CONNECTOR_TYPE_FUTURE (0x05)
#define PROTECTION_TYPE_MASK (BIT_1)
#define PROTECTION_TYPE_NONE (0x00)
#define PROTECTION_TYPE_HDCP (0x02)
/// HDCP Control Data Register ============================================ ///
#define TPI_HDCP_CONTROL_DATA_REG (0x2A)
#define PROTECTION_LEVEL_MASK (BIT_0)
#define PROTECTION_LEVEL_MIN (0x00)
#define PROTECTION_LEVEL_MAX (0x01)
/*---------------------------------------------------------------------------*/
#if 0
/* Caller: ChangeVideoMode(), HDCP_Poll(), HotPlugServiceLoop(), RestartHDCP()
*/
#define EnableTMDS(hdmi) ReadClearWriteTPI(hdmi, TPI_SYSTEM_CONTROL, BIT_TMDS_OUTPUT) // 0x1A[4] = 0
/* Caller: ChangeVideoMode(), HDCP_Poll(), TPI_Poll(), RestartHDCP(),
* OnHdmiCableDisconnected()
*/
#define DisableTMDS(hdmi) ReadSetWriteTPI(hdmi, TPI_SYSTEM_CONTROL, BIT_TMDS_OUTPUT) // 0x1A[4] = 1
#else
void EnableTMDS(struct hdmi_info *hdmi);
void DisableTMDS(struct hdmi_info *hdmi);
#endif
// FIXME: fix the global variables
extern u8 pvid_mode, vid_mode;
extern u8 LinkProtectionLevel;
extern u8 systemInitialized;
/*---------------------------------------------------------------------------*/
int hdmi_read(struct i2c_client *client, u8 cmd);
int hdmi_write_byte(struct i2c_client *client, u8 reg, u8 val);
int hdmi_enable_int(struct i2c_client *client);
int hdmi_disable_int(struct i2c_client *client);
int hdmi_read_edid(struct hdmi_info *info, struct i2c_client *client);
int hdmi_standby(struct hdmi_info *hdmi);
int hdmi_wakeup(struct hdmi_info *hdmi);
int read_backdoor_register(struct hdmi_info *hdmi, u8 PageNum, u8 RegOffset);
void ReadSetWriteTPI(struct hdmi_info *hdmi, u8 Offset, u8 Pattern);
void ReadModifyWriteTPI(struct hdmi_info *hdmi, u8 Offset, u8 Mask, u8 Value);
void tpi_clear_interrupt(struct hdmi_info *hdmi, u8 pattern);
bool tpi_init(struct hdmi_info *hdmi);
void ReadClearWriteTPI(struct hdmi_info *hdmi, u8 Offset, u8 Pattern);
s32 ReadBlockTPI(struct hdmi_info *hdmi, u8 TPI_Offset, u16 NBytes, u8 *pData);
bool IsRepeater(struct hdmi_info *hdmi);
bool GetDDC_Access(struct hdmi_info *hdmi, u8* SysCtrlRegVal);
bool ReleaseDDC(struct hdmi_info *hdmi, u8 SysCtrlRegVal);
int tpi_read_backdoor_register(struct hdmi_info *hdmi, u8 PageNum, u8 RegOffset)
;
void tpi_write_backdoor_register(struct hdmi_info *hdmi, u8 PageNum, u8 RegOffset, u8 RegValue);
int HotPlugServiceLoop(struct hdmi_info *hdmi);
int hdmi_active9022(struct i2c_client *client);
int hdmi_active9022_dup(struct i2c_client *client);
bool avc_send_avi_info_frames(struct hdmi_info *hdmi);
bool avc_init_video(struct hdmi_info *hdmi, u8 mode, u8 TclkSel, bool Init);
//void hdcp_on(struct hdmi_info *hdmi);
void hdcp_off(struct hdmi_info *hdmi);
void hdcp_check_status(struct hdmi_info *hdmi, u8 InterruptStatusImage);
void hdcp_init(struct hdmi_info *hdmi);
int hdcp_debugfs_init(struct hdmi_info *hdmi);
extern u8 EDID_TempData[];
u8 edid_simple_parsing(struct hdmi_info *hdmi);
int edid_dump_hex(u8 *src, int src_size, char *output, int output_size);
bool edid_is_video_mode_supported(struct video_mode *vmode);
int edid_debugfs_init(struct hdmi_info *hdmi);
bool edid_check_sink_type(struct hdmi_info *hdmi);
int HotPlugServiceLoop(struct hdmi_info *hdmi);
int tpi_prepare(struct hdmi_info *hdmi);
//bool InitVideo(struct hdmi_info *hdmi, u8 Mode, u8 TclkSel, bool Init);
void avc_set_basic_audio(struct hdmi_info *hdmi);
int hdmi_debugfs_init(struct hdmi_info *hdmi);
int tpi_debugfs_init(struct hdmi_info *hdmi);
ssize_t hdmi_dbgfs_open(struct inode *inode, struct file *file);
void SetAudioMute(struct hdmi_info *hdmi, u8 audioMute);
void SetInputColorSpace(struct hdmi_info *hdmi, u8 inputColorSpace);
void WriteBackDoorRegister(struct hdmi_info *hdmi, u8 PageNum, u8 RegOffset, u8 RegValue);
#define TPI_INPUT_FORMAT 0x09
#define TPI_OUTPUT_FORMAT 0x0A
#define TPI_SYSTEM_CONTROL 0x1A
#define TPI_DEVICE_POWER_STATE_CTRL_REG (0x1E)
#define CTRL_PIN_CONTROL_MASK (BIT_4)
#define CTRL_PIN_TRISTATE (0x00)
#define CTRL_PIN_DRIVEN_TX_BRIDGE (0x10)
#define TX_POWER_STATE_D0 (0x00)
#define TX_POWER_STATE_D1 (0x01)
#define TX_POWER_STATE_D2 (0x02)
#define TX_POWER_STATE_D3 (0x03)
#define TPI_AUDIO_INTERFACE 0x26
#define TPI_HDCP_QUERY_DATA 0x29
#define TPI_HDCP_CTRL 0x2A
#endif

View File

@ -0,0 +1,411 @@
#ifndef _TPI_H_
#define _TPI_H_
#define TPI_PIX_CLK_LSB (0x00)
#define TPI_PIX_CLK_MSB (0x01)
#define TPI_VERT_FREQ_LSB (0x02)
#define TPI_VERT_FREQ_MSB (0x03)
#define TPI_TOTAL_PIX_LSB (0x04)
#define TPI_TOTAL_PIX_MSB (0x05)
#define TPI_TOTAL_LINES_LSB (0x06)
#define TPI_TOTAL_LINES_MSB (0x07)
// Pixel Repetition Data
//======================
#define TPI_PIX_REPETITION (0x08)
// TPI AVI Input and Output Format Data
//=====================================
/// AVI Input Format Data ================================================= ///
#define TPI_INPUT_FORMAT_REG (0x09)
//Finish this...
#define INPUT_COLOR_SPACE_MASK (BIT_1 | BIT_0)
#define INPUT_COLOR_SPACE_RGB (0x00)
#define INPUT_COLOR_SPACE_YCBCR444 (0x01)
#define INPUT_COLOR_SPACE_YCBCR422 (0x02)
#define INPUT_COLOR_SPACE_BLACK_MODE (0x03)
/// AVI Output Format Data ================================================ ///
#define TPI_OUTPUT_FORMAT_REG (0x0A)
#define TPI_YC_Input_Mode (0x0B)
// TPI AVI InfoFrame Data
//=======================
#define TPI_AVI_BYTE_0 (0x0C)
#define TPI_AVI_BYTE_1 (0x0D)
#define TPI_AVI_BYTE_2 (0x0E)
#define TPI_AVI_BYTE_3 (0x0F)
#define TPI_AVI_BYTE_4 (0x10)
#define TPI_AVI_BYTE_5 (0x11)
#define TPI_AUDIO_BYTE_0 (0xBF)
#define TPI_INFO_FRM_DBYTE5 0xC8
#define TPI_INFO_FRM_DBYTE6 0xC9
#define TPI_END_TOP_BAR_LSB (0x12)
#define TPI_END_TOP_BAR_MSB (0x13)
#define TPI_START_BTM_BAR_LSB (0x14)
#define TPI_START_BTM_BAR_MSB (0x15)
#define TPI_END_LEFT_BAR_LSB (0x16)
#define TPI_END_LEFT_BAR_MSB (0x17)
#define TPI_END_RIGHT_BAR_LSB (0x18)
#define TPI_END_RIGHT_BAR_MSB (0x19)
// Colorimetry
//============
#define SET_EX_COLORIMETRY 0x0C // Set TPI_AVI_BYTE_2 to extended colorimetry and use
//TPI_AVI_BYTE_3
// ===================================================== //
#define TPI_SYSTEM_CONTROL_DATA_REG (0x1A)
#define LINK_INTEGRITY_MODE_MASK (BIT_6)
#define LINK_INTEGRITY_STATIC (0x00)
#define LINK_INTEGRITY_DYNAMIC (0x40)
#define TMDS_OUTPUT_CONTROL_MASK (BIT_4)
#define TMDS_OUTPUT_CONTROL_ACTIVE (0x00)
#define TMDS_OUTPUT_CONTROL_POWER_DOWN (0x10)
#define AV_MUTE_MASK (BIT_3)
#define AV_MUTE_NORMAL (0x00)
#define AV_MUTE_MUTED (0x08)
#define DDC_BUS_REQUEST_MASK (BIT_2)
#define DDC_BUS_REQUEST_NOT_USING (0x00)
#define DDC_BUS_REQUEST_REQUESTED (0x04)
#define DDC_BUS_GRANT_MASK (BIT_1)
#define DDC_BUS_GRANT_NOT_AVAILABLE (0x00)
#define DDC_BUS_GRANT_GRANTED (0x02)
#define OUTPUT_MODE_MASK (BIT_0)
#define OUTPUT_MODE_DVI (0x00)
#define OUTPUT_MODE_HDMI (0x01)
// TPI Identification Registers
//=============================
#define TPI_DEVICE_ID (0x1B)
#define TPI_DEVICE_REV_ID (0x1C)
#define TPI_RESERVED2 (0x1D)
// ===================================================== //
#define TPI_DEVICE_POWER_STATE_CTRL_REG (0x1E)
#define CTRL_PIN_CONTROL_MASK (BIT_4)
#define CTRL_PIN_TRISTATE (0x00)
#define CTRL_PIN_DRIVEN_TX_BRIDGE (0x10)
#define TX_POWER_STATE_MASK (BIT_1 | BIT_0)
#define TX_POWER_STATE_D0 (0x00)
#define TX_POWER_STATE_D1 (0x01)
#define TX_POWER_STATE_D2 (0x02)
#define TX_POWER_STATE_D3 (0x03)
// Configuration of I2S Interface
//===============================
#define TPI_I2S_EN (0x1F)
#define TPI_I2S_IN_CFG (0x20)
// Available only when TPI 0x26[7:6]=10 to select I2S input
//=========================================================
#define TPI_I2S_CHST_0 (0x21)
#define TPI_I2S_CHST_1 (0x22)
#define TPI_I2S_CHST_2 (0x23)
#define TPI_I2S_CHST_3 (0x24)
#define TPI_I2S_CHST_4 (0x25)
// Available only when 0x26[7:6]=01
//=================================
#define TPI_SPDIF_HEADER (0x24)
#define TPI_AUDIO_HANDLING (0x25)
// Audio Configuration Regiaters
//==============================
#define TPI_AUDIO_INTERFACE_REG (0x26)
// Finish this...
#define AUDIO_MUTE_MASK (BIT_4)
#define AUDIO_MUTE_NORMAL (0x00)
#define AUDIO_MUTE_MUTED (0x10)
#define TPI_AUDIO_SAMPLE_CTRL (0x27)
#define TPI_SPEAKER_CFG (0xC7)
#define TPI_CHANNEL_COUNT (0xC4)
/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ///
/*\
| | HDCP Implementation
| |
| | HDCP link security logic is implemented in certain transmitters; unique
| | keys are embedded in each chip as part of the solution. The security
| | scheme is fully automatic and handled completely by the hardware.
\*/
/// HDCP Query Data Register ============================================== ///
#define TPI_HDCP_QUERY_DATA_REG (0x29)
#define EXTENDED_LINK_PROTECTION_MASK (BIT_7)
#define EXTENDED_LINK_PROTECTION_NONE (0x00)
#define EXTENDED_LINK_PROTECTION_SECURE (0x80)
#define LOCAL_LINK_PROTECTION_MASK (BIT_6)
#define LOCAL_LINK_PROTECTION_NONE (0x00)
#define LOCAL_LINK_PROTECTION_SECURE (0x40)
#define LINK_STATUS_MASK (BIT_5 | BIT_4)
#define LINK_STATUS_NORMAL (0x00)
#define LINK_STATUS_LINK_LOST (0x10)
#define LINK_STATUS_RENEGOTIATION_REQ (0x20)
#define LINK_STATUS_LINK_SUSPENDED (0x30)
#define HDCP_REPEATER_MASK (BIT_3)
#define HDCP_REPEATER_NO (0x00)
#define HDCP_REPEATER_YES (0x08)
#define CONNECTOR_TYPE_MASK (BIT_2 | BIT_0)
#define CONNECTOR_TYPE_DVI (0x00)
#define CONNECTOR_TYPE_RSVD (0x01)
#define CONNECTOR_TYPE_HDMI (0x04)
#define CONNECTOR_TYPE_FUTURE (0x05)
#define PROTECTION_TYPE_MASK (BIT_1)
#define PROTECTION_TYPE_NONE (0x00)
#define PROTECTION_TYPE_HDCP (0x02)
/// HDCP Control Data Register ============================================ ///
#define TPI_HDCP_CONTROL_DATA_REG (0x2A)
#define PROTECTION_LEVEL_MASK (BIT_0)
#define PROTECTION_LEVEL_MIN (0x00)
#define PROTECTION_LEVEL_MAX (0x01)
/// HDCP BKSV Registers =================================================== ///
#define TPI_BKSV_1_REG (0x2B)
#define TPI_BKSV_2_REG (0x2C)
#define TPI_BKSV_3_REG (0x2D)
#define TPI_BKSV_4_REG (0x2E)
#define TPI_BKSV_5_REG (0x2F)
/// HDCP Revision Data Register =========================================== ///
#define TPI_HDCP_REVISION_DATA_REG (0x30)
#define HDCP_MAJOR_REVISION_MASK (BIT_7 | BIT_6 | BIT_5 | BIT_4)
#define HDCP_MAJOR_REVISION_VALUE (0x10)
#define HDCP_MINOR_REVISION_MASK (BIT_3 | BIT_2 | BIT_1 | BIT_0)
#define HDCP_MINOR_REVISION_VALUE (0x02)
/// HDCP KSV and V' Value Data Register =================================== ///
#define TPI_V_PRIME_SELECTOR_REG (0x31)
/// V' Value Readback Registers =========================================== ///
#define TPI_V_PRIME_7_0_REG (0x32)
#define TPI_V_PRIME_15_9_REG (0x33)
#define TPI_V_PRIME_23_16_REG (0x34)
#define TPI_V_PRIME_31_24_REG (0x35)
/// HDCP AKSV Registers =================================================== ///
#define TPI_AKSV_1_REG (0x36)
#define TPI_AKSV_2_REG (0x37)
#define TPI_AKSV_3_REG (0x38)
#define TPI_AKSV_4_REG (0x39)
#define TPI_AKSV_5_REG (0x3A)
/*\
| | Interrupt Service
| |
| | TPI can be configured to generate an interrupt to the host to notify it of
| | various events. The host can either poll for activity or use an interrupt
| | handler routine. TPI generates on a single interrupt (INT) to the host.
\*/
/// Interrupt Enable Register ============================================= ///
#define TPI_INTERRUPT_ENABLE_REG (0x3C)
#define HDCP_AUTH_STATUS_CHANGE_EN_MASK (BIT_7)
#define HDCP_AUTH_STATUS_CHANGE_DISABLE (0x00)
#define HDCP_AUTH_STATUS_CHANGE_ENABLE (0x80)
#define HDCP_VPRIME_VALUE_READY_EN_MASK (BIT_6)
#define HDCP_VPRIME_VALUE_READY_DISABLE (0x00)
#define HDCP_VPRIME_VALUE_READY_ENABLE (0x40)
#define HDCP_SECURITY_CHANGE_EN_MASK (BIT_5)
#define HDCP_SECURITY_CHANGE_DISABLE (0x00)
#define HDCP_SECURITY_CHANGE_ENABLE (0x20)
#define AUDIO_ERROR_EVENT_EN_MASK (BIT_4)
#define AUDIO_ERROR_EVENT_DISABLE (0x00)
#define AUDIO_ERROR_EVENT_ENABLE (0x10)
#define CPI_EVENT_NO_RX_SENSE_MASK (BIT_3)
#define CPI_EVENT_NO_RX_SENSE_DISABLE (0x00)
#define CPI_EVENT_NO_RX_SENSE_ENABLE (0x08)
#define RECEIVER_SENSE_EVENT_EN_MASK (BIT_1)
#define RECEIVER_SENSE_EVENT_DISABLE (0x00)
#define RECEIVER_SENSE_EVENT_ENABLE (0x02)
#define HOT_PLUG_EVENT_EN_MASK (BIT_0)
#define HOT_PLUG_EVENT_DISABLE (0x00)
#define HOT_PLUG_EVENT_ENABLE (0x01)
/// Interrupt Status Register ============================================= ///
#define TPI_INTERRUPT_STATUS_REG (0x3D)
#define HDCP_AUTH_STATUS_CHANGE_EVENT_MASK (BIT_7)
#define HDCP_AUTH_STATUS_CHANGE_EVENT_NO (0x00)
#define HDCP_AUTH_STATUS_CHANGE_EVENT_YES (0x80)
#define HDCP_VPRIME_VALUE_READY_EVENT_MASK (BIT_6)
#define HDCP_VPRIME_VALUE_READY_EVENT_NO (0x00)
#define HDCP_VPRIME_VALUE_READY_EVENT_YES (0x40)
#define HDCP_SECURITY_CHANGE_EVENT_MASK (BIT_5)
#define HDCP_SECURITY_CHANGE_EVENT_NO (0x00)
#define HDCP_SECURITY_CHANGE_EVENT_YES (0x20)
#define AUDIO_ERROR_EVENT_MASK (BIT_4)
#define AUDIO_ERROR_EVENT_NO (0x00)
#define AUDIO_ERROR_EVENT_YES (0x10)
#define CPI_EVENT_MASK (BIT_3)
#define CPI_EVENT_NO (0x00)
#define CPI_EVENT_YES (0x08)
#define RX_SENSE_MASK (BIT_3) // This bit is dual purpose depending on the value of 0x3C[3]
#define RX_SENSE_NOT_ATTACHED (0x00)
#define RX_SENSE_ATTACHED (0x08)
#define HOT_PLUG_PIN_STATE_MASK (BIT_2)
#define HOT_PLUG_PIN_STATE_LOW (0x00)
#define HOT_PLUG_PIN_STATE_HIGH (0x04)
#define RECEIVER_SENSE_EVENT_MASK (BIT_1)
#define RECEIVER_SENSE_EVENT_NO (0x00)
#define RECEIVER_SENSE_EVENT_YES (0x02)
#define HOT_PLUG_EVENT_MASK (BIT_0)
#define HOT_PLUG_EVENT_NO (0x00)
#define HOT_PLUG_EVENT_YES (0x01)
/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ///
// Sync Register Configuration and Sync Monitoring Registers
//==========================================================
#define TPI_SYNC_GEN_CTRL (0x60)
#define TPI_SYNC_POLAR_DETECT (0x61)
// Explicit Sync DE Generator Registers (TPI 0x60[7]=0)
//=====================================================
#define TPI_DE_DLY (0x62)
#define TPI_DE_CTRL (0x63)
#define TPI_DE_TOP (0x64)
#define TPI_RESERVED4 (0x65)
#define TPI_DE_CNT_7_0 (0x66)
#define TPI_DE_CNT_11_8 (0x67)
#define TPI_DE_LIN_7_0 (0x68)
#define TPI_DE_LIN_10_8 (0x69)
#define TPI_DE_H_RES_7_0 (0x6A)
#define TPI_DE_H_RES_10_8 (0x6B)
#define TPI_DE_V_RES_7_0 (0x6C)
#define TPI_DE_V_RES_10_8 (0x6D)
// Embedded Sync Register Set (TPI 0x60[7]=1)
//===========================================
#define TPI_HBIT_TO_HSYNC_7_0 (0x62)
#define TPI_HBIT_TO_HSYNC_9_8 (0x63)
#define TPI_FIELD_2_OFFSET_7_0 (0x64)
#define TPI_FIELD_2_OFFSET_11_8 (0x65)
#define TPI_HWIDTH_7_0 (0x66)
#define TPI_HWIDTH_8_9 (0x67)
#define TPI_VBIT_TO_VSYNC (0x68)
#define TPI_VWIDTH (0x69)
// TPI Enable Register
//====================
#define TPI_ENABLE (0xC7)
// Misc InfoFrames
//================
#define MISC_INFO_FRAMES_CTRL (0xBF)
#define MISC_INFO_FRAMES_TYPE (0xC0)
#define MISC_INFO_FRAMES_VER (0xC1)
#define MISC_INFO_FRAMES_LEN (0xC2)
#define MISC_INFO_FRAMES_CHKSUM (0xC3)
// Backdoor Register Offsets
//==========================
#define INTERNAL_PAGE_0 0x00
#define INTERNAL_PAGE_1 0x01
#define DEVICE_ID_LOW_BYTE 0x02
#define DEVICE_ID_HI_BYTE 0x03
#define AUDIO_INPUT_LENGTH 0x24
#define SW_RESET 0x05
#define POWER_DOWN 0x6F
// Backdoor constants
//===================
#define DIV_BY_2 0x00
#define MULT_BY_1 0x01
#define MULT_BY_2 0x02
#define MULT_BY_4 0x03
#define INTERNAL_PAGE_1 0x01
#define INTERNAL_PAGE_2 0x02
#define TMDS_CONT_REG 0x82
#endif

View File

@ -0,0 +1,7 @@
sil-objs = \
tpi.o \
hdcp.o \
av_config.o \
debug-sil902x.o
obj-$(CONFIG_MSM_HDMI) += sil.o

View File

@ -0,0 +1,365 @@
/*
* Copyright (C) 2009 HTC
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
/* TODO:
* 1. Mutex while active
*/
#include <linux/delay.h>
#include <mach/msm_fb.h>
#include "../include/fb-hdmi.h"
#include "../include/sil902x.h"
#include "../include/tpi.h"
#if 1
#define HDMI_DBG(s...) printk("[hdmi/avc]" s)
#else
#define HDMI_DBG(s...) do {} while (0)
#endif
static u8 video_param[][8] = {
[hd_720p] = {0x01, 0x1d, 0x70, 0x17, 0x72, 0x06, 0xee, 0x02},
[svga] = {0xa0, 0x0f, 0x70, 0x17, 0x20, 0x04, 0x74, 0x02},
[pal] = {0x8c, 0x0a, 0x88, 0x13, 0x60, 0x03, 0x71, 0x02}, /* 576p50 */
[edtv] = {0x8c, 0x0a, 0x70, 0x17, 0x5a, 0x03, 0x0d, 0x02},/* 480p60 */
[vga] = {0x8c, 0x0a, 0x70, 0x17, 0x20, 0x03, 0x0d, 0x02},
};
#if 0
static u8 avi_info_frame[][14] = {
[hd_720p] = {0x32, 0x0d, 0xa8, 0x84, 0x04, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00},
[svga] = {0xd1, 0x0e, 0x08, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00},
[pal] = {0x64, 0x0d, 0x68, 0x84, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00}, /* 576p50 */
[edtv] = {0x73, 0x0d, 0x68, 0x84, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00},
[vga] = {0x5E, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00},
};
#else
static u8 avi_info_frame[][14] = {
[hd_720p] = {0x43, 0x00, 0x28, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00},
[svga] = {0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00},
[pal] = {0x46, 0x00, 0x18, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00}, /* 576p50 */
[edtv] = {0x55, 0x00, 0x18, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00},
[vga] = {0x5E, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00},
};
#endif
static u8 audio_info_frame[] =
{ 0xc2, 0x84, 0x01, 0x0a, 0x71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
#if 0
int hdmi_active9022(struct i2c_client *client)
{
int i, ret = -EIO;
struct hdmi_info *info = i2c_get_clientdata(client);
u8 *video_parm = &video_param[info->res][0];
HDMI_DBG("%s+\n", __func__);
mutex_lock(&info->polling_lock);
mutex_lock(&info->lock);
//hdcp_off(info);
DisableTMDS(info);
msleep(128);
/* choose video mode */
ret = i2c_smbus_write_i2c_block_data(client, 0, 8, video_parm);
/* wakeup Sil9022 to D0 state */
ret = hdmi_write_byte(client, HDMI_POWER, 0);
if (edid_check_sink_type(info)) {
/* HDMI Output */
ReadModifyWriteTPI(info, TPI_SYSTEM_CONTROL,
OUTPUT_MODE_MASK, OUTPUT_MODE_HDMI);
/* audio configuration */
ret = hdmi_write_byte(client, 0x26, 0x91);
ret = hdmi_write_byte(client, 0x25, 0x03);
ret = hdmi_write_byte(client, 0x27, 0x59);
ret = hdmi_write_byte(client, 0x28, 0x00);
ret = hdmi_write_byte(client, 0x1f, 0x80);
ret = hdmi_write_byte(client, 0x20, 0x90);
ret = hdmi_write_byte(client, 0x21, 0x00);
//ret = hdmi_write_byte(client, 0x24, 0x00);//0x00 for 44.1k
ret = hdmi_write_byte(client, 0x24, 0x02);//0x02 for 48k
ret = hdmi_write_byte(client, 0x25, 0x00);
ret = hdmi_write_byte(client, 0xbc, 0x02);
ret = hdmi_write_byte(client, 0xbd, 0x24);
ret = hdmi_write_byte(client, 0xbe, 0x92);
ret = hdmi_write_byte(client, 0xbc, 0x02);
ret = hdmi_write_byte(client, 0xbd, 0x2f);
ret = hdmi_write_byte(client, 0xbe, 0);
ret = hdmi_write_byte(client, 0x26, 0x81);
} else {
ReadModifyWriteTPI(info, TPI_SYSTEM_CONTROL,
OUTPUT_MODE_MASK, OUTPUT_MODE_DVI);
SetAudioMute(info, AUDIO_MUTE_MUTED);
}
ret = hdmi_write_byte(client, HDMI_PIXEL_DATA, 0x60);
ret = i2c_smbus_write_i2c_block_data(client, HDMI_AUDIO_INFO_FRAME,
15, audio_info_frame);
hdmi_write_byte(client, 0x09, 0);
hdmi_write_byte(client, 0x0a, 0);
for (i = 0; i < 14 ;i++)
hdmi_write_byte(client, 0xc + i, avi_info_frame[info->res][i]);
EnableTMDS(info);
HDMI_DBG("%s-\n", __func__);
mutex_unlock(&info->lock);
mutex_unlock(&info->polling_lock);
return ret;
}
#endif
int avc_set_video_parm(struct hdmi_info *hdmi)
{
int ret;
u8 *video_parm = video_param[hdmi->res];
ret = i2c_smbus_write_i2c_block_data(hdmi->client, 0, 8, video_parm);
return ret;
}
int avc_set_blank_screen(struct hdmi_info *hdmi)
{
HDMI_DBG("%s+\n", __func__);
hdmi_write_byte(hdmi->client, 0x09, 0x03);
hdmi_write_byte(hdmi->client, 0x19, 0x00);
hdmi_write_byte(hdmi->client, 0x26,
hdmi_read(hdmi->client, 0x26) | 0x10);
}
int hdmi_active9022_dup(struct i2c_client *client)
{
int i, ret = -EIO;
struct hdmi_info *info = i2c_get_clientdata(client);
u8 *video_parm = &video_param[info->res][0];
HDMI_DBG("%s+\n", __func__);
//hdcp_off(info);
DisableTMDS(info);
msleep(128);
/* choose video mode */
ret = i2c_smbus_write_i2c_block_data(client, 0, 8, video_parm);
/* wakeup Sil9022 to D0 state */
ret = hdmi_write_byte(client, HDMI_POWER, 0);
if (edid_check_sink_type(info)) {
/* HDMI Output */
ReadModifyWriteTPI(info, TPI_SYSTEM_CONTROL,
OUTPUT_MODE_MASK, OUTPUT_MODE_HDMI);
/* audio configuration */
ret = hdmi_write_byte(client, 0x26, 0x91);
ret = hdmi_write_byte(client, 0x25, 0x03);
ret = hdmi_write_byte(client, 0x27, 0x59);
ret = hdmi_write_byte(client, 0x28, 0x00);
ret = hdmi_write_byte(client, 0x1f, 0x80);
ret = hdmi_write_byte(client, 0x20, 0x90);
ret = hdmi_write_byte(client, 0x21, 0x00);
//ret = hdmi_write_byte(client, 0x24, 0x00);//0x00 for 44.1k
ret = hdmi_write_byte(client, 0x24, 0x02);//0x02 for 48k
ret = hdmi_write_byte(client, 0x25, 0x00);
ret = hdmi_write_byte(client, 0xbc, 0x02);
ret = hdmi_write_byte(client, 0xbd, 0x24);
ret = hdmi_write_byte(client, 0xbe, 0x92);
ret = hdmi_write_byte(client, 0xbc, 0x02);
ret = hdmi_write_byte(client, 0xbd, 0x2f);
ret = hdmi_write_byte(client, 0xbe, 0);
ret = hdmi_write_byte(client, 0x26, 0x81);
} else {
ReadModifyWriteTPI(info, TPI_SYSTEM_CONTROL,
OUTPUT_MODE_MASK, OUTPUT_MODE_DVI);
SetAudioMute(info, AUDIO_MUTE_MUTED);
}
ret = hdmi_write_byte(client, HDMI_PIXEL_DATA, 0x60);
ret = i2c_smbus_write_i2c_block_data(client, HDMI_AUDIO_INFO_FRAME,
15, audio_info_frame);
hdmi_write_byte(client, 0x09, 0x03);
hdmi_write_byte(client, 0x0a, 0);
for (i = 0; i < 14 ;i++)
hdmi_write_byte(client, 0xc + i, avi_info_frame[info->res][i]);
EnableTMDS(info);
HDMI_DBG("%s-\n", __func__);
return ret;
}
bool avc_send_avi_info_frames(struct hdmi_info *hdmi)
{
int i;
HDMI_DBG("%s res=%d\n", __func__, hdmi->res);
for (i = 0; i < 14 ;i++)
hdmi_write_byte(hdmi->client, 0xc + i,
avi_info_frame[hdmi->res][i]);
return true;
}
#if 0
/* FIXME: intergrate with active9022 */
bool InitVideo(struct hdmi_info *hdmi, u8 Mode, u8 TclkSel, bool Init)
{
int Pattern, ret;
u8 *video_parm = &video_param[hdmi->res][0];
/* Use TPI 0x08[7:6] for 9022A/24A video clock multiplier */
Pattern = (1 << 6) & 0xc0;
ReadSetWriteTPI(hdmi, TPI_PIX_REPETITION, Pattern);
ret = i2c_smbus_write_block_data(hdmi->client, 0, 8, video_parm);
/* input format */
hdmi_write_byte(hdmi->client, 0x09, 0);
hdmi_write_byte(hdmi->client, 0x0a, 0);
if(Init) {
hdmi_write_byte(hdmi->client, 0x08, 0x60);
hdmi_write_byte(hdmi->client, 0x09, 0);
hdmi_write_byte(hdmi->client, 0x0a, 0);
hdmi_write_byte(hdmi->client, 0x60, 0x4);
}
// termination ???
//ret = (read_back_door_register(0x1, 0x82) & 0x3f ) | 0x25;
//write_back_door_register();
#if 0
for (i = 0; i < 14 ;i++) {
hdmi_write_byte(hdmi->client, 0xc + i, avi_info_frame[info->res][i]);
}
#endif
return true;
}
#endif
bool avc_init_video(struct hdmi_info *hdmi, u8 mode, u8 TclkSel, bool Init)
{
int ret;
u8 *video_parm = &video_param[hdmi->res][0];
HDMI_DBG("%s\n", __func__);
/* Use TPI 0x08[7:6] for 9022A/24A video clock multiplier */
hdmi_write_byte(hdmi->client, HDMI_PIXEL_DATA, 0x60);
ret = i2c_smbus_write_i2c_block_data(hdmi->client, 0, 8, video_parm);
/* input format */
hdmi_write_byte(hdmi->client, TPI_INPUT_FORMAT_REG, 0x00);
hdmi_write_byte(hdmi->client, TPI_OUTPUT_FORMAT_REG, 0x00);
#if 0
if (Init) {
hdmi_write_byte(hdmi->client, 0x08, 0x60);
hdmi_write_byte(hdmi->client, 0x09, 0);
hdmi_write_byte(hdmi->client, 0x0a, 0);
/* Default to External Sync mode + disable VSync adjustment */
hdmi_write_byte(hdmi->client, 0x60, 0x4);
/* Disable DE generator by default */
hdmi_write_byte(hdmi->client, 0x63, 0x0);
}
#endif
ret = (tpi_read_backdoor_register(hdmi, INTERNAL_PAGE_1, TMDS_CONT_REG)
& 0x3f ) | 0x25;
tpi_write_backdoor_register(hdmi, INTERNAL_PAGE_1, TMDS_CONT_REG, ret);
return true;
}
void avc_set_basic_audio(struct hdmi_info *hdmi)
{
int ret;
struct i2c_client *client = hdmi->client;
HDMI_DBG("%s\n", __func__);
ret = hdmi_write_byte(client, 0x26, 0x91);
ret = hdmi_write_byte(client, 0x25, 0x03);
ret = hdmi_write_byte(client, 0x27, 0x59);
ret = hdmi_write_byte(client, 0x28, 0x00);
ret = hdmi_write_byte(client, 0x1f, 0x80);
ret = hdmi_write_byte(client, 0x20, 0x90);
ret = hdmi_write_byte(client, 0x21, 0x00);
//ret = hdmi_write_byte(client, 0x24, 0x00);//0x00 for 44.1k
ret = hdmi_write_byte(client, 0x24, 0x02);//0x02 for 48k
ret = hdmi_write_byte(client, 0x25, 0x00);
ret = hdmi_write_byte(client, 0xbc, 0x02);
ret = hdmi_write_byte(client, 0xbd, 0x24);
ret = hdmi_write_byte(client, 0xbe, 0x92);
ret = hdmi_write_byte(client, 0xbc, 0x02);
ret = hdmi_write_byte(client, 0xbd, 0x2f);
ret = hdmi_write_byte(client, 0xbe, 0);
ret = hdmi_write_byte(client, 0x26, 0x81);
ret = i2c_smbus_write_i2c_block_data(client, HDMI_AUDIO_INFO_FRAME,
15, audio_info_frame);
}
/* simplifier version of ChangeVideoMode() */
u8 avc_change_video_mode(struct hdmi_info *hdmi, int *resolution)
{
HDMI_DBG("%s\n", __func__);
hdcp_off(hdmi);
DisableTMDS(hdmi);
/* allow control InfoFrames to pass through to the sink device. */
mdelay(128);
// FIXME: video mode
avc_init_video(hdmi, vid_mode, 0, 0);
hdmi_write_byte(hdmi->client, TPI_PIX_REPETITION, 0x60);
hdmi_write_byte(hdmi->client, TPI_INPUT_FORMAT, 0);
if (edid_check_sink_type(hdmi))
ReadSetWriteTPI(hdmi, 0x1a, 0x01);
else
ReadSetWriteTPI(hdmi, 0x1a, 0x00);
/* FIXME: 720p/480p ?*/
hdmi_write_byte(hdmi->client, TPI_OUTPUT_FORMAT, 0);
/* set 0x60[7] = 0 for External Sync */
ReadClearWriteTPI(hdmi, TPI_SYNC_GEN_CTRL, 0x80);
/* clear 0x63[6] = 0 to disable internal DE */
ReadClearWriteTPI(hdmi, 0x63, 1 << 6);
/* InfoFrames - only if output mode is HDMI */
if (edid_check_sink_type(hdmi))
avc_send_avi_info_frames(hdmi);
/* SETTING UP AVI InfoFrames CLEARS 0x63 and 0x60[5] */
hdmi_write_byte(hdmi->client, TPI_SYNC_GEN_CTRL, 1 << 2);
/* SETTING UP AVI InfoFrames CLEARS 0x63 */
hdmi_write_byte(hdmi->client, 0x63, 0);
/* YC Input Mode Select */
hdmi_write_byte(hdmi->client, 0x0b, 0); // 0x0b
EnableTMDS(hdmi);
return VIDEO_MODE_SET_OK;
}

View File

@ -0,0 +1,109 @@
#include <linux/delay.h>
#include <linux/debugfs.h>
#include <mach/msm_fb.h>
#include <mach/msm_hdmi.h>
#include "../include/fb-hdmi.h"
#include "../include/sil902x.h"
//#define HDMI_DEBUGFS
#if defined(HDMI_DEBUGFS)
static spinlock_t hdmi_dbgfs_lock;
ssize_t hdmi_dbgfs_open(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
return 0;
}
static ssize_t hdmi_dbgfs_read(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
static char line[80], buffer[80*8*4];
static char hextab[] = "0123456789abcdefg";
int i, j, n = 0, v, len, offset, line_size;
unsigned long irq_flags;
struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
len = ((int)hdmi->edid_buf[0x7e]+1) * 128;
spin_lock_irqsave(&hdmi_dbgfs_lock, irq_flags);
memset(line, ' ', 79);
line[79] = '\0';
offset = strlen("0000 | ");
line_size = offset + 3 * 16 + 1;
for (i = 0; i < len / 16 ; i++) {
scnprintf(line, offset + 1, "%04x | ", (i << 4));
for (j = 0; j < 16 ; j++) {
v = hdmi->edid_buf[i * 16 + j];
line[offset + j * 3] = hextab[v / 16];
line[offset + j * 3 + 1] = hextab[v % 16];
}
line[line_size - 1] = '\n';
strncpy(buffer + i * line_size, line, line_size);
n += line_size;
}
spin_unlock_irqrestore(&hdmi_dbgfs_lock, irq_flags);
return simple_read_from_buffer(buf, count, ppos, buffer, n);
}
#if 0
static ssize_t hdmi_dbgfs_write(struct file *filp, const char __user *buf,
size_t count, loff_t *ppos)
{
unsigned long v;
unsigned long irq_flags;
char buff[80];
struct tv_reg_data *trd = (struct tv_reg_data *)filp->private_data;
if (count >= sizeof(buff))
return -EINVAL;
if (copy_from_user(&buff, buf, 80))
return -EFAULT;
buff[count] = 0;
#if 0
spin_lock_irqsave(&hdmi_dbgfs_lock, irq_flags);
strict_strtoul(buff, 16, &v);
buff[strlen(buff)]=0;
writel(v, tvenc_base+trd->offset);
spin_unlock_irqrestore(&hdmi_dbgfs_lock, irq_flags);
#endif
return count;
}
#endif
static struct file_operations hdmi_fops[] = {
{
.open = hdmi_dbgfs_open,
.read = hdmi_dbgfs_read,
}
};
int hdmi_debugfs_init(struct hdmi_info *hdmi)
{
HDMI_DBG("%s\n", __func__);
spin_lock_init(&hdmi_dbgfs_lock);
hdmi->debug_dir = debugfs_create_dir(HDMI_DEBUGFS_ROOT, 0);
if (IS_ERR(hdmi->debug_dir))
return PTR_ERR(hdmi->debug_dir);
// FIXME: error handling
debugfs_create_file("dummy", 0644, hdmi->debug_dir, hdmi,
&hdmi_fops[0]);
edid_debugfs_init(hdmi);
tpi_debugfs_init(hdmi);
hdcp_debugfs_init(hdmi);
/*
int ret;
if (!ret) {
pr_err("%s: failure on debugfs_create_file()\n", __func__);
return -1;
}
*/
return 0;
}
#endif

View File

@ -0,0 +1,354 @@
#include <linux/delay.h>
#include <linux/debugfs.h>
#include <mach/msm_fb.h>
#include <mach/msm_hdmi.h>
#include "../include/fb-hdmi.h"
#include "../include/sil902x.h"
#include "../include/tpi.h"
#if 1
#define HDCP_DBG(fmt, arg...) printk( "[hdmi/hdcp]%s: " fmt, __func__, ##arg)
#else
#define HDCP_DBG(fmt...) do {} while (0)
#endif
#define hdcp_err(fmt, arg...) pr_err( "[hdmi/hdcp]%s: " fmt, __func__, ##arg)
//#define HDCP_DEBUG /* Remove this definition when releasing!!! */
#if defined(HDCP_DEBUG)
#define HDCP_OVERWRITE_CONTROL 0x1
#define HDCP_SUPPORT 0x2
#define HDCP_AVALABLE 0x4
static int hdcp_control = 0x7;
module_param_named(hdcp_control, hdcp_control, int,
S_IRUGO | S_IWUSR | S_IWGRP);
#endif
bool HDCP_TxSupports;
bool HDCP_Started;
u8 HDCP_LinkProtectionLevel;
//////////////////////////////////////////////////////////////////////////////
// FUNCTION : IsHDCP_Supported()
// PURPOSE : Check Tx revision number to find if this Tx supports HDCP
// by reading the HDCP revision number from TPI register 0x30.
// RETURNS : true if Tx supports HDCP. false if not.
//////////////////////////////////////////////////////////////////////////////
bool hdcp_check_support(struct hdmi_info *hdmi)
{
u8 HDCP_Rev;
bool HDCP_Supported;
HDCP_Supported = true;
/* Check Device ID */
HDCP_Rev = hdmi_read(hdmi->client, TPI_HDCP_REVISION_DATA_REG);
if (HDCP_Rev !=
(HDCP_MAJOR_REVISION_VALUE | HDCP_MINOR_REVISION_VALUE))
HDCP_Supported = false;
HDCP_DBG("ret=%d\n", HDCP_Supported);
return HDCP_Supported;
}
//////////////////////////////////////////////////////////////////////////////
// FUNCTION : AreAKSV_OK()
// PURPOSE : Check if AKSVs contain 20 '0' and 20 '1'
// INPUT PARAMS : None
// OUTPUT PARAMS : None
// GLOBALS USED : TBD
// RETURNS : true if 20 zeros and 20 ones found in AKSV. false OTHERWISE
//////////////////////////////////////////////////////////////////////////////
static bool hdcp_check_aksv(struct hdmi_info *hdmi)
{
int ret;
u8 B_Data[AKSV_SIZE];
u8 i, j, NumOfOnes = 0;
memset(B_Data, 0, AKSV_SIZE);
#if 0
ReadBlockTPI(hdmi, TPI_AKSV_1_REG, AKSV_SIZE, B_Data);
#else
for (i = 0; i < 5; i++) {
B_Data[i] = hdmi_read(hdmi->client, TPI_AKSV_1_REG+i);
}
#endif
HDCP_DBG(" askv={%02x, %02x, %02x, %02x, %02x}\n",
B_Data[0], B_Data[1], B_Data[2], B_Data[3], B_Data[4]);
for (i=0; i < AKSV_SIZE; i++)
for (j=0; j < BYTE_SIZE; j++) {
if (B_Data[i] & 0x01)
NumOfOnes++;
B_Data[i] >>= 1;
}
if (NumOfOnes != NUM_OF_ONES_IN_KSV)
ret = false;
else ret = true;
HDCP_DBG(":ret=%s\n", ret ? "true" : "false");
return true;
}
//////////////////////////////////////////////////////////////////////////////
// FUNCTION : HDCP_On()
// PURPOSE : Switch hdcp on.
// INPUT PARAMS : None
// OUTPUT PARAMS: None
// GLOBALS USED : HDCP_Started set to true
// RETURNS : None
//////////////////////////////////////////////////////////////////////////////
//void hdcp_on(struct hdmi_info *hdmi)
void hdcp_on(struct hdmi_info *hdmi, const char *caller)
{
HDCP_DBG(", caller=%s\n", caller);
hdmi_write_byte(hdmi->client, TPI_HDCP_CONTROL_DATA_REG, PROTECTION_LEVEL_MAX);
HDCP_Started = true;
}
//////////////////////////////////////////////////////////////////////////////
// FUNCTION : HDCP_Off()
// PURPOSE : Switch hdcp off.
// GLOBALS USED : HDCP_Started set to false
// RETURNS : None
//////////////////////////////////////////////////////////////////////////////
void hdcp_off(struct hdmi_info *hdmi)
{
HDCP_DBG("\n");
SetInputColorSpace(hdmi, INPUT_COLOR_SPACE_BLACK_MODE);
SetAudioMute(hdmi, AUDIO_MUTE_MUTED);
hdmi_write_byte(hdmi->client, TPI_HDCP_CONTROL_DATA_REG, PROTECTION_LEVEL_MIN);
HDCP_Started = false;
HDCP_LinkProtectionLevel = EXTENDED_LINK_PROTECTION_NONE | LOCAL_LINK_PROTECTION_NONE;
}
void hdcp_init(struct hdmi_info *hdmi)
{
HDCP_DBG("\n");
HDCP_TxSupports = false;
HDCP_Started = false;
HDCP_LinkProtectionLevel = EXTENDED_LINK_PROTECTION_NONE | LOCAL_LINK_PROTECTION_NONE;
/* TX-related... need only be done once. */
if (!hdcp_check_support(hdmi)) {
hdcp_err("TX does not support HDCP\n");
return;
}
if (!hdcp_check_aksv(hdmi)) {
hdcp_err("Illegal AKSV\n");
return;
}
HDCP_TxSupports = true;
}
void hdcp_restart(struct hdmi_info *hdmi)
{
HDCP_DBG("\n");
DisableTMDS(hdmi);
hdcp_off(hdmi);
EnableTMDS(hdmi);
}
void hdcp_check_status(struct hdmi_info *hdmi, u8 InterruptStatusImage)
{
u8 QueryData, LinkStatus, RegImage, NewLinkProtectionLevel;
if (HDCP_TxSupports == false)
return;
if ((HDCP_LinkProtectionLevel ==
(EXTENDED_LINK_PROTECTION_NONE | LOCAL_LINK_PROTECTION_NONE)) &&
(HDCP_Started == false)) {
QueryData = hdmi_read(hdmi->client, TPI_HDCP_QUERY_DATA_REG);
/* Is HDCP avaialable */
if (QueryData & PROTECTION_TYPE_MASK) {
hdcp_on(hdmi, __func__);
}
}
/* Check if Link Status has changed: */
if (InterruptStatusImage & SECURITY_CHANGE_EVENT) {
HDCP_DBG("SECURITY_CHANGE_EVENT\n");
LinkStatus = hdmi_read(hdmi->client, TPI_HDCP_QUERY_DATA_REG);
LinkStatus &= LINK_STATUS_MASK;
tpi_clear_interrupt(hdmi, SECURITY_CHANGE_EVENT);
switch (LinkStatus) {
case LINK_STATUS_NORMAL:
HDCP_DBG("Link = Normal\n");
break;
case LINK_STATUS_LINK_LOST:
HDCP_DBG("Link = Lost\n");
hdcp_restart(hdmi);
break;
case LINK_STATUS_RENEGOTIATION_REQ:
HDCP_DBG("Link = Renegotiation Required\n");
hdcp_off(hdmi);
hdcp_on(hdmi, __func__);
break;
case LINK_STATUS_LINK_SUSPENDED:
HDCP_DBG("Link = Suspended\n");
hdcp_on(hdmi, __func__);
break;
}
}
/* Check if HDCP state has changed: */
if (InterruptStatusImage & HDCP_CHANGE_EVENT) {
HDCP_DBG("HDCP_CHANGE_EVENT\n");
RegImage = hdmi_read(hdmi->client, TPI_HDCP_QUERY_DATA_REG);
NewLinkProtectionLevel = RegImage &
(EXTENDED_LINK_PROTECTION_MASK | LOCAL_LINK_PROTECTION_MASK);
if (NewLinkProtectionLevel != HDCP_LinkProtectionLevel) {
HDCP_LinkProtectionLevel = NewLinkProtectionLevel;
switch (HDCP_LinkProtectionLevel) {
case (EXTENDED_LINK_PROTECTION_NONE | LOCAL_LINK_PROTECTION_NONE):
HDCP_DBG("Protection = None\n");
hdcp_restart(hdmi);
break;
case LOCAL_LINK_PROTECTION_SECURE:
SetAudioMute(hdmi, AUDIO_MUTE_NORMAL);
//SetInputColorSpace (hdmi, INPUT_COLOR_SPACE_YCBCR422);
SetInputColorSpace (hdmi, INPUT_COLOR_SPACE_RGB);
HDCP_DBG("Protection = Local, Video Unmuted\n");
break;
case (EXTENDED_LINK_PROTECTION_SECURE | LOCAL_LINK_PROTECTION_SECURE):
HDCP_DBG("Protection = Extended\n");
break;
default:
HDCP_DBG("Protection = Extended but not Local?\n");
hdcp_restart(hdmi);
break;
}
}
tpi_clear_interrupt(hdmi, HDCP_CHANGE_EVENT);
}
}
/*----------------------------------------------------------------------------*/
#if defined(HDMI_DEBUGFS)
static ssize_t hdcp_supported_read(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
int n=0;
char buffer[80];
//struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
HDMI_DBG("%s\n", __func__);
//n = scnprintf(buffer, 80, "%d\n", is_hdcp_supported(hdmi));
n++;
buffer[n] = 0;
return simple_read_from_buffer(buf, count, ppos, buffer, n);
}
static ssize_t hdcp_available_read(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
int n=0;
char buffer[80];
//struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
HDMI_DBG("%s\n", __func__);
//n = scnprintf(buffer, 80, "%d\n", is_hdcp_available(hdmi));
n++;
buffer[n] = 0;
return simple_read_from_buffer(buf, count, ppos, buffer, n);
}
static ssize_t hdcp_on_read(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
hdcp_on(hdmi, __func__);
return 0;
}
static ssize_t hdcp_off_read(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
hdcp_off(hdmi);
return 0;
}
static ssize_t hdcp_restart_read(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
hdcp_restart(hdmi);
return 0;
}
static ssize_t hdcp_handle_read(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
//struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
//handle_hdcp(hdmi);
return 0;
}
static ssize_t hdcp_poll_read(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
//struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
//hdcp_poll(hdmi);
return 0;
}
static ssize_t hdcp_aksv_read(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
hdcp_check_aksv(hdmi);
return 0;
}
static ssize_t hdcp_bksv_read(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
//struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
//hdcp_check_bksv(hdmi);
return 0;
}
static struct file_operations hdcp_debugfs_fops[] = {
{ .open = hdmi_dbgfs_open, .read = hdcp_supported_read, },
{ .open = hdmi_dbgfs_open, .read = hdcp_available_read, },
{ .open = hdmi_dbgfs_open, .read = hdcp_on_read, },
{ .open = hdmi_dbgfs_open, .read = hdcp_off_read, },
{ .open = hdmi_dbgfs_open, .read = hdcp_restart_read, },
{ .open = hdmi_dbgfs_open, .read = hdcp_poll_read, },
{ .open = hdmi_dbgfs_open, .read = hdcp_aksv_read, },
{ .open = hdmi_dbgfs_open, .read = hdcp_bksv_read, },
};
// mutex ?
int hdcp_debugfs_init(struct hdmi_info *hdmi)
{
struct dentry *hdcp_dent;
hdcp_dent = debugfs_create_dir("hdcp", hdmi->debug_dir);
if (IS_ERR(hdcp_dent))
return PTR_ERR(hdcp_dent);
//FIXME: error handling
debugfs_create_file("supported", 0444, hdcp_dent, hdmi,
&hdcp_debugfs_fops[0]);
debugfs_create_file("available", 0444, hdcp_dent, hdmi,
&hdcp_debugfs_fops[1]);
debugfs_create_file("on", 0444, hdcp_dent, hdmi,
&hdcp_debugfs_fops[2]);
debugfs_create_file("off", 0444, hdcp_dent, hdmi,
&hdcp_debugfs_fops[3]);
debugfs_create_file("restart", 0444, hdcp_dent, hdmi,
&hdcp_debugfs_fops[4]);
debugfs_create_file("poll", 0444, hdcp_dent, hdmi,
&hdcp_debugfs_fops[5]);
debugfs_create_file("aksv", 0444, hdcp_dent, hdmi,
&hdcp_debugfs_fops[6]);
debugfs_create_file("bksv", 0444, hdcp_dent, hdmi,
&hdcp_debugfs_fops[7]);
return 0;
}
#endif

View File

@ -0,0 +1,960 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/debugfs.h>
#include <linux/uaccess.h>
#include <mach/msm_fb.h>
#include <mach/msm_hdmi.h>
#include <linux/interrupt.h>
// FIXME: remove this if unnecessary in the future
#ifdef CONFIG_HTC_HEADSET_MGR
#include <mach/htc_headset_mgr.h>
#endif
#if 1
#define HDMI_DBG(s...) printk("[hdmi/tpi]" s)
#else
#define HDMI_DBG(s...) do {} while (0)
#endif
#include "../include/fb-hdmi.h"
#include "../include/sil902x.h"
#include "../include/tpi.h"
#define NEW_INTEGRATE
#define DBG_POLLING 0x1
static int debug_mask;
module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
#define DLOG(mask, fmt, args...) \
do { \
if (debug_mask & mask) \
printk(KERN_INFO "[hdmi/sil]: "fmt, ##args); \
} while (0)
#define X1 0x01
#define AFTER_INIT 1
void HotPlugService (struct hdmi_info *hdmi);
// FIXME: should be decide by detection
static bool dsRxPoweredUp;
static bool edidDataValid;
static bool tmdsPoweredUp;
u8 pvid_mode, vid_mode = 16;
u8 systemInitialized;
void tpi_clear_interrupt(struct hdmi_info *hdmi, u8 pattern)
{
/* write "1" to clear interrupt bit, and 0 won't effect origin value. */
hdmi_write_byte(hdmi->client, TPI_INTERRUPT_STATUS_REG, pattern);
}
//////////////////////////////////////////////////////////////////////////////
// FUNCTION : EnableInterrupts()
// PURPOSE : Enable the interrupts specified in the input parameter
// INPUT PARAMS : A bit pattern with "1" for each interrupt that needs to be
// set in the Interrupt Enable Register (TPI offset 0x3C)
// OUTPUT PARAMS : void
// GLOBALS USED : None
// RETURNS : TRUE
//////////////////////////////////////////////////////////////////////////////
bool tpi_enable_interrupts(struct hdmi_info *hdmi, u8 Interrupt_Pattern)
{
HDMI_DBG("%s, reg=%02x, pat=%02x\n", __func__, TPI_INTERRUPT_EN, Interrupt_Pattern);
ReadSetWriteTPI(hdmi, TPI_INTERRUPT_EN, Interrupt_Pattern);
return true;
}
static void tpi_disable_interrupts(struct hdmi_info *hdmi, u8 pattern)
{
/*
HDMI_DBG("%s, reg=%02x, pat=%02x\n", __func__,
TPI_INTERRUPT_EN, pattern);
*/
ReadClearWriteTPI(hdmi, TPI_INTERRUPT_EN, pattern);
}
static void tpi_clear_pending_event(struct hdmi_info *hdmi)
{
int retry = 100;
if (hdmi->sleeping == SLEEP) return;
while (retry--) {
hdmi_write_byte(hdmi->client, 0x3c, 1);
hdmi_write_byte(hdmi->client, 0x3d, 1);
if (hdmi_read(hdmi->client, 0x3d) & 0x01)
msleep(1);
else
break;
}
if (retry < 19) HDMI_DBG("%s: retry=%d\n", __func__, 19 - retry);
}
//////////////////////////////////////////////////////////////////////////////
// FUNCTION : ReadBackDoorRegister()
// PURPOSE : Read a 922x register value from a backdoor register
// Write:
// 1. 0xBC => Internal page num
// 2. 0xBD => Backdoor register offset
// Read:
// 3. 0xBE => Returns the backdoor register value
// INPUT PARAMS : Internal page number, backdoor register offset, pointer to
// buffer to store read value
// OUTPUT PARAMS: Buffer that stores the read value
// RETURNS : TRUE
// NOTE : This workaround is needed for the 9220/2 only.
//////////////////////////////////////////////////////////////////////////////
int tpi_read_backdoor_register(struct hdmi_info *hdmi, u8 PageNum, u8 RegOffset)
{
// FIXME: error handling
struct i2c_client *client = hdmi->client;
/* Internal page */
hdmi_write_byte(client, TPI_INTERNAL_PAGE_REG, PageNum);
/* Indexed register */
hdmi_write_byte(client, TPI_REGISTER_OFFSET_REG, RegOffset);
/* Read value into buffer */
return hdmi_read(client, TPI_REGISTER_VALUE_REG);
}
void tpi_write_backdoor_register(struct hdmi_info *hdmi, u8 PageNum, u8 RegOffset, u8 RegValue) {
/* Internal page */
hdmi_write_byte(hdmi->client, TPI_INTERNAL_PAGE_REG, PageNum);
/* Indexed register */
hdmi_write_byte(hdmi->client, TPI_REGISTER_OFFSET_REG, RegOffset);
/* Read value into buffer */
hdmi_write_byte(hdmi->client, TPI_REGISTER_VALUE_REG, RegValue);
}
#define TPI_INTERNAL_PAGE_REG 0xBC
#define TPI_INDEXED_OFFSET_REG 0xBD
#define TPI_INDEXED_VALUE_REG 0xBE
#define INDEXED_PAGE_0 0x01
#define INDEXED_PAGE_1 0x02
#define INDEXED_PAGE_2 0x03
void ReadModifyWriteIndexedRegister(struct hdmi_info *hdmi, u8 PageNum, u8 RegOffset, u8 Mask, u8 Value)
{
u8 Tmp;
hdmi_write_byte(hdmi->client, TPI_INTERNAL_PAGE_REG, PageNum);
hdmi_write_byte(hdmi->client, TPI_INDEXED_OFFSET_REG, RegOffset);
Tmp = hdmi_read(hdmi->client, TPI_INDEXED_VALUE_REG);
Tmp &= ~Mask;
Tmp |= (Value & Mask);
hdmi_write_byte(hdmi->client, TPI_INDEXED_VALUE_REG, Tmp);
}
void ReadSetWriteTPI(struct hdmi_info *hdmi, u8 Offset, u8 Pattern)
{
u8 Tmp;
struct i2c_client *client = hdmi->client;
Tmp = hdmi_read(client, Offset);
Tmp |= Pattern;
hdmi_write_byte(client, Offset, Tmp);
}
int tpi_set_bit(struct hdmi_info *hdmi, u8 reg, u8 pattern)
{
return hdmi_write_byte(hdmi->client, reg,
hdmi_read(hdmi->client, reg) | pattern);
}
////
void ReadModifyWriteTPI(struct hdmi_info *hdmi, u8 Offset, u8 Mask, u8 Value)
{
u8 Tmp;
struct i2c_client *client = hdmi->client;
Tmp = hdmi_read(client, Offset);
Tmp &= ~Mask;
Tmp |= (Value & Mask);
hdmi_write_byte(client, Offset, Tmp);
}
////
void ReadClearWriteTPI(struct hdmi_info *hdmi, u8 Offset, u8 Pattern)
{
u8 Tmp;
Tmp = hdmi_read(hdmi->client, Offset);
Tmp &= ~Pattern;
hdmi_write_byte(hdmi->client, Offset, Tmp);
}
void tpi_clear_bit(struct hdmi_info *hdmi, u8 reg, u8 pattern)
{
hdmi_write_byte(hdmi->client, reg,
hdmi_read(hdmi->client, reg) & pattern);
}
////
/* Caller: ChangeVideoMode(), HDCP_Poll(), HotPlugServiceLoop(), RestartHDCP()
*/
void EnableTMDS(struct hdmi_info *hdmi)
{
u8 val;
#if 1
/* 0x1A[4] = 0 */
ReadClearWriteTPI(hdmi, TPI_SYSTEM_CONTROL, BIT_TMDS_OUTPUT);
if (edid_check_sink_type(hdmi))
hdmi_write_byte(hdmi->client, 0x26,
hdmi_read(hdmi->client, 0x26) & ~0x10);
#else
struct i2c_client *client = hdmi->i2c_client;
val = hdmi_read(client, TPI_SYSTEM_CONTROL);
hdmi_write_byte(client, TPI_SYSTEM_CONTROL, val & ~BIT_TMDS_OUTPUT);
HDMI_DBG("%s, reg 0x1a: %02x->%02x\n", __func__,
val, val & ~BIT_TMDS_OUTPUT);
#endif
}
/* Caller: ChangeVideoMode(), HDCP_Poll(), TPI_Poll(), RestartHDCP(),
* OnHdmiCableDisconnected()
*/
void DisableTMDS(struct hdmi_info *hdmi)
{
/* 0x1A[4] = 1 */
//ReadClearWriteTPI(hdmi, TPI_SYSTEM_CONTROL, BIT_TMDS_OUTPUT);
ReadSetWriteTPI(hdmi, TPI_SYSTEM_CONTROL, BIT_TMDS_OUTPUT);
}
static void OnDownstreamRxPoweredDown(struct hdmi_info *hdmi)
{
HDMI_DBG("%s\n", __func__);
dsRxPoweredUp = false;
hdcp_off(hdmi);
}
static void OnDownstreamRxPoweredUp(struct hdmi_info *hdmi)
{
HDMI_DBG("%s\n", __func__);
dsRxPoweredUp = true;
HotPlugService(hdmi);
#ifdef CONFIG_HTC_HEADSET_MGR
/* send cable in event */
switch_send_event(BIT_HDMI_CABLE, 1);
HDMI_DBG("Cable inserted.\n");
#endif
pvid_mode = vid_mode;
hdmi_active9022_dup(hdmi->client);
}
bool GetDDC_Access(struct hdmi_info *hdmi, u8* SysCtrlRegVal)
{
u8 sysCtrl, TPI_ControlImage, DDCReqTimeout = T_DDC_ACCESS;
HDMI_DBG("%s\n", __func__);
/* Read and store original value. Will be passed into ReleaseDDC() */
sysCtrl = hdmi_read(hdmi->client, TPI_SYSTEM_CONTROL);
*SysCtrlRegVal = sysCtrl;
sysCtrl |= BIT_DDC_BUS_REQ;
hdmi_write_byte(hdmi->client, TPI_SYSTEM_CONTROL, sysCtrl);
/* Loop till 0x1A[1] reads "1" */
while (DDCReqTimeout--) {
TPI_ControlImage = hdmi_read(hdmi->client, TPI_SYSTEM_CONTROL);
/* When 0x1A[1] reads "1" */
if (TPI_ControlImage & BIT_DDC_BUS_GRANT) {
sysCtrl |= BIT_DDC_BUS_GRANT;
/* lock host DDC bus access (0x1A[2:1] = 11) */
hdmi_write_byte(hdmi->client, TPI_SYSTEM_CONTROL, sysCtrl);
return true;
}
/* 0x1A[2] = "1" - Requst the DDC bus */
hdmi_write_byte(hdmi->client, TPI_SYSTEM_CONTROL, sysCtrl);
mdelay(200);
}
/* Failure... restore original value. */
hdmi_write_byte(hdmi->client, TPI_SYSTEM_CONTROL, sysCtrl);
return false;
}
bool ReleaseDDC(struct hdmi_info *hdmi, u8 SysCtrlRegVal)
{
u8 DDCReqTimeout = T_DDC_ACCESS, TPI_ControlImage;
HDMI_DBG("%s\n", __func__);
/* Just to be sure bits [2:1] are 0 before it is written */
SysCtrlRegVal &= ~(0x6);
/* Loop till 0x1A[1] reads "0" */
while (DDCReqTimeout--) {
/* Cannot use ReadClearWriteTPI() here. A read of
* TPI_SYSTEM_CONTROL is invalid while DDC is granted.
* Doing so will return 0xFF, and cause an invalid value to be
* written back.
*/
/* 0x1A[2:1] = "0" - release the DDC bus */
//ReadClearWriteTPI(TPI_SYSTEM_CONTROL,BITS_2_1);
hdmi_write_byte(hdmi->client, TPI_SYSTEM_CONTROL, SysCtrlRegVal);
TPI_ControlImage = hdmi_read(hdmi->client, TPI_SYSTEM_CONTROL);
/* When 0x1A[2:1] read "0" */
if (!(TPI_ControlImage & 0x6))
return true;
}
/* Failed to release DDC bus control */
return false;
}
int tpi_read_edid(struct hdmi_info *hdmi)
{
u8 SysCtrlReg;
int ret, edid_blocks = 0;
struct i2c_msg msg;
u8 i2c_buff[2];
u8 pbuf[] = {1, 0, 1, 128} ;
struct i2c_msg paging_msg[] = {
{
.addr = 0x30, .flags = 0, .len = 1, .buf = &pbuf[0],
},
{
.addr = 0x50, .flags = 0, .len = 1, .buf = &pbuf[1],
},
{ //Block-2
.addr = 0x50, .flags = I2C_M_RD, .len = 128, .buf = &hdmi->edid_buf[256],
},
{
.addr = 0x30, .flags = 0, .len = 1, .buf = &pbuf[2],
},
{
.addr = 0x50, .flags = 0, .len = 1, .buf = &pbuf[3],
},
{ //Block-3
.addr = 0x50, .flags = I2C_M_RD, .len = 128, .buf = &hdmi->edid_buf[384],
},
};
HDMI_DBG("%s\n", __func__);
#if 0
DisableTMDS(hdmi);
#else
u8 val;
val = hdmi_read(hdmi->client, TPI_SYSTEM_CONTROL);
//hdmi_write_byte(hdmi->client, TPI_SYSTEM_CONTROL, val|BIT_4|BIT_6);
hdmi_write_byte(hdmi->client, TPI_SYSTEM_CONTROL, val|BIT_4);
#endif
if (!GetDDC_Access(hdmi, &SysCtrlReg)) {
pr_err("%s: DDC bus request failed\n", __func__);
return DDC_BUS_REQ_FAILURE;
}
// Block-0
memset(hdmi->edid_buf, 0, 512);
msg.addr = 0x50;
msg.flags = 0;
msg.len = 1;
msg.buf = hdmi->edid_buf;
ret = i2c_transfer(hdmi->client->adapter, &msg, 1);
if (ret < 0)
dev_err(&hdmi->client->dev, "%s: i2c transfer error\n", __func__);
msg.addr = 0x50;
msg.flags = I2C_M_RD;
msg.len = 128;
msg.buf = hdmi->edid_buf;
ret = i2c_transfer(hdmi->client->adapter, &msg, 1);
if (ret < 0) {
dev_err(&hdmi->client->dev, "%s: i2c transfer error\n", __func__);
goto end_read_edid;
} else {
if (hdmi->edid_buf[0x7e] <= 3)
edid_blocks = hdmi->edid_buf[0x7e] ;
dev_info(&hdmi->client->dev, "EDID blocks = %d\n", edid_blocks);
if (edid_blocks == 0 ) {
goto end_read_edid;
}
// Block-1
msg.addr = 0x50;
msg.flags = 0;
msg.len = 1;
i2c_buff[0] = 128;
msg.buf = i2c_buff;
ret = i2c_transfer(hdmi->client->adapter, &msg, 1);
msg.addr = 0x50;
msg.flags = I2C_M_RD;
msg.len = 128;
msg.buf = &hdmi->edid_buf[128];
ret = i2c_transfer(hdmi->client->adapter, &msg, 1);
}
if (edid_blocks > 1) {
// block 2/3
i2c_transfer(hdmi->client->adapter, paging_msg, 3);
i2c_transfer(hdmi->client->adapter, &paging_msg[3], 3);
}
end_read_edid:
if (!ReleaseDDC(hdmi, SysCtrlReg)) {
pr_err("%s: DDC bus release failed\n", __func__);
return DDC_BUS_REQ_FAILURE;
}
edid_simple_parsing(hdmi);
return 0;
}
//////////////////////////////////////////////////////////////////////////////
// FUNCTION : HotPlugService()
// PURPOSE : Implement Hot Plug Service Loop activities
// INPUT PARAMS : None
// OUTPUT PARAMS: void
// GLOBALS USED : LinkProtectionLevel
// RETURNS : An error code that indicates success or cause of failure
//////////////////////////////////////////////////////////////////////////////
extern bool HDCP_TxSupports;
static bool tmdsPoweredUp;
void HotPlugService (struct hdmi_info *hdmi)
{
HDMI_DBG("%s\n", __func__);
mutex_lock(&hdmi->lock);
tpi_disable_interrupts(hdmi, 0xFF);
/*
// use 1st mode supported by sink
//vid_mode = EDID_Data.VideoDescriptor[0];
vid_mode = 0;
*/
avc_init_video(hdmi, vid_mode, X1, AFTER_INIT);
hdmi_write_byte(hdmi->client, HDMI_POWER, 0);
if (edid_check_sink_type(hdmi))
avc_send_avi_info_frames(hdmi);
/* This check needs to be changed to if HDCP is required by the content
once support has been added by RX-side library. */
if (HDCP_TxSupports == true) {
HDMI_DBG("TMDS -> Enabled\n");
/* turn on black mode will lost around 3 secs frames thus remove it */
//SetInputColorSpace(hdmi, INPUT_COLOR_SPACE_BLACK_MODE);
#if 1
ReadModifyWriteTPI(hdmi, TPI_SYSTEM_CONTROL,
LINK_INTEGRITY_MODE_MASK | TMDS_OUTPUT_CONTROL_MASK,
LINK_INTEGRITY_DYNAMIC | TMDS_OUTPUT_CONTROL_ACTIVE);
#else
ReadModifyWriteTPI(hdmi, TPI_SYSTEM_CONTROL,
LINK_INTEGRITY_MODE_MASK | TMDS_OUTPUT_CONTROL_MASK,
LINK_INTEGRITY_DYNAMIC);
#endif
tmdsPoweredUp = true;
} else {
EnableTMDS(hdmi);
}
if (edid_check_sink_type(hdmi))
avc_set_basic_audio(hdmi);
else
SetAudioMute(hdmi, AUDIO_MUTE_MUTED);
tpi_enable_interrupts(hdmi, HOT_PLUG_EVENT | RX_SENSE_EVENT |
AUDIO_ERROR_EVENT | SECURITY_CHANGE_EVENT |
V_READY_EVENT | HDCP_CHANGE_EVENT);
//complete(&hdmi->hotplug_completion);
mutex_unlock(&hdmi->lock);
}
static bool tpi_start(struct hdmi_info *hdmi)
{
u8 devID = 0x00;
u16 wID = 0x0000;
hdmi_write_byte(hdmi->client, TPI_ENABLE, 0x00); // Write "0" to 72:C7 to start HW TPI mode
mdelay(100);
devID = tpi_read_backdoor_register(hdmi, 0x00, 0x03);
wID = devID;
wID <<= 8;
devID = tpi_read_backdoor_register(hdmi, 0x00, 0x02);
wID |= devID;
devID = hdmi_read(hdmi->client, TPI_DEVICE_ID);
HDMI_DBG("%s, ID=%04X\n", __func__, (u32)wID);
if (devID == SiI_DEVICE_ID) {
return true;
}
pr_err("%s: Unsupported TX\n", __func__);
return false;
}
bool tpi_init(struct hdmi_info *hdmi)
{
tmdsPoweredUp = false;
hdmi->cable_connected = false;
dsRxPoweredUp = false;
edidDataValid = false;
/* Enable HW TPI mode, check device ID */
if (tpi_start(hdmi)) {
hdcp_init(hdmi);
return true;
}
return 0;
}
void SetAudioMute(struct hdmi_info *hdmi, u8 audioMute)
{
ReadModifyWriteTPI(hdmi, TPI_AUDIO_INTERFACE_REG, AUDIO_MUTE_MASK, audioMute);
}
void SetInputColorSpace(struct hdmi_info *hdmi, u8 inputColorSpace)
{
ReadModifyWriteTPI(hdmi, TPI_INPUT_FORMAT_REG, INPUT_COLOR_SPACE_MASK, inputColorSpace);
/* Must be written for previous write to take effect. Just write read value unmodified. */
ReadModifyWriteTPI(hdmi, TPI_END_RIGHT_BAR_MSB, 0x00, 0x00);
}
static char edid_hex_buff[2048];
int lcdc_enable_video(void);
int lcdc_disable_video(void);
void tpi_cable_conn(struct hdmi_info *hdmi)
{
HDMI_DBG("%s\n", __func__);
hdmi->cable_connected = true;
tpi_write_backdoor_register(hdmi, INTERNAL_PAGE_0, 0xCE, 0x00); // Clear BStatus
tpi_write_backdoor_register(hdmi, INTERNAL_PAGE_0, 0xCF, 0x00);
//-----------------------------------------------
hdmi_write_byte(hdmi->client, 0x09, 0x03);
hdmi_write_byte(hdmi->client, 0x19, 0x00); // go to blank mode, avoid screen noise
/*
HDMI_DBG("solomon: H/V total=%02x, %02x, %02x, %02x\n",
hdmi_read(hdmi->client, 0x6a),
hdmi_read(hdmi->client, 0x6b),
hdmi_read(hdmi->client, 0x6c),
hdmi_read(hdmi->client, 0x6d)
);
*/
lcdc_enable_video();
msleep(160);
/*
//clk_set_rate(hdmi->ebi1_clk, 120000000);
HDMI_DBG("solomon: H/V total=%02x, %02x, %02x, %02x\n",
hdmi_read(hdmi->client, 0x6a),
hdmi_read(hdmi->client, 0x6b),
hdmi_read(hdmi->client, 0x6c),
hdmi_read(hdmi->client, 0x6d)
);
*/
EnableTMDS(hdmi);
//-----------------------------------------------
tpi_read_edid(hdmi);
memset(edid_hex_buff, 0, 2048);
edid_dump_hex(hdmi->edid_buf, 256, edid_hex_buff, 2048);
printk("EDID data:\n%s\n=====", edid_hex_buff);
/* select output mode (HDMI/DVI) according to sink capabilty */
if (edid_check_sink_type(hdmi))
ReadModifyWriteTPI(hdmi, TPI_SYSTEM_CONTROL, OUTPUT_MODE_MASK, OUTPUT_MODE_HDMI);
else
ReadModifyWriteTPI(hdmi, TPI_SYSTEM_CONTROL, OUTPUT_MODE_MASK, OUTPUT_MODE_DVI);
hdmi->first = false;
#if 0
#ifdef CONFIG_HTC_HEADSET_MGR
/* send cable in event */
switch_send_event(BIT_HDMI_CABLE, 1);
HDMI_DBG("Cable inserted.\n");
#endif
#endif
}
void tpi_cable_disconn(struct hdmi_info *hdmi, bool into_d3)
{
HDMI_DBG("%s, into_d3=%d\n", __func__, into_d3);
hdmi->cable_connected = false;
dsRxPoweredUp = false;
edidDataValid = false;
hdcp_off(hdmi);
DisableTMDS(hdmi);
#if 1
/* wait for debounce */
msleep(20);
tpi_clear_pending_event(hdmi);
#else
reg = hdmi_read(hdmi->client, 0x3d);
if (!(reg & 0x0c))
tpi_clear_pending_event(hdmi);
#endif
if (into_d3) {
mutex_lock(&hdmi->lock);
HDMI_DBG("%s, playing=%d\n", __func__, hdmi->user_playing);
if (false == hdmi->user_playing)
lcdc_disable_video();
clk_set_rate(hdmi->ebi1_clk, 0);
hdmi_standby(hdmi);
hdmi->power(2);
memset(hdmi->edid_buf, 0, 512);
mutex_unlock(&hdmi->lock);
}
#ifdef CONFIG_HTC_HEADSET_MGR
HDMI_DBG("Cable unplugged.\n");
switch_send_event(BIT_HDMI_CABLE, 0);
#endif
}
static char *str_debug_interrupt[] = {
"HOT_PLUG_EVENT\t\t\t",
"RECEIVER_SENSE_EVENT\t\t",
"HOT_PLUG_PIN_STATE\t\t",
"RX_SENSE_MASK\t\t\t",
"AUDIO_ERROR_EVENT\t\t",
"HDCP_SECURITY_CHANGE_EVENT\t",
"HDCP_VPRIME_VALUE_READY_EVENT\t",
"HDCP_AUTH_STATUS_CHANGE_EVENT\t",
};
void tpi_debug_interrupt(struct hdmi_info *hdmi, u8 old_status, u8 new_status)
{
int i, diff, on_off;
HDMI_DBG("%s: status changed, %02x to %02x\n", __func__,
old_status, new_status);
for (i = 7; i >= 0; i--) {
diff = (old_status ^ new_status) & (1 << i);
if (!diff)
continue;
on_off = new_status & (1 << i);
HDMI_DBG("%d-%s->%s\n", i, str_debug_interrupt[i],
on_off ? "on" : "off");
}
}
//////////////////////////////////////////////////////////////////////////////
// FUNCTION : TPI_Poll ()
// PURPOSE : Poll Interrupt Status register for new interrupts
// INPUT PARAMS : None
// OUTPUT PARAMS: None
// GLOBALS USED : LinkProtectionLevel
// RETURNS : None
//////////////////////////////////////////////////////////////////////////////
static u8 last_status = 0;
static void tpi_poll(struct hdmi_info *hdmi)
{
u8 status, orig_status;
int retry = 20;
mutex_lock(&hdmi->polling_lock);
orig_status = status = hdmi_read(hdmi->client, TPI_INTERRUPT_STATUS_REG);
if (last_status != status) {
tpi_debug_interrupt(hdmi, last_status, status);
}
last_status = status;
DLOG(DBG_POLLING, "%s, INT_STAT=%02x\n", __func__, status);
#if 0
if (status & HOT_PLUG_EVENT) {
#else
if (hdmi->first || status & HOT_PLUG_EVENT) {
if (hdmi->first) hdmi->first = false;
#endif
// Enable HPD interrupt bit
ReadSetWriteTPI(hdmi, TPI_INTERRUPT_ENABLE_REG, HOT_PLUG_EVENT);
// Repeat this loop while cable is bouncing:
do {
//DLOG(DBG_POLLING, "TPI: Interrupt status image - 2= %02x\n", status);
hdmi_write_byte(hdmi->client, TPI_INTERRUPT_STATUS_REG, HOT_PLUG_EVENT);
// Delay for metastability protection and to help filter out connection bouncing
mdelay(T_HPD_DELAY);
// Read Interrupt status register
status = hdmi_read(hdmi->client, TPI_INTERRUPT_STATUS_REG);
//DLOG(DBG_POLLING, "TPI: Interrupt status image - 3= %02x\n", status);
if (!retry--) {
HDMI_DBG("%s: retry failed\n", __func__);
break;
}
} while (status & HOT_PLUG_EVENT);// loop as long as HP interrupts recur
DLOG(DBG_POLLING, "int status: %02x, after debouncing: %02x\n",
orig_status, status);
//DLOG(DBG_POLLING, "TPI->hdmiCableConnected = %d\n", hdmi->cable_connected);
if (((status & HOT_PLUG_STATE) >> 2) != hdmi->cable_connected) {
DLOG(DBG_POLLING, "cable status changed: from %d to %d\n",
hdmi->cable_connected, !!(status & HOT_PLUG_STATE));
//DLOG(DBG_POLLING, "TPI-> CONDITION\n");
if (hdmi->cable_connected == true)
tpi_cable_disconn(hdmi, status & 0x8 ? false : true);
else {
tpi_cable_conn(hdmi);
ReadModifyWriteIndexedRegister(hdmi, INDEXED_PAGE_0, 0x0A, 0x08, 0x08);
}
if (hdmi->cable_connected == false) {
mutex_unlock(&hdmi->polling_lock);
return;
}
} else if ( false == hdmi->cable_connected)
/* only occur while booting without cable attached. */
tpi_cable_disconn(hdmi, true);
}
// Check rx power
if (((status & RX_SENSE_STATE) >> 3) != dsRxPoweredUp)
{
if (hdmi->cable_connected == true) {
if (dsRxPoweredUp == true)
OnDownstreamRxPoweredDown(hdmi);
else
OnDownstreamRxPoweredUp(hdmi);
}
tpi_clear_interrupt(hdmi, RX_SENSE_EVENT);
}
// Check if Audio Error event has occurred:
if (status & AUDIO_ERROR_EVENT)
// The hardware handles the event without need for host intervention (PR, p. 31)
tpi_clear_interrupt(hdmi, AUDIO_ERROR_EVENT);
if (hdmi->video_streaming) {
if ((hdmi->cable_connected == true) && (dsRxPoweredUp == true))
hdcp_check_status(hdmi, status);
}
mutex_unlock(&hdmi->polling_lock);
}
static void tpi_work_func(struct work_struct *work)
{
u8 reg = 0;
struct hdmi_info *hdmi =
container_of(work, struct hdmi_info, polling_work);
if (hdmi->sleeping == SLEEP) {
mutex_lock(&hdmi->lock);
hdmi->power(3);
hdmi_wakeup(hdmi);
tpi_init(hdmi);
hdcp_off(hdmi);
mutex_unlock(&hdmi->lock);
}
tpi_poll(hdmi);
#if 1
mutex_lock(&hdmi->lock);
if (hdmi->sleeping == AWAKE)
reg = hdmi_read(hdmi->client, 0x3d) & 0x0c;
if (hdmi->cable_connected || reg) {
hdmi->polling = true;
mod_timer(&hdmi->timer, jiffies + INTERVAL_HDCP_POLLING);
} else {
enable_irq(hdmi->client->irq);
hdmi->isr_enabled = true;
hdmi->polling = false;
}
mutex_unlock(&hdmi->lock);
#else
if (hdmi->sleeping == AWAKE) {
reg = hdmi_read(hdmi->client, 0x3d);
if (reg & 0x0c) {
hdmi->polling = true;
mod_timer(&hdmi->timer, jiffies + INTERVAL_HDCP_POLLING);
} else {
tpi_clear_pending_event(hdmi);
}
}
if (hdmi->cable_connected ) {
hdmi->polling = true;
mod_timer(&hdmi->timer, jiffies + INTERVAL_HDCP_POLLING);
} else {
enable_irq(hdmi->client->irq);
hdmi->isr_enabled = true;
hdmi->polling = false;
}
#endif
/*
HDMI_DBG("after polling: reg=%02x, conn=%d, isr=%d, polling=%d\n",
reg, hdmi->cable_connected, hdmi->isr_enabled, hdmi->polling);
*/
}
static void tpi_timer_func(unsigned long arg)
{
struct hdmi_info *hdmi = (struct hdmi_info *) arg;
schedule_work(&hdmi->polling_work);
}
int tpi_prepare(struct hdmi_info *hdmi)
{
HDMI_DBG("%s\n", __func__);
init_timer(&hdmi->timer);
hdmi->timer.data = (unsigned long)hdmi;
hdmi->timer.function = tpi_timer_func;
hdmi->cable_connected = false;
init_completion(&hdmi->hotplug_completion);
INIT_WORK(&hdmi->polling_work, tpi_work_func);
return 0;
}
/*============================================================================*/
#if defined(HDMI_DEBUGFS)
static ssize_t tpi_dbg_open(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
return 0;
}
static ssize_t tpi_ddc_request_read(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
//struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
return 0;
}
static ssize_t tpi_ddc_request_write(struct file *filp, const char __user *buf,
size_t count, loff_t *ppos)
{
return 0;
}
static ssize_t tpi_isr_read(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
int n = 0;
char buffer[4];
struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
HDMI_DBG("%s\n", __func__);
n = scnprintf(buffer, 4, "%d\n", hdmi->isr_enabled);
n++;
buffer[n] = 0;
return simple_read_from_buffer(buf, count, ppos, buffer, n);
}
static ssize_t tpi_polling_read(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
int n = 0;
char buffer[4];
struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
HDMI_DBG("%s\n", __func__);
n = scnprintf(buffer, 4, "%d\n", hdmi->polling);
n++;
buffer[n] = 0;
return simple_read_from_buffer(buf, count, ppos, buffer, n);
}
static ssize_t tpi_int_status_read(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
int n = 0;
char buffer[8];
struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
HDMI_DBG("%s\n", __func__);
n = scnprintf(buffer, 8, "%02x\n", hdmi_read(hdmi->client, 0x3d));
n++;
buffer[n] = 0;
return simple_read_from_buffer(buf, count, ppos, buffer, n);
}
static ssize_t tpi_int_enable_read(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
int n = 0;
char buffer[8];
struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
HDMI_DBG("%s\n", __func__);
n = scnprintf(buffer, 8, "%02x\n", hdmi_read(hdmi->client, 0x3c));
n++;
buffer[n] = 0;
return simple_read_from_buffer(buf, count, ppos, buffer, n);
}
static ssize_t tpi_avc_read(struct file *filp, char __user *buf,
size_t count, loff_t *ppos)
{
int n = 0;
char buffer[8];
struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
HDMI_DBG("%s\n", __func__);
/*
n = scnprintf(buffer, 8, "%02x\n", hdmi_read(hdmi->client, 0x3c));
n++;
buffer[n] = 0;
return simple_read_from_buffer(buf, count, ppos, buffer, n);
*/
hdmi_active9022(hdmi->client);
return 0;
}
static struct file_operations tpi_debugfs_fops[] = {
{
.open = tpi_dbg_open,
.read = tpi_ddc_request_read,
.write = tpi_ddc_request_write,
},
{
.open = tpi_dbg_open,
.read = tpi_isr_read,
},
{
.open = tpi_dbg_open,
.read = tpi_polling_read,
},
{
.open = tpi_dbg_open,
.read = tpi_int_status_read,
},
{
.open = tpi_dbg_open,
.read = tpi_int_enable_read,
},
{
.open = tpi_dbg_open,
.read = tpi_avc_read,
},
};
int tpi_debugfs_init(struct hdmi_info *hdmi)
{
struct dentry *tpi_dent;
tpi_dent = debugfs_create_dir("tpi", hdmi->debug_dir);
if (IS_ERR(tpi_dent))
return PTR_ERR(tpi_dent);
//FIXME: error handling
debugfs_create_file("ddc_request", 0644, tpi_dent, hdmi,
&tpi_debugfs_fops[0]);
debugfs_create_file("isr_enabled", 0444, tpi_dent, hdmi,
&tpi_debugfs_fops[1]);
debugfs_create_file("polling", 0444, tpi_dent, hdmi,
&tpi_debugfs_fops[2]);
debugfs_create_file("int_stat", 0444, tpi_dent, hdmi,
&tpi_debugfs_fops[3]);
debugfs_create_file("int_ena", 0444, tpi_dent, hdmi,
&tpi_debugfs_fops[4]);
debugfs_create_file("avc", 0444, tpi_dent, hdmi,
&tpi_debugfs_fops[5]);
return 0;
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -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;

View File

@ -0,0 +1,267 @@
/*
* Copyright (C) 2008 HTC Corporation.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/wakelock.h>
#include <mach/msm_fb.h>
static DECLARE_WAIT_QUEUE_HEAD(epson_vsync_wait);
struct panel_info {
struct msm_mddi_client_data *client_data;
struct platform_device pdev;
struct msm_panel_data panel_data;
struct msmfb_callback *epson_callback;
struct wake_lock idle_lock;
int epson_got_int;
};
static struct platform_device mddi_eps_cabc = {
.name = "eps_cabc",
.id = 0,
};
static void epson_request_vsync(struct msm_panel_data *panel_data,
struct msmfb_callback *callback)
{
struct panel_info *panel = container_of(panel_data, struct panel_info,
panel_data);
struct msm_mddi_client_data *client_data = panel->client_data;
panel->epson_callback = callback;
if (panel->epson_got_int) {
panel->epson_got_int = 0;
client_data->activate_link(client_data);
}
}
static void epson_clear_vsync(struct msm_panel_data *panel_data)
{
struct panel_info *panel = container_of(panel_data, struct panel_info,
panel_data);
struct msm_mddi_client_data *client_data = panel->client_data;
client_data->activate_link(client_data);
}
static void epson_wait_vsync(struct msm_panel_data *panel_data)
{
struct panel_info *panel = container_of(panel_data, struct panel_info,
panel_data);
struct msm_mddi_client_data *client_data = panel->client_data;
if (panel->epson_got_int) {
panel->epson_got_int = 0;
client_data->activate_link(client_data); /* clears interrupt */
}
if (wait_event_timeout(epson_vsync_wait, panel->epson_got_int,
HZ/2) == 0)
printk(KERN_ERR "timeout waiting for VSYNC\n");
panel->epson_got_int = 0;
/* interrupt clears when screen dma starts */
}
static int epson_suspend(struct msm_panel_data *panel_data)
{
struct panel_info *panel = container_of(panel_data, struct panel_info,
panel_data);
struct msm_mddi_client_data *client_data = panel->client_data;
struct msm_mddi_bridge_platform_data *bridge_data =
client_data->private_client_data;
int ret;
wake_lock(&panel->idle_lock);
ret = bridge_data->uninit(bridge_data, client_data);
wake_unlock(&panel->idle_lock);
if (ret) {
printk(KERN_INFO "mddi epson client: non zero return from "
"uninit\n");
return ret;
}
client_data->suspend(client_data);
return 0;
}
static int epson_resume(struct msm_panel_data *panel_data)
{
struct panel_info *panel = container_of(panel_data, struct panel_info,
panel_data);
struct msm_mddi_client_data *client_data = panel->client_data;
struct msm_mddi_bridge_platform_data *bridge_data =
client_data->private_client_data;
int ret;
wake_lock(&panel->idle_lock);
client_data->resume(client_data);
wake_unlock(&panel->idle_lock);
ret = bridge_data->init(bridge_data, client_data);
if (ret)
return ret;
return 0;
}
static int epson_blank(struct msm_panel_data *panel_data)
{
struct panel_info *panel = container_of(panel_data, struct panel_info,
panel_data);
struct msm_mddi_client_data *client_data = panel->client_data;
struct msm_mddi_bridge_platform_data *bridge_data =
client_data->private_client_data;
return bridge_data->blank(bridge_data, client_data);
}
static int epson_unblank(struct msm_panel_data *panel_data)
{
struct panel_info *panel = container_of(panel_data, struct panel_info,
panel_data);
struct msm_mddi_client_data *client_data = panel->client_data;
struct msm_mddi_bridge_platform_data *bridge_data =
client_data->private_client_data;
return bridge_data->unblank(bridge_data, client_data);
}
static irqreturn_t epson_vsync_interrupt(int irq, void *data)
{
struct panel_info *panel = data;
panel->epson_got_int = 1;
if (panel->epson_callback) {
panel->epson_callback->func(panel->epson_callback);
panel->epson_callback = 0;
}
wake_up(&epson_vsync_wait);
return IRQ_HANDLED;
}
static int setup_vsync(struct panel_info *panel,
int init)
{
int ret;
int gpio = 98;
unsigned int irq;
if (!init) {
ret = 0;
goto uninit;
}
ret = gpio_request(gpio, "vsync");
if (ret)
goto err_request_gpio_failed;
ret = gpio_direction_input(gpio);
if (ret)
goto err_gpio_direction_input_failed;
ret = irq = gpio_to_irq(gpio);
if (ret < 0)
goto err_get_irq_num_failed;
ret = request_irq(irq, epson_vsync_interrupt, IRQF_TRIGGER_FALLING,
"vsync", panel);
if (ret)
goto err_request_irq_failed;
printk(KERN_INFO "vsync on gpio %d now %d\n",
gpio, gpio_get_value(gpio));
return 0;
uninit:
free_irq(gpio_to_irq(gpio), panel);
err_request_irq_failed:
err_get_irq_num_failed:
err_gpio_direction_input_failed:
gpio_free(gpio);
err_request_gpio_failed:
return ret;
}
static int mddi_epson_probe(struct platform_device *pdev)
{
int ret;
struct msm_mddi_client_data *client_data = pdev->dev.platform_data;
struct msm_mddi_bridge_platform_data *bridge_data =
client_data->private_client_data;
struct panel_data *panel_data = &bridge_data->panel_conf;
struct panel_info *panel =
kzalloc(sizeof(struct panel_info), GFP_KERNEL);
if (!panel)
return -ENOMEM;
platform_set_drvdata(pdev, panel);
printk(KERN_DEBUG "%s\n", __func__);
if (panel_data->caps & MSMFB_CAP_CABC) {
printk(KERN_INFO "CABC enabled\n");
mddi_eps_cabc.dev.platform_data = client_data;
platform_device_register(&mddi_eps_cabc);
}
ret = setup_vsync(panel, 1);
if (ret) {
dev_err(&pdev->dev, "mddi_bridge_setup_vsync failed\n");
return ret;
}
panel->client_data = client_data;
panel->panel_data.suspend = epson_suspend;
panel->panel_data.resume = epson_resume;
panel->panel_data.wait_vsync = epson_wait_vsync;
panel->panel_data.request_vsync = epson_request_vsync;
panel->panel_data.clear_vsync = epson_clear_vsync;
panel->panel_data.blank = epson_blank;
panel->panel_data.unblank = epson_unblank;
panel->panel_data.fb_data = &bridge_data->fb_data;
panel->panel_data.caps = ~MSMFB_CAP_PARTIAL_UPDATES;
panel->pdev.name = "msm_panel";
panel->pdev.id = pdev->id;
panel->pdev.resource = client_data->fb_resource;
panel->pdev.num_resources = 1;
panel->pdev.dev.platform_data = &panel->panel_data;
platform_device_register(&panel->pdev);
wake_lock_init(&panel->idle_lock, WAKE_LOCK_IDLE, "eps_idle_lock");
return 0;
}
static int mddi_epson_remove(struct platform_device *pdev)
{
struct panel_info *panel = platform_get_drvdata(pdev);
setup_vsync(panel, 0);
kfree(panel);
return 0;
}
static struct platform_driver mddi_client_d263_0000 = {
.probe = mddi_epson_probe,
.remove = mddi_epson_remove,
.driver = { .name = "mddi_c_4ca3_0000" },
};
static int __init mddi_client_epson_init(void)
{
platform_driver_register(&mddi_client_d263_0000);
return 0;
}
module_init(mddi_client_epson_init);

View File

@ -0,0 +1,269 @@
/*
* Copyright (C) 2008 HTC Corporation.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/wakelock.h>
#include <mach/msm_fb.h>
static DECLARE_WAIT_QUEUE_HEAD(novtec_vsync_wait);
struct panel_info {
struct msm_mddi_client_data *client_data;
struct platform_device pdev;
struct msm_panel_data panel_data;
struct msmfb_callback *novtec_callback;
struct wake_lock idle_lock;
int novtec_got_int;
};
static struct platform_device mddi_nov_cabc = {
.name = "nov_cabc",
.id = 0,
};
static void novtec_request_vsync(struct msm_panel_data *panel_data,
struct msmfb_callback *callback)
{
struct panel_info *panel = container_of(panel_data, struct panel_info,
panel_data);
struct msm_mddi_client_data *client_data = panel->client_data;
panel->novtec_callback = callback;
if (panel->novtec_got_int) {
panel->novtec_got_int = 0;
client_data->activate_link(client_data);
}
}
static void novtec_clear_vsync(struct msm_panel_data *panel_data)
{
struct panel_info *panel = container_of(panel_data, struct panel_info,
panel_data);
struct msm_mddi_client_data *client_data = panel->client_data;
client_data->activate_link(client_data);
}
static void novtec_wait_vsync(struct msm_panel_data *panel_data)
{
struct panel_info *panel = container_of(panel_data, struct panel_info,
panel_data);
struct msm_mddi_client_data *client_data = panel->client_data;
if (panel->novtec_got_int) {
panel->novtec_got_int = 0;
client_data->activate_link(client_data); /* clears interrupt */
}
if (wait_event_timeout(novtec_vsync_wait, panel->novtec_got_int,
HZ/2) == 0)
printk(KERN_ERR "timeout waiting for VSYNC\n");
panel->novtec_got_int = 0;
/* interrupt clears when screen dma starts */
}
static int novtec_suspend(struct msm_panel_data *panel_data)
{
struct panel_info *panel = container_of(panel_data, struct panel_info,
panel_data);
struct msm_mddi_client_data *client_data = panel->client_data;
struct msm_mddi_bridge_platform_data *bridge_data =
client_data->private_client_data;
int ret;
wake_lock(&panel->idle_lock);
ret = bridge_data->uninit(bridge_data, client_data);
wake_unlock(&panel->idle_lock);
if (ret) {
printk(KERN_INFO "mddi novtec client: non zero return from "
"uninit\n");
return ret;
}
client_data->suspend(client_data);
return 0;
}
static int novtec_resume(struct msm_panel_data *panel_data)
{
struct panel_info *panel = container_of(panel_data, struct panel_info,
panel_data);
struct msm_mddi_client_data *client_data = panel->client_data;
struct msm_mddi_bridge_platform_data *bridge_data =
client_data->private_client_data;
int ret;
wake_lock(&panel->idle_lock);
client_data->resume(client_data);
wake_unlock(&panel->idle_lock);
ret = bridge_data->init(bridge_data, client_data);
if (ret)
return ret;
return 0;
}
static int novtec_blank(struct msm_panel_data *panel_data)
{
struct panel_info *panel = container_of(panel_data, struct panel_info,
panel_data);
struct msm_mddi_client_data *client_data = panel->client_data;
struct msm_mddi_bridge_platform_data *bridge_data =
client_data->private_client_data;
return bridge_data->blank(bridge_data, client_data);
}
static int novtec_unblank(struct msm_panel_data *panel_data)
{
struct panel_info *panel = container_of(panel_data, struct panel_info,
panel_data);
struct msm_mddi_client_data *client_data = panel->client_data;
struct msm_mddi_bridge_platform_data *bridge_data =
client_data->private_client_data;
return bridge_data->unblank(bridge_data, client_data);
}
static irqreturn_t novtec_vsync_interrupt(int irq, void *data)
{
struct panel_info *panel = data;
panel->novtec_got_int = 1;
if (panel->novtec_callback) {
mdelay(3);
panel->novtec_callback->func(panel->novtec_callback);
panel->novtec_callback = 0;
}
wake_up(&novtec_vsync_wait);
return IRQ_HANDLED;
}
static int setup_vsync(struct panel_info *panel,
int init)
{
int ret;
int gpio = 98;
unsigned int irq;
if (!init) {
ret = 0;
goto uninit;
}
ret = gpio_request(gpio, "vsync");
if (ret)
goto err_request_gpio_failed;
ret = gpio_direction_input(gpio);
if (ret)
goto err_gpio_direction_input_failed;
ret = irq = gpio_to_irq(gpio);
if (ret < 0)
goto err_get_irq_num_failed;
ret = request_irq(irq, novtec_vsync_interrupt, IRQF_TRIGGER_FALLING,
"vsync", panel);
if (ret)
goto err_request_irq_failed;
printk(KERN_INFO "vsync on gpio %d now %d\n",
gpio, gpio_get_value(gpio));
return 0;
uninit:
free_irq(gpio_to_irq(gpio), panel);
err_request_irq_failed:
err_get_irq_num_failed:
err_gpio_direction_input_failed:
gpio_free(gpio);
err_request_gpio_failed:
return ret;
}
static int mddi_novtec_probe(struct platform_device *pdev)
{
int ret;
struct msm_mddi_client_data *client_data = pdev->dev.platform_data;
struct msm_mddi_bridge_platform_data *bridge_data =
client_data->private_client_data;
struct panel_data *panel_data = &bridge_data->panel_conf;
struct panel_info *panel =
kzalloc(sizeof(struct panel_info), GFP_KERNEL);
if (!panel)
return -ENOMEM;
platform_set_drvdata(pdev, panel);
printk(KERN_DEBUG "%s\n", __func__);
if (panel_data->caps & MSMFB_CAP_CABC) {
printk(KERN_INFO "CABC enabled\n");
mddi_nov_cabc.dev.platform_data = client_data;
platform_device_register(&mddi_nov_cabc);
}
ret = setup_vsync(panel, 1);
if (ret) {
dev_err(&pdev->dev, "mddi_bridge_setup_vsync failed\n");
return ret;
}
panel->client_data = client_data;
panel->panel_data.suspend = novtec_suspend;
panel->panel_data.resume = novtec_resume;
panel->panel_data.wait_vsync = novtec_wait_vsync;
panel->panel_data.request_vsync = novtec_request_vsync;
panel->panel_data.clear_vsync = novtec_clear_vsync;
panel->panel_data.blank = novtec_blank;
panel->panel_data.unblank = novtec_unblank;
panel->panel_data.fb_data = &bridge_data->fb_data;
panel->panel_data.caps = MSMFB_CAP_PARTIAL_UPDATES;
panel->pdev.name = "msm_panel";
panel->pdev.id = pdev->id;
panel->pdev.resource = client_data->fb_resource;
panel->pdev.num_resources = 1;
panel->pdev.dev.platform_data = &panel->panel_data;
platform_device_register(&panel->pdev);
wake_lock_init(&panel->idle_lock, WAKE_LOCK_IDLE, "nov_idle_lock");
return 0;
}
static int mddi_novtec_remove(struct platform_device *pdev)
{
struct panel_info *panel = platform_get_drvdata(pdev);
setup_vsync(panel, 0);
kfree(panel);
return 0;
}
static struct platform_driver mddi_client_d263_0000 = {
.probe = mddi_novtec_probe,
.remove = mddi_novtec_remove,
.driver = { .name = "mddi_c_b9f6_5582" },
};
static int __init mddi_client_novtec_init(void)
{
platform_driver_register(&mddi_client_d263_0000);
return 0;
}
module_init(mddi_client_novtec_init);

View File

@ -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 {

View File

@ -17,7 +17,6 @@
#include <linux/kernel.h>
#include <linux/fb.h>
#include <linux/io.h>
#include <linux/msm_mdp.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
@ -29,11 +28,11 @@
#include <mach/msm_iomap.h>
#include <mach/msm_fb.h>
#include <asm/mach-types.h>
#include <linux/platform_device.h>
#include "mdp_hw.h"
#include "mdp_ppp.h"
#include <asm/mach-types.h>
struct class *mdp_class;
@ -42,6 +41,18 @@ struct class *mdp_class;
static DECLARE_WAIT_QUEUE_HEAD(mdp_ppp_waitqueue);
static unsigned int mdp_irq_mask;
struct clk *mdp_clk_to_disable_later = 0;
static struct mdp_blit_req *timeout_req;
#ifdef CONFIG_FB_MSM_OVERLAY
extern int mdp4_overlay_get(struct mdp_device *mdp_dev, struct fb_info *info, struct mdp_overlay *req);
extern int mdp4_overlay_set(struct mdp_device *mdp_dev, struct fb_info *info, struct mdp_overlay *req);
extern int mdp4_overlay_unset(struct mdp_device *mdp_dev, struct fb_info *info, int ndx);
extern int mdp4_overlay_play(struct mdp_device *mdp_dev, struct fb_info *info, struct msmfb_overlay_data *req,
struct file **pp_src_file);
extern void mdp4_mddi_overlay(void *priv, uint32_t addr, uint32_t stride,
uint32_t width, uint32_t height, uint32_t x,
uint32_t y);
#include "mdp4.h"
#endif
DEFINE_MUTEX(mdp_mutex);
static int locked_enable_mdp_irq(struct mdp_info *mdp, uint32_t mask)
@ -51,16 +62,14 @@ static int locked_enable_mdp_irq(struct mdp_info *mdp, uint32_t mask)
/* if the mask bits are already set return an error, this interrupt
* is already enabled */
if (mdp_irq_mask & mask) {
// pr_err("mdp irq already on %x %x\n", mdp_irq_mask, mask);
pr_err("mdp irq already on %x %x\n", mdp_irq_mask, mask);
return -1;
}
/* if the mdp irq is not already enabled enable it */
if (!mdp_irq_mask) {
clk_set_rate(mdp->ebi1_clk, 128000000);
clk_enable(mdp->clk);
enable_irq(mdp->irq);
clk_set_rate(mdp->ebi1_clk, 128000000);
}
/* clear out any previous irqs for the requested mask*/
@ -86,8 +95,6 @@ static int enable_mdp_irq(struct mdp_info *mdp, uint32_t mask)
static int locked_disable_mdp_irq(struct mdp_info *mdp, uint32_t mask)
{
if (!mask) return 1;
/* this interrupt is already disabled! */
if (!(mdp_irq_mask & mask)) {
printk(KERN_ERR "mdp irq already off %x %x\n",
@ -102,11 +109,9 @@ static int locked_disable_mdp_irq(struct mdp_info *mdp, uint32_t mask)
/* if no one is waiting on the interrupt, disable it */
if (!mdp_irq_mask) {
disable_irq_nosync(mdp->irq);
if (mdp->clk)
clk_disable(mdp->clk);
clk_set_rate(mdp->ebi1_clk, 0);
}
return 0;
}
@ -133,13 +138,21 @@ static irqreturn_t mdp_isr(int irq, void *data)
status = mdp_readl(mdp, MDP_INTR_STATUS);
mdp_writel(mdp, status, MDP_INTR_CLEAR);
#if defined(CONFIG_MACH_HTCLEO)
status &= ~0x10000; // Cotulla
#endif
// pr_info("%s: status=%08x (irq_mask=%08x)\n", __func__, status,
// mdp_irq_mask);
status &= mdp_irq_mask;
#ifdef CONFIG_MSM_MDP40
if (mdp->mdp_dev.overrides & MSM_MDP4_MDDI_DMA_SWITCH) {
if(status && mdp->out_if[MSM_MDDI_PMDH_INTERFACE].dma_cb != NULL)
status |= (INTR_OVERLAY0_DONE | MDP_DMA_S_DONE);
}
#endif
for (i = 0; i < MSM_MDP_NUM_INTERFACES; ++i) {
struct mdp_out_interface *out_if = &mdp->out_if[i];
if (status & out_if->dma_mask) {
@ -182,20 +195,16 @@ static int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq)
unsigned long irq_flags;
// pr_info("%s: WAITING for 0x%x\n", __func__, mask);
wait_event_timeout(*wq, !mdp_check_mask(mdp, mask), HZ); // Cotulla TEST
wait_event_timeout(*wq, !mdp_check_mask(mdp, mask), HZ);
spin_lock_irqsave(&mdp->lock, irq_flags);
if (mdp_irq_mask & mask) {
locked_disable_mdp_irq(mdp, mask);
pr_warning("%s: timeout waiting for mdp to complete 0x%x\n",
__func__, mask);
printk("GLBL_CLK_ENA: %08X\n", readl(MSM_CLK_CTL_BASE + 0x0000));
printk("GLBL_CLK_STATE: %08X\n", readl(MSM_CLK_CTL_BASE + 0x0004));
printk("GLBL_SLEEP_EN: %08X\n", readl(MSM_CLK_CTL_BASE + 0x001C));
printk("GLBL_CLK_ENA_2: %08X\n", readl(MSM_CLK_CTL_BASE + 0x0220));
printk("GLBL_CLK_STATE_2: %08X\n", readl(MSM_CLK_CTL_BASE + 0x0224));
printk("GLBL_CLK_SLEEP_EN_2: %08X\n", readl(MSM_CLK_CTL_BASE + 0x023C));
mdp_ppp_dump_debug(mdp);
locked_disable_mdp_irq(mdp, mask);
if(timeout_req)
mdp_dump_blit(timeout_req);
ret = -ETIMEDOUT;
} else {
// pr_info("%s: SUCCESS waiting for 0x%x\n", __func__, mask);
@ -217,6 +226,7 @@ void mdp_dma_wait(struct mdp_device *mdp_dev, int interface)
case MSM_MDDI_PMDH_INTERFACE:
case MSM_MDDI_EMDH_INTERFACE:
case MSM_LCDC_INTERFACE:
case MSM_TV_INTERFACE:
BUG_ON(!mdp->out_if[interface].registered);
mask = mdp->out_if[interface].dma_mask;
wq = &mdp->out_if[interface].dma_waitqueue;
@ -243,15 +253,15 @@ static int mdp_ppp_wait(struct mdp_info *mdp)
return mdp_wait(mdp, DL0_ROI_DONE, &mdp_ppp_waitqueue);
}
static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride,
uint32_t width, uint32_t height, uint32_t x,
uint32_t y)
static void mdp_dmas_to_mddi(void *priv, uint32_t addr, uint32_t stride,
uint32_t width, uint32_t height, uint32_t x, uint32_t y)
{
struct mdp_info *mdp = priv;
uint32_t dma2_cfg;
uint16_t ld_param = 0; /* 0=PRIM, 1=SECD, 2=EXT */
uint32_t video_packet_parameter;
uint16_t ld_param = 1;
printk("MDDI start\n");
if(machine_is_htcleo()) {
dma2_cfg = DMA_PACK_ALIGN_MSB |
DMA_PACK_PATTERN_RGB;
@ -261,30 +271,120 @@ static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride,
dma2_cfg |= DMA_OUT_SEL_LCDC;
dma2_cfg |= DMA_IBUF_FORMAT_RGB565;
/* 655 16BPP */
dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS;
}
else {
} else {
dma2_cfg = DMA_PACK_TIGHT |
DMA_PACK_ALIGN_LSB |
DMA_PACK_PATTERN_RGB |
DMA_OUT_SEL_AHB |
DMA_IBUF_NONCONTIGUOUS;
dma2_cfg |= mdp->format;
dma2_cfg |= DMA_OUT_SEL_MDDI;
#if defined CONFIG_MSM_MDP22 || defined CONFIG_MSM_MDP30
if (mdp->format == DMA_IBUF_FORMAT_RGB888_OR_ARGB8888)
#else
if (mdp->format == DMA_IBUF_FORMAT_XRGB8888)
#endif
dma2_cfg |= DMA_PACK_PATTERN_BGR;
else
dma2_cfg |= DMA_PACK_PATTERN_RGB;
dma2_cfg |= DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY;
dma2_cfg |= DMA_OUT_SEL_MDDI;
dma2_cfg |= DMA_DITHER_EN;
dma2_cfg |= DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY;
/* 666 18BPP */
dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS;
dma2_cfg |= DMA_DITHER_EN;
}
#ifdef CONFIG_MSM_MDP22
if (mdp->mdp_dev.color_format == MSM_MDP_OUT_IF_FMT_RGB565) {
dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS;
video_packet_parameter = MDDI_VDO_PACKET_DESC_RGB565;
} else if (mdp->mdp_dev.color_format == MSM_MDP_OUT_IF_FMT_RGB666) {
dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS;
video_packet_parameter = MDDI_VDO_PACKET_DESC_RGB666;
}
/* setup size, address, and stride */
mdp_writel(mdp, (height << 16) | (width), MDP_DMA_S_SIZE);
mdp_writel(mdp, addr, MDP_DMA_S_IBUF_ADDR);
mdp_writel(mdp, stride, MDP_DMA_S_IBUF_Y_STRIDE);
/* set y & x offset and MDDI transaction parameters */
mdp_writel(mdp, (y << 16) | (x), MDP_DMA_S_OUT_XY);
mdp_writel(mdp, ld_param, MDP_MDDI_PARAM_WR_SEL);
if (mdp->mdp_dev.overrides & MSM_MDP_PANEL_IGNORE_PIXEL_DATA) {
mdp_writel(mdp, (video_packet_parameter << 16) | 0xE3,
MDP_MDDI_PARAM);
}
else {
mdp_writel(mdp, (video_packet_parameter << 16) | MDDI_VDO_PACKET_PRIM,
MDP_MDDI_PARAM);
}
mdp_writel(mdp, dma2_cfg, MDP_DMA_S_CONFIG);
mdp_writel(mdp, 0, MDP_DMA_S_START);
}
static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride,
uint32_t width, uint32_t height, uint32_t x,
uint32_t y)
{
struct mdp_info *mdp = priv;
uint32_t dma2_cfg = 0;
uint32_t video_packet_parameter = 0;
uint16_t ld_param = 0; /* 0=PRIM, 1=SECD, 2=EXT */
#if !defined(CONFIG_MSM_MDP30)
dma2_cfg = DMA_PACK_TIGHT |
DMA_PACK_ALIGN_LSB |
DMA_OUT_SEL_AHB |
DMA_IBUF_NONCONTIGUOUS;
#endif
dma2_cfg |= mdp->format;
#if defined CONFIG_MSM_MDP22 || defined CONFIG_MSM_MDP30
if (mdp->format == DMA_IBUF_FORMAT_RGB888_OR_ARGB8888)
#else
if (mdp->format == DMA_IBUF_FORMAT_XRGB8888)
#endif
dma2_cfg |= DMA_PACK_PATTERN_BGR;
else
dma2_cfg |= DMA_PACK_PATTERN_RGB;
dma2_cfg |= DMA_OUT_SEL_MDDI;
dma2_cfg |= DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY;
#if !defined(CONFIG_MSM_MDP30)
dma2_cfg |= DMA_DITHER_EN;
#endif
if (mdp->mdp_dev.color_format == MSM_MDP_OUT_IF_FMT_RGB565) {
dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS;
video_packet_parameter = MDDI_VDO_PACKET_DESC_RGB565;
} else if (mdp->mdp_dev.color_format == MSM_MDP_OUT_IF_FMT_RGB666) {
dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS;
video_packet_parameter = MDDI_VDO_PACKET_DESC_RGB666;
}
#if defined(CONFIG_MSM_MDP30) || defined(CONFIG_MSM_MDP302)
writel(height << 16 | width, mdp->base + 0x90004);
writel(addr, mdp->base + 0x90008);
writel(stride, mdp->base + 0x9000c);
/* set y & x offset and MDDI transaction parameters */
writel(y << 16 | x, mdp->base + 0x90010);
writel(ld_param, mdp->base + 0x00090);
writel((video_packet_parameter << 16) | MDDI_VDO_PACKET_PRIM,
mdp->base + 0x00094);
writel(dma2_cfg, mdp->base + 0x90000);
/* start DMA2 */
writel(0, mdp->base + 0x0044);
#elif defined(CONFIG_MSM_MDP22)
/* setup size, address, and stride */
mdp_writel(mdp, (height << 16) | (width),
MDP_CMD_DEBUG_ACCESS_BASE + 0x0184);
@ -294,7 +394,7 @@ static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride,
/* set y & x offset and MDDI transaction parameters */
mdp_writel(mdp, (y << 16) | (x), MDP_CMD_DEBUG_ACCESS_BASE + 0x0194);
mdp_writel(mdp, ld_param, MDP_CMD_DEBUG_ACCESS_BASE + 0x01a0);
mdp_writel(mdp, (MDDI_VDO_PACKET_DESC << 16) | MDDI_VDO_PACKET_PRIM,
mdp_writel(mdp, (video_packet_parameter << 16) | MDDI_VDO_PACKET_PRIM,
MDP_CMD_DEBUG_ACCESS_BASE + 0x01a4);
mdp_writel(mdp, dma2_cfg, MDP_CMD_DEBUG_ACCESS_BASE + 0x0180);
@ -310,7 +410,7 @@ static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride,
/* set y & x offset and MDDI transaction parameters */
mdp_writel(mdp, (y << 16) | (x), MDP_DMA_P_OUT_XY);
mdp_writel(mdp, ld_param, MDP_MDDI_PARAM_WR_SEL);
mdp_writel(mdp, (MDDI_VDO_PACKET_DESC << 16) | MDDI_VDO_PACKET_PRIM,
mdp_writel(mdp, (video_packet_parameter << 16) | MDDI_VDO_PACKET_PRIM,
MDP_MDDI_PARAM);
mdp_writel(mdp, dma2_cfg, MDP_DMA_P_CONFIG);
@ -326,7 +426,7 @@ void mdp_dma(struct mdp_device *mdp_dev, uint32_t addr, uint32_t stride,
struct mdp_out_interface *out_if;
unsigned long flags;
if (interface < 0 || interface > MSM_MDP_NUM_INTERFACES ||
if (interface < 0 || interface >= MSM_MDP_NUM_INTERFACES ||
!mdp->out_if[interface].registered) {
pr_err("%s: Unknown interface: %d\n", __func__, interface);
BUG();
@ -335,7 +435,7 @@ void mdp_dma(struct mdp_device *mdp_dev, uint32_t addr, uint32_t stride,
spin_lock_irqsave(&mdp->lock, flags);
if (locked_enable_mdp_irq(mdp, out_if->dma_mask)) {
// pr_err("%s: busy\n", __func__);
pr_err("%s: busy\n", __func__);
goto done;
}
@ -417,14 +517,14 @@ int mdp_check_output_format(struct mdp_device *mdp_dev, int bpp)
int mdp_set_output_format(struct mdp_device *mdp_dev, int bpp)
{
struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
uint32_t format, pack_pattern;
uint32_t format, pack_pattern = DMA_PACK_PATTERN_RGB;
switch (bpp) {
case 16:
format = DMA_IBUF_FORMAT_RGB565;
pack_pattern = DMA_PACK_PATTERN_RGB;
break;
#ifdef CONFIG_MSM_MDP22
#if defined CONFIG_MSM_MDP22 || defined CONFIG_MSM_MDP30
case 24:
case 32:
format = DMA_IBUF_FORMAT_RGB888_OR_ARGB8888;
@ -480,8 +580,8 @@ static void dump_req(struct mdp_blit_req *req,
}
int mdp_blit_and_wait(struct mdp_info *mdp, struct mdp_blit_req *req,
struct file *src_file, unsigned long src_start, unsigned long src_len,
struct file *dst_file, unsigned long dst_start, unsigned long dst_len)
struct file *src_file, unsigned long src_start, unsigned long src_len,
struct file *dst_file, unsigned long dst_start, unsigned long dst_len)
{
int ret;
enable_mdp_irq(mdp, DL0_ROI_DONE);
@ -495,17 +595,13 @@ int mdp_blit_and_wait(struct mdp_info *mdp, struct mdp_blit_req *req,
ret = mdp_ppp_wait(mdp);
if (unlikely(ret)) {
printk(KERN_ERR "%s: failed!\n", __func__);
// pr_err("original request:\n");
// dump_req(mdp->req, src_start, src_len, dst_start, dst_len);
pr_err("original request:\n");
dump_req(mdp->req, src_start, src_len, dst_start, dst_len);
pr_err("dead request:\n");
dump_req(req, src_start, src_len, dst_start, dst_len);
//BUG();
BUG();
return ret;
}
// Cotulla: Disable each request dump to prevent spam in logs
// pr_err("good request:\n");
// dump_req(req, src_start, src_len, dst_start, dst_len);
return 0;
}
@ -517,9 +613,7 @@ int mdp_blit(struct mdp_device *mdp_dev, struct fb_info *fb,
struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
struct file *src_file = 0, *dst_file = 0;
printk("+MDP_BLIT: %08X\n", req->flags);
#ifdef CONFIG_MSM_MDP31
#if defined(CONFIG_MSM_MDP31) || defined(CONFIG_MSM_MDP302)
if (req->flags & MDP_ROT_90) {
if (unlikely(((req->dst_rect.h == 1) &&
((req->src_rect.w != 1) ||
@ -567,10 +661,11 @@ int mdp_blit(struct mdp_device *mdp_dev, struct fb_info *fb,
}
mutex_lock(&mdp_mutex);
timeout_req = req;
/* transp_masking unimplemented */
req->transp_mask = MDP_TRANSP_NOP;
mdp->req = req;
#ifndef CONFIG_MSM_MDP31
#if !defined(CONFIG_MSM_MDP31) && !defined(CONFIG_MSM_MDP302)
if (unlikely((req->transp_mask != MDP_TRANSP_NOP ||
req->alpha != MDP_ALPHA_NOP ||
HAS_ALPHA(req->src.format)) &&
@ -622,7 +717,37 @@ end:
put_img(src_file);
put_img(dst_file);
mutex_unlock(&mdp_mutex);
printk("-MDP_BLIT\n");
return ret;
}
int mdp_fb_mirror(struct mdp_device *mdp_dev,
struct fb_info *src_fb, struct fb_info *dst_fb,
struct mdp_blit_req *req)
{
int ret;
struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
if (!src_fb || !dst_fb)
return -EINVAL;
enable_mdp_irq(mdp, DL0_ROI_DONE);
ret = mdp_ppp_blit(mdp, req,
-1, src_fb->fix.smem_start, src_fb->fix.smem_len,
-1, dst_fb->fix.smem_start, dst_fb->fix.smem_len);
if (ret)
goto err_bad_blit;
ret = mdp_ppp_wait(mdp);
if (ret) {
pr_err("mdp_ppp_wait error\n");
goto err_wait_failed;
}
return 0;
err_bad_blit:
disable_mdp_irq(mdp, DL0_ROI_DONE);
err_wait_failed:
return ret;
}
@ -690,7 +815,7 @@ int mdp_out_if_req_irq(struct mdp_device *mdp_dev, int interface,
if (mask) {
ret = locked_enable_mdp_irq(mdp, mask);
if (ret) {
// pr_err("%s: busy\n", __func__);
pr_err("%s: busy\n", __func__);
goto done;
}
mdp->out_if[interface].irq_mask = mask;
@ -716,11 +841,26 @@ int register_mdp_client(struct class_interface *cint)
return class_interface_register(cint);
}
#ifdef CONFIG_MSM_MDP40
void mdp_hw_init(struct mdp_info *mdp)
{
mdp_irq_mask = 0;
mdp_writel(mdp, 0, MDP_INTR_ENABLE);
}
#else
#include "mdp_csc_table.h"
void mdp_check_tearing(struct mdp_info *mdp, struct msm_mdp_platform_data *pdata)
{
mdp_writel(mdp, pdata->sync_config, MDP_SYNC_CONFIG_0);
mdp_writel(mdp, 1, MDP_TEAR_CHECK_EN);
mdp_writel(mdp, pdata->sync_thresh, MDP_SYNC_THRESH_0);
mdp_writel(mdp, pdata->sync_start_pos, MDP_PRIM_START_POS);
}
void mdp_hw_init(struct mdp_info *mdp)
{
int n;
int lcdc_enabled;
mdp_irq_mask = 0;
@ -731,6 +871,7 @@ void mdp_hw_init(struct mdp_info *mdp)
mdp_writel(mdp, 1, MDP_EBI2_PORTMAP_MODE);
#ifndef CONFIG_MSM_MDP22
lcdc_enabled = mdp_readl(mdp, MDP_LCDC_EN);
/* disable lcdc */
mdp_writel(mdp, 0, MDP_LCDC_EN);
/* enable auto clock gating for all blocks by default */
@ -781,15 +922,19 @@ void mdp_hw_init(struct mdp_info *mdp)
#ifndef CONFIG_MSM_MDP31
mdp_writel(mdp, 0x04000400, MDP_COMMAND_CONFIG);
#endif
#ifndef CONFIG_MSM_MDP22
if(lcdc_enabled)
mdp_writel(mdp, 1, MDP_LCDC_EN);
#endif
}
#endif //CONFIG_MSM_MDP40
int mdp_probe(struct platform_device *pdev)
{
struct resource *resource;
int ret;
int ret = -EINVAL;
struct mdp_info *mdp;
struct msm_mdp_platform_data *pdata = pdev->dev.platform_data;
resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!resource) {
@ -821,13 +966,49 @@ int mdp_probe(struct platform_device *pdev)
mdp->mdp_dev.dma = mdp_dma;
mdp->mdp_dev.dma_wait = mdp_dma_wait;
mdp->mdp_dev.blit = mdp_blit;
#ifdef CONFIG_FB_MSM_OVERLAY
mdp->mdp_dev.overlay_get = mdp4_overlay_get;
mdp->mdp_dev.overlay_set = mdp4_overlay_set;
mdp->mdp_dev.overlay_unset = mdp4_overlay_unset;
mdp->mdp_dev.overlay_play = mdp4_overlay_play;
#endif
mdp->mdp_dev.set_grp_disp = mdp_set_grp_disp;
mdp->mdp_dev.set_output_format = mdp_set_output_format;
mdp->mdp_dev.check_output_format = mdp_check_output_format;
mdp->mdp_dev.configure_dma = mdp_configure_dma;
ret = mdp_out_if_register(&mdp->mdp_dev, MSM_MDDI_PMDH_INTERFACE, mdp,
MDP_DMA_P_DONE, mdp_dma_to_mddi);
if (pdata == NULL || pdata->overrides == 0)
mdp->mdp_dev.overrides = 0;
else if(pdata->overrides)
mdp->mdp_dev.overrides = pdata->overrides;
if (pdata == NULL || pdata->color_format == 0)
mdp->mdp_dev.color_format = MSM_MDP_OUT_IF_FMT_RGB565;
else if(pdata->color_format)
mdp->mdp_dev.color_format = pdata->color_format;
if (pdata == NULL || pdata->dma_channel == MDP_DMA_P) {
#ifdef CONFIG_MSM_MDP40
if (mdp->mdp_dev.overrides & MSM_MDP4_MDDI_DMA_SWITCH) {
ret = mdp_out_if_register(&mdp->mdp_dev,
MSM_MDDI_PMDH_INTERFACE, mdp, INTR_OVERLAY0_DONE
| MDP_DMA_S_DONE, mdp4_mddi_overlay);
} else {
ret = mdp_out_if_register(&mdp->mdp_dev,
MSM_MDDI_PMDH_INTERFACE, mdp, INTR_OVERLAY0_DONE,
mdp4_mddi_overlay);
}
#else
ret = mdp_out_if_register(&mdp->mdp_dev,
MSM_MDDI_PMDH_INTERFACE, mdp, MDP_DMA_P_DONE,
mdp_dma_to_mddi);
#endif
} else if (pdata->dma_channel == MDP_DMA_S) {
ret = mdp_out_if_register(&mdp->mdp_dev,
MSM_MDDI_PMDH_INTERFACE, mdp, MDP_DMA_S_DONE,
mdp_dmas_to_mddi);
}
if (ret)
goto error_mddi_pmdh_register;
@ -853,8 +1034,28 @@ int mdp_probe(struct platform_device *pdev)
clk_enable(mdp->clk);
mdp_clk_to_disable_later = mdp->clk;
mdp_hw_init(mdp);
#ifdef CONFIG_MSM_MDP40
//MDP_DISP_INTF_SEL
if (mdp_readl(mdp, 0xc0000))
mdp_writel(mdp, 0x8, 0x0038);
else
mdp_writel(mdp, 0xa, 0x0038); //mddi
//FIXME: should select mddi or lcdc interface
//mdp_writel(mdp, 0x8, 0x0038); //lcdc
#endif
#ifdef CONFIG_MSM_MDP40
extern void mdp4_hw_init(struct mdp_info *mdp);
mdp4_hw_init(mdp);
#else
mdp_hw_init(mdp);
#endif
#if defined CONFIG_MSM_MDP302
/* enable the tearing check in MDP */
if(pdata != NULL && pdata->tearing_check)
mdp_check_tearing(mdp, pdata);
#endif
/* register mdp device */
mdp->mdp_dev.dev.parent = &pdev->dev;
mdp->mdp_dev.dev.class = mdp_class;

View File

@ -74,23 +74,52 @@ void mdp_ppp_dump_debug(const struct mdp_info *mdp);
#define mdp_writel(mdp, value, offset) writel(value, mdp->base + offset)
#define mdp_readl(mdp, offset) readl(mdp->base + offset)
#define MDP_SYNC_CONFIG_0 (0x00000)
#define MDP_SYNC_CONFIG_1 (0x00004)
#define MDP_SYNC_CONFIG_2 (0x00008)
#define MDP_SYNC_STATUS_0 (0x0000c)
#define MDP_SYNC_STATUS_1 (0x00010)
#define MDP_SYNC_STATUS_2 (0x00014)
#define MDP_SYNC_THRESH_0 (0x00018)
#define MDP_SYNC_THRESH_1 (0x0001c)
#define MDP_INTR_ENABLE (0x00020)
#define MDP_INTR_STATUS (0x00024)
#define MDP_INTR_CLEAR (0x00028)
#define MDP_DISPLAY0_START (0x00030)
#define MDP_DISPLAY1_START (0x00034)
#define MDP_DISPLAY_STATUS (0x00038)
#define MDP_EBI2_LCD0 (0x0003c)
#define MDP_EBI2_LCD1 (0x00040)
#define MDP_EBI2_PORTMAP_MODE (0x0005c)
#ifdef CONFIG_MSM_MDP302
#define MDP_SYNC_CONFIG_0 ( 0x00300)
#define MDP_SYNC_CONFIG_1 ( 0x00304)
#define MDP_SYNC_CONFIG_2 ( 0x00308)
#else
#define MDP_SYNC_CONFIG_0 ( 0x00000)
#define MDP_SYNC_CONFIG_1 ( 0x00004)
#define MDP_SYNC_CONFIG_2 ( 0x00008)
#endif
#define MDP_SYNC_STATUS_0 ( 0x0000c)
#define MDP_SYNC_STATUS_1 ( 0x00010)
#define MDP_SYNC_STATUS_2 ( 0x00014)
#ifdef CONFIG_MSM_MDP302
#define MDP_SYNC_THRESH_0 ( 0x00200)
#define MDP_SYNC_THRESH_1 ( 0x00204)
#else
#define MDP_SYNC_THRESH_0 ( 0x00018)
#define MDP_SYNC_THRESH_1 ( 0x0001c)
#endif
#ifdef CONFIG_MSM_MDP40
#define MDP_INTR_ENABLE ( 0x0050)
#define MDP_INTR_STATUS ( 0x0054)
#define MDP_INTR_CLEAR ( 0x0058)
#define MDP_EBI2_LCD0 ( 0x0060)
#define MDP_EBI2_LCD1 ( 0x0064)
#define MDP_EBI2_PORTMAP_MODE ( 0x0070)
#define MDP_DMA_P_HIST_INTR_STATUS ( 0x95014)
#define MDP_DMA_P_HIST_INTR_CLEAR ( 0x95018)
#define MDP_DMA_P_HIST_INTR_ENABLE ( 0x9501C)
#else
#define MDP_INTR_ENABLE ( 0x00020)
#define MDP_INTR_STATUS ( 0x00024)
#define MDP_INTR_CLEAR ( 0x00028)
#define MDP_EBI2_LCD0 ( 0x0003c)
#define MDP_EBI2_LCD1 ( 0x00040)
#define MDP_EBI2_PORTMAP_MODE ( 0x0005c)
#endif
#define MDP_DISPLAY0_START ( 0x00030)
#define MDP_DISPLAY1_START ( 0x00034)
#define MDP_DISPLAY_STATUS ( 0x00038)
/* CONFIG_MSM_MDP302 */
#define MDP_TEAR_CHECK_EN ( 0x0020c)
#define MDP_PRIM_START_POS ( 0x00210)
#ifndef CONFIG_MSM_MDP31
#define MDP_DISPLAY0_ADDR (0x00054)
@ -228,7 +257,11 @@ void mdp_ppp_dump_debug(const struct mdp_info *mdp);
#define MDP_TEST_CAPTURED_DCLK (0xd0210)
#define MDP_TEST_MISR_CAPT_VAL_DCLK (0xd0214)
#ifdef CONFIG_MSM_MDP40
#define MDP_DMA_P_START (0x000c)
#else
#define MDP_DMA_P_START (0x00044)
#endif
#define MDP_DMA_P_CONFIG (0x90000)
#define MDP_DMA_P_SIZE (0x90004)
#define MDP_DMA_P_IBUF_ADDR (0x90008)
@ -236,6 +269,30 @@ void mdp_ppp_dump_debug(const struct mdp_info *mdp);
#define MDP_DMA_P_OUT_XY (0x90010)
#define MDP_DMA_P_COLOR_CORRECT_CONFIG (0x90070)
#define MDP_DMA_S_START (0x00048)
#define MDP_DMA_S_CONFIG (0xa0000)
#define MDP_DMA_S_SIZE (0xa0004)
#define MDP_DMA_S_IBUF_ADDR (0xa0008)
#define MDP_DMA_S_IBUF_Y_STRIDE (0xa000c)
#define MDP_DMA_S_OUT_XY (0xa0010)
#ifdef CONFIG_MSM_MDP40
#define MDP_LCDC_EN (0xc0000)
#define MDP_LCDC_HSYNC_CTL (0xc0004)
#define MDP_LCDC_VSYNC_PERIOD (0xc0008)
#define MDP_LCDC_VSYNC_PULSE_WIDTH (0xc000c)
#define MDP_LCDC_DISPLAY_HCTL (0xc0010)
#define MDP_LCDC_DISPLAY_V_START (0xc0014)
#define MDP_LCDC_DISPLAY_V_END (0xc0018)
#define MDP_LCDC_ACTIVE_HCTL (0xc001c)
#define MDP_LCDC_ACTIVE_V_START (0xc0020)
#define MDP_LCDC_ACTIVE_V_END (0xc0024)
#define MDP_LCDC_BORDER_CLR (0xc0028)
#define MDP_LCDC_UNDERFLOW_CTL (0xc002c)
#define MDP_LCDC_HSYNC_SKEW (0xc0030)
#define MDP_LCDC_TEST_CTL (0xc0034)
#define MDP_LCDC_CTL_POLARITY (0xc0038)
#else
#define MDP_LCDC_EN (0xe0000)
#define MDP_LCDC_HSYNC_CTL (0xe0004)
#define MDP_LCDC_VSYNC_PERIOD (0xe0008)
@ -251,6 +308,7 @@ void mdp_ppp_dump_debug(const struct mdp_info *mdp);
#define MDP_LCDC_HSYNC_SKEW (0xe0030)
#define MDP_LCDC_TEST_CTL (0xe0034)
#define MDP_LCDC_CTL_POLARITY (0xe0038)
#endif
#define MDP_PPP_SCALE_STATUS (0x50000)
#define MDP_PPP_BLEND_STATUS (0x70000)
@ -262,15 +320,30 @@ void mdp_ppp_dump_debug(const struct mdp_info *mdp);
#define DL0_ROI_DONE (1<<0)
#define TV_OUT_DMA3_DONE (1<<6)
#define TV_ENC_UNDERRUN (1<<7)
#define TV_OUT_FRAME_START (1<<13)
#ifdef CONFIG_MSM_MDP22
#define MDP_DMA_P_DONE (1 << 2)
#define MDP_DMA_S_DONE (1 << 3)
#else /* CONFIG_MSM_MDP31 */
#ifdef CONFIG_MSM_MDP40
#define MDP_DMA_P_DONE (1 << 4)
#else
#define MDP_DMA_P_DONE (1 << 14)
#endif
#define MDP_DMA_S_DONE (1 << 2)
#define MDP_LCDC_UNDERFLOW (1 << 16)
#ifdef CONFIG_MSM_MDP40
#define MDP_LCDC_FRAME_START (1 << 7)
#else
#define MDP_LCDC_FRAME_START (1 << 15)
#endif
#endif
#define MDP_TOP_LUMA 16
#define MDP_TOP_CHROMA 0
#define MDP_BOTTOM_LUMA 19
@ -294,9 +367,15 @@ void mdp_ppp_dump_debug(const struct mdp_info *mdp);
(((a)<<(bit*3))|((x)<<(bit*2))|((y)<<bit)|(z))
/* MDP_SYNC_CONFIG_0/1/2 */
#if defined(CONFIG_MSM_MDP30)
#define MDP_SYNCFG_HGT_LOC 21
#define MDP_SYNCFG_VSYNC_EXT_EN (1<<20)
#define MDP_SYNCFG_VSYNC_INT_EN (1<<19)
#else
#define MDP_SYNCFG_HGT_LOC 22
#define MDP_SYNCFG_VSYNC_EXT_EN (1<<21)
#define MDP_SYNCFG_VSYNC_INT_EN (1<<20)
#endif
/* MDP_SYNC_THRESH_0 */
#define MDP_PRIM_BELOW_LOC 0
@ -309,11 +388,11 @@ void mdp_ppp_dump_debug(const struct mdp_info *mdp);
/* MDP_VSYNC_CTRL */
#define DISP0_VSYNC_MAP_VSYNC0 0
#define DISP0_VSYNC_MAP_VSYNC1 (1<<0)
#define DISP0_VSYNC_MAP_VSYNC2 ((1<<0)|(1<<1))
#define DISP0_VSYNC_MAP_VSYNC2 (1<<0)|(1<<1)
#define DISP1_VSYNC_MAP_VSYNC0 0
#define DISP1_VSYNC_MAP_VSYNC1 (1<<2)
#define DISP1_VSYNC_MAP_VSYNC2 ((1<<2)|(1<<3))
#define DISP1_VSYNC_MAP_VSYNC2 (1<<2)|(1<<3)
#define PRIMARY_LCD_SYNC_EN (1<<4)
#define PRIMARY_LCD_SYNC_DISABLE 0
@ -744,7 +823,25 @@ void mdp_ppp_dump_debug(const struct mdp_info *mdp);
#define DMA_IBUF_FORMAT_MASK (1 << 20)
#define DMA_IBUF_NONCONTIGUOUS (1<<21)
#else /* CONFIG_MSM_MDP31 */
#elif defined(CONFIG_MSM_MDP30)
#define DMA_OUT_SEL_AHB 0
#define DMA_OUT_SEL_MDDI (1<<19)
#define DMA_AHBM_LCD_SEL_PRIMARY 0
#define DMA_AHBM_LCD_SEL_SECONDARY (0)
#define DMA_IBUF_C3ALPHA_EN (0)
#define DMA_DITHER_EN (1<<24)
#define DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY 0
#define DMA_MDDI_DMAOUT_LCD_SEL_SECONDARY (0)
#define DMA_MDDI_DMAOUT_LCD_SEL_EXTERNAL (0)
#define DMA_IBUF_FORMAT_MASK (1 << 20)
#define DMA_IBUF_FORMAT_RGB565 (1<<25)
#define DMA_IBUF_FORMAT_RGB888_OR_ARGB8888 (1<<26)
#define DMA_IBUF_NONCONTIGUOUS (0)
#else /* CONFIG_MSM_MDP31 | CONFIG_MSM_MDP302 */
#define DMA_OUT_SEL_AHB (0 << 19)
#define DMA_OUT_SEL_MDDI (1 << 19)
@ -763,7 +860,8 @@ void mdp_ppp_dump_debug(const struct mdp_info *mdp);
#endif
/* MDDI REGISTER ? */
#define MDDI_VDO_PACKET_DESC 0x5666
#define MDDI_VDO_PACKET_DESC_RGB565 0x5565
#define MDDI_VDO_PACKET_DESC_RGB666 0x5666
#define MDDI_VDO_PACKET_PRIM 0xC3
#define MDDI_VDO_PACKET_SECD 0xC0

View File

@ -26,11 +26,23 @@
#include <asm/io.h>
#include <asm/mach-types.h>
#include <linux/msm_mdp.h>
#include <mach/msm_fb.h>
#include "mdp_hw.h"
#ifdef CONFIG_MSM_MDP40
#include "mdp4.h"
#endif
#if 0
#define D(fmt, args...) printk(KERN_INFO "Dispaly: " fmt, ##args)
#else
#define D(fmt, args...) do {} while (0)
#endif
#if defined(CONFIG_ARCH_MSM7227)
#define LCDC_MUX_CTL (MSM_TGPIO1_BASE + 0x278)
#endif
struct mdp_lcdc_info {
struct mdp_info *mdp;
struct clk *mdp_clk;
@ -44,7 +56,7 @@ struct mdp_lcdc_info {
struct msmfb_callback frame_start_cb;
wait_queue_head_t vsync_waitq;
int got_vsync;
unsigned color_format;
struct {
uint32_t clk_rate;
uint32_t hsync_ctl;
@ -60,6 +72,10 @@ struct mdp_lcdc_info {
static struct mdp_device *mdp_dev;
#ifdef CONFIG_MSM_MDP40
static struct mdp4_overlay_pipe *lcdc_pipe;
#endif
#define panel_to_lcdc(p) container_of((p), struct mdp_lcdc_info, fb_panel_data)
static int lcdc_unblank(struct msm_panel_data *fb_panel)
@ -68,6 +84,7 @@ static int lcdc_unblank(struct msm_panel_data *fb_panel)
struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops;
pr_info("%s: ()\n", __func__);
panel_ops->unblank(panel_ops);
return 0;
@ -86,27 +103,49 @@ static int lcdc_blank(struct msm_panel_data *fb_panel)
static int lcdc_suspend(struct msm_panel_data *fb_panel)
{
int status;
struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops;
pr_info("%s: suspending\n", __func__);
#if defined(CONFIG_ARCH_MSM7227)
writel(0x0, LCDC_MUX_CTL);
status = readl(LCDC_MUX_CTL);
D("suspend_lcdc_mux_ctl = %x\n", status);
#endif
mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN);
clk_disable(lcdc->pad_pclk);
clk_disable(lcdc->pclk);
clk_disable(lcdc->mdp_clk);
if (panel_ops->uninit)
panel_ops->uninit(panel_ops);
return 0;
}
static int lcdc_resume(struct msm_panel_data *fb_panel)
{
unsigned int status;
struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops;
pr_info("%s: resuming\n", __func__);
if (panel_ops->init) {
if (panel_ops->init(panel_ops) < 0)
printk(KERN_ERR "LCD init fail!\n");
}
clk_enable(lcdc->mdp_clk);
clk_enable(lcdc->pclk);
clk_enable(lcdc->pad_pclk);
#if defined(CONFIG_ARCH_MSM7227)
writel(0x1, LCDC_MUX_CTL);
status = readl(LCDC_MUX_CTL);
D("resume_lcdc_mux_ctl = %x\n",status);
#endif
mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN);
return 0;
@ -124,8 +163,6 @@ static int lcdc_hw_init(struct mdp_lcdc_info *lcdc)
clk_set_rate(lcdc->pclk, lcdc->parms.clk_rate);
clk_set_rate(lcdc->pad_pclk, lcdc->parms.clk_rate);
// mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN); // Cotulla
/* write the lcdc params */
mdp_writel(lcdc->mdp, lcdc->parms.hsync_ctl, MDP_LCDC_HSYNC_CTL);
mdp_writel(lcdc->mdp, lcdc->parms.vsync_period, MDP_LCDC_VSYNC_PERIOD);
@ -138,7 +175,7 @@ static int lcdc_hw_init(struct mdp_lcdc_info *lcdc)
mdp_writel(lcdc->mdp, lcdc->parms.hsync_skew, MDP_LCDC_HSYNC_SKEW);
mdp_writel(lcdc->mdp, 0, MDP_LCDC_BORDER_CLR);
mdp_writel(lcdc->mdp, 0xFFFFFF | 0x80000000, MDP_LCDC_UNDERFLOW_CTL); // Cotulla
mdp_writel(lcdc->mdp, 0xff, MDP_LCDC_UNDERFLOW_CTL);
mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_HCTL);
mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_START);
mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_END);
@ -152,38 +189,28 @@ static int lcdc_hw_init(struct mdp_lcdc_info *lcdc)
mdp_writel(lcdc->mdp, 0, MDP_DMA_P_OUT_XY);
dma_cfg = mdp_readl(lcdc->mdp, MDP_DMA_P_CONFIG);
dma_cfg |= (DMA_PACK_ALIGN_LSB |
DMA_PACK_PATTERN_RGB |
DMA_DITHER_EN);
dma_cfg |= DMA_OUT_SEL_LCDC;
dma_cfg &= ~DMA_DST_BITS_MASK;
if(machine_is_htcleo()) {
dma_cfg = DMA_PACK_ALIGN_MSB |
DMA_PACK_PATTERN_RGB;
dma_cfg |= DMA_OUT_SEL_LCDC;
dma_cfg &= ~DMA_DST_BITS_MASK;
}
else {
dma_cfg |= (DMA_PACK_ALIGN_LSB |
DMA_PACK_PATTERN_RGB |
} else {
dma_cfg = mdp_readl(lcdc->mdp, MDP_DMA_P_CONFIG);
if (lcdc->pdata->overrides & MSM_MDP_LCDC_DMA_PACK_ALIGN_LSB)
dma_cfg &= ~DMA_PACK_ALIGN_MSB;
else
dma_cfg |= DMA_PACK_ALIGN_MSB;
dma_cfg |= (DMA_PACK_PATTERN_RGB |
DMA_DITHER_EN);
dma_cfg |= DMA_OUT_SEL_LCDC;
dma_cfg &= ~DMA_DST_BITS_MASK;
}
/* enable the lcdc timing generation */
mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN);
if (fb_panel->fb_data->output_format == MSM_MDP_OUT_IF_FMT_RGB666)
dma_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS;
else
if(lcdc->color_format == MSM_MDP_OUT_IF_FMT_RGB565)
dma_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS;
else if (lcdc->color_format == MSM_MDP_OUT_IF_FMT_RGB666)
dma_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS;
mdp_writel(lcdc->mdp, dma_cfg, MDP_DMA_P_CONFIG);
@ -193,14 +220,12 @@ static int lcdc_hw_init(struct mdp_lcdc_info *lcdc)
return 0;
}
static void lcdc_wait_vsync(struct msm_panel_data *panel)
{
struct mdp_lcdc_info *lcdc = panel_to_lcdc(panel);
int ret;
ret = wait_event_timeout(lcdc->vsync_waitq, lcdc->got_vsync, HZ / 2);
ret = wait_event_timeout(lcdc->vsync_waitq, lcdc->got_vsync, HZ / 2);
if (!ret && !lcdc->got_vsync)
pr_err("%s: timeout waiting for VSYNC\n", __func__);
lcdc->got_vsync = 0;
@ -233,13 +258,13 @@ static void lcdc_request_vsync(struct msm_panel_data *fb_panel,
// vsync_cb->func(vsync_cb);
}
else
{
lcdc->got_vsync = 0;
mdp_out_if_req_irq(mdp_dev, MSM_LCDC_INTERFACE, MDP_LCDC_FRAME_START, &lcdc->frame_start_cb);
lcdc_wait_vsync(fb_panel);
}
{
lcdc->got_vsync = 0;
mdp_out_if_req_irq(mdp_dev, MSM_LCDC_INTERFACE, MDP_LCDC_FRAME_START,
&lcdc->frame_start_cb);
lcdc_wait_vsync(fb_panel);
}
// CotullaFIX end
}
static void lcdc_clear_vsync(struct msm_panel_data *fb_panel)
@ -266,13 +291,11 @@ static void lcdc_dma_start(void *priv, uint32_t addr, uint32_t stride,
uint32_t y)
{
struct mdp_lcdc_info *lcdc = priv;
// printk("DMA start\n");
struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
if (mdp->dma_config_dirty)
{
mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN);
mdelay(20);
mdelay(30);
mdp_dev->configure_dma(mdp_dev);
mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN);
}
@ -280,6 +303,35 @@ static void lcdc_dma_start(void *priv, uint32_t addr, uint32_t stride,
mdp_writel(lcdc->mdp, addr, MDP_DMA_P_IBUF_ADDR);
}
#ifdef CONFIG_MSM_MDP40
static void lcdc_overlay_start(void *priv, uint32_t addr, uint32_t stride,
uint32_t width, uint32_t height, uint32_t x,
uint32_t y)
{
struct mdp_lcdc_info *lcdc = priv;
struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
struct mdp4_overlay_pipe *pipe;
pipe = lcdc_pipe;
pipe->srcp0_addr = addr;
if (mdp->dma_config_dirty)
{
if(mdp->format == DMA_IBUF_FORMAT_RGB565) {
pipe->src_format = MDP_RGB_565;
pipe->srcp0_ystride = pipe->src_width * 2;
} else if(mdp->format == DMA_IBUF_FORMAT_XRGB8888) {
pipe->src_format = MDP_RGBA_8888;
pipe->srcp0_ystride = pipe->src_width * 4;
}
mdp4_overlay_format2pipe(pipe);
mdp->dma_config_dirty = false;
}
mdp4_overlay_rgb_setup(pipe);
mdp4_overlay_reg_flush(pipe, 1); /* rgb1 and mixer0 */
}
#endif
static void precompute_timing_parms(struct mdp_lcdc_info *lcdc)
{
struct msm_lcdc_timing *timing = lcdc->pdata->timing;
@ -291,21 +343,21 @@ static void precompute_timing_parms(struct mdp_lcdc_info *lcdc)
unsigned int display_vstart;
unsigned int display_vend;
hsync_period = (timing->hsync_back_porch +
hsync_period = (timing->hsync_pulse_width + timing->hsync_back_porch +
fb_data->xres + timing->hsync_front_porch);
hsync_start_x = timing->hsync_back_porch;
hsync_start_x = (timing->hsync_pulse_width + timing->hsync_back_porch);
hsync_end_x = hsync_start_x + fb_data->xres - 1;
vsync_period = (timing->vsync_back_porch +
vsync_period = (timing->vsync_pulse_width + timing->vsync_back_porch +
fb_data->yres + timing->vsync_front_porch);
vsync_period *= hsync_period;
display_vstart = timing->vsync_back_porch;
display_vstart = timing->vsync_pulse_width + timing->vsync_back_porch;
display_vstart *= hsync_period;
display_vstart += timing->hsync_skew;
display_vend = (timing->vsync_back_porch + fb_data->yres) *
hsync_period;
display_vend = (timing->vsync_pulse_width + timing->vsync_back_porch +
fb_data->yres) * hsync_period;
display_vend += timing->hsync_skew - 1;
/* register values we pre-compute at init time from the timing
@ -332,6 +384,10 @@ static int mdp_lcdc_probe(struct platform_device *pdev)
struct msm_lcdc_platform_data *pdata = pdev->dev.platform_data;
struct mdp_lcdc_info *lcdc;
int ret = 0;
#ifdef CONFIG_MSM_MDP40
struct mdp4_overlay_pipe *pipe;
int ptype;
#endif
if (!pdata) {
pr_err("%s: no LCDC platform data found\n", __func__);
@ -369,25 +425,61 @@ static int mdp_lcdc_probe(struct platform_device *pdev)
lcdc->frame_start_cb.func = lcdc_frame_start;
platform_set_drvdata(pdev, lcdc);
#ifdef CONFIG_MSM_MDP40
mdp_out_if_register(mdp_dev, MSM_LCDC_INTERFACE, lcdc, INTR_OVERLAY0_DONE,
lcdc_overlay_start);
#else
mdp_out_if_register(mdp_dev, MSM_LCDC_INTERFACE, lcdc, MDP_DMA_P_DONE,
lcdc_dma_start);
#endif
precompute_timing_parms(lcdc);
lcdc->fb_start = pdata->fb_resource->start;
lcdc->mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
if(lcdc->mdp->mdp_dev.color_format)
lcdc->color_format = lcdc->mdp->mdp_dev.color_format;
else
lcdc->color_format = MSM_MDP_OUT_IF_FMT_RGB565;
#ifdef CONFIG_MSM_MDP40
if (lcdc_pipe == NULL) {
ptype = mdp4_overlay_format2type(MDP_RGB_565);
pipe = mdp4_overlay_pipe_alloc(ptype);
pipe->mixer_stage = MDP4_MIXER_STAGE_BASE;
pipe->mixer_num = MDP4_MIXER0;
pipe->src_format = MDP_RGB_565;
mdp4_overlay_format2pipe(pipe);
pipe->mdp = lcdc->mdp;
lcdc_pipe = pipe; /* keep it */
} else {
pipe = lcdc_pipe;
}
pipe->src_height = pdata->fb_data->yres;
pipe->src_width = pdata->fb_data->xres;
pipe->src_h = pdata->fb_data->yres;
pipe->src_w = pdata->fb_data->xres;
pipe->src_y = 0;
pipe->src_x = 0;
pipe->srcp0_addr = (uint32_t) lcdc->fb_start;
pipe->srcp0_ystride = pdata->fb_data->xres * 2;
mdp4_overlay_dmap_xy(pipe);
mdp4_overlay_dmap_cfg(pipe, 1);
mdp4_overlay_rgb_setup(pipe);
mdp4_mixer_stage_up(pipe);
mdp4_overlayproc_cfg(pipe);
mdp4_overlay_reg_flush(pipe, 1);
#endif
lcdc->fb_panel_data.suspend = lcdc_suspend;
lcdc->fb_panel_data.resume = lcdc_resume;
lcdc->fb_panel_data.wait_vsync = lcdc_wait_vsync;
//#if defined(CONFIG_MACH_HTCLEO)
// Temporarily disable vsync to prevent a scheduler bug, need
// to be looked into further.
// lcdc->fb_panel_data.request_vsync = 0;
//#else
lcdc->fb_panel_data.request_vsync = lcdc_request_vsync;
//#endif
lcdc->fb_panel_data.clear_vsync = lcdc_clear_vsync;
lcdc->fb_panel_data.blank = lcdc_blank;
lcdc->fb_panel_data.unblank = lcdc_unblank;
@ -395,7 +487,6 @@ static int mdp_lcdc_probe(struct platform_device *pdev)
lcdc->fb_panel_data.interface_type = MSM_LCDC_INTERFACE;
ret = lcdc_hw_init(lcdc);
// ret = 0;
if (ret) {
pr_err("%s: Cannot initialize the mdp_lcdc\n", __func__);
goto err_hw_init;
@ -407,8 +498,6 @@ static int mdp_lcdc_probe(struct platform_device *pdev)
lcdc->fb_pdev.num_resources = 1;
lcdc->fb_pdev.dev.platform_data = &lcdc->fb_panel_data;
if (pdata->panel_ops->init)
pdata->panel_ops->init(pdata->panel_ops);
ret = platform_device_register(&lcdc->fb_pdev);
if (ret) {

View File

@ -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;
}

View File

@ -16,6 +16,7 @@
#define _VIDEO_MSM_MDP_PPP_H_
#include <linux/types.h>
#define PPP_DUMP_BLITS 0
struct ppp_regs {
uint32_t src0;
@ -68,11 +69,10 @@ int mdp_ppp_cfg_scale(const struct mdp_info *mdp, struct ppp_regs *regs,
struct mdp_rect *src_rect, struct mdp_rect *dst_rect,
uint32_t src_format, uint32_t dst_format);
int mdp_ppp_load_blur(const struct mdp_info *mdp);
void mdp_dump_blit(struct mdp_blit_req *req);
#ifndef CONFIG_MSM_MDP31
int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req, struct ppp_regs *regs);
#else
#if defined(CONFIG_MSM_MDP31) || defined(CONFIG_MSM_MDP302)
int mdp_ppp_blit_split_width(struct mdp_info *mdp, const struct mdp_blit_req *req,
struct file *src_file, unsigned long src_start, unsigned long src_len,
struct file *dst_file, unsigned long dst_start, unsigned long dst_len);
@ -80,6 +80,9 @@ int mdp_ppp_blit_split_height(struct mdp_info *mdp, const struct mdp_blit_req *r
struct file *src_file, unsigned long src_start, unsigned long src_len,
struct file *dst_file, unsigned long dst_start, unsigned long dst_len);
#if defined(CONFIG_MSM_MDP302)
int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req, struct ppp_regs *regs);
#else
static inline int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req,
struct ppp_regs *regs)
{
@ -87,6 +90,10 @@ static inline int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req,
}
#endif
#else
int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req, struct ppp_regs *regs);
#endif
int mdp_get_bytes_per_pixel(int format);
int mdp_blit_and_wait(struct mdp_info *mdp, struct mdp_blit_req *req,
struct file *src_file, unsigned long src_start, unsigned long src_len,

View File

@ -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;
}

View File

@ -32,6 +32,11 @@
#include <linux/clk.h>
#include <linux/debugfs.h>
#include <linux/dma-mapping.h>
#include <linux/android_pmem.h>
extern void start_drawing_late_resume(struct early_suspend *h);
static void msmfb_resume_handler(struct early_suspend *h);
static void msmfb_resume(struct work_struct *work);
#define MSMFB_DEBUG 1
#ifdef CONFIG_FB_MSM_LOGO
@ -56,13 +61,12 @@ extern int load_565rle_image(char *filename);
#define DLOG(mask, fmt, args...) \
do { \
if (msmfb_debug_mask & mask) \
if ((msmfb_debug_mask | SUSPEND_RESUME) & mask) \
printk(KERN_INFO "msmfb: "fmt, ##args); \
} while (0)
#define BITS_PER_PIXEL(info) (info->fb->var.bits_per_pixel)
#define BYTES_PER_PIXEL(info) (info->fb->var.bits_per_pixel >> 3)
static int msmfb_debug_mask;
module_param_named(msmfb_debug_mask, msmfb_debug_mask, int,
S_IRUGO | S_IWUSR | S_IWGRP);
@ -96,12 +100,59 @@ struct msmfb_info {
wait_queue_head_t frame_wq;
struct workqueue_struct *resume_workqueue;
struct work_struct resume_work;
struct work_struct msmfb_resume_work;
struct msmfb_callback dma_callback;
struct msmfb_callback vsync_callback;
struct hrtimer fake_vsync;
ktime_t vsync_request_time;
unsigned fb_resumed;
};
#ifdef CONFIG_FB_MSM_OVERLAY
#define USE_OVERLAY 1
struct overlay_waitevent{
uint32_t waked_up;
wait_queue_head_t event_wait;
};
static struct overlay_waitevent overlay_event;
DEFINE_MUTEX(overlay_event_lock);
#endif
#if (defined(CONFIG_USB_FUNCTION_PROJECTOR) || defined(CONFIG_USB_ANDROID_PROJECTOR))
static spinlock_t fb_data_lock = SPIN_LOCK_UNLOCKED;
static struct msm_fb_info msm_fb_data;
int msmfb_get_var(struct msm_fb_info *tmp)
{
unsigned long flags;
spin_lock_irqsave(&fb_data_lock, flags);
memcpy(tmp, &msm_fb_data, sizeof(msm_fb_data));
spin_unlock_irqrestore(&fb_data_lock, flags);
return 0;
}
/* projector need this, and very much */
int msmfb_get_fb_area(void)
{
int area;
unsigned long flags;
spin_lock_irqsave(&fb_data_lock, flags);
area = msm_fb_data.msmfb_area;
spin_unlock_irqrestore(&fb_data_lock, flags);
return area;
}
static void msmfb_set_var(unsigned char *addr, int area)
{
unsigned long flags;
spin_lock_irqsave(&fb_data_lock, flags);
msm_fb_data.fb_addr = addr;
msm_fb_data.msmfb_area = area;
spin_unlock_irqrestore(&fb_data_lock, flags);
}
#endif
static int msmfb_open(struct fb_info *info, int user)
{
return 0;
@ -248,6 +299,18 @@ static void msmfb_pan_update(struct fb_info *info, uint32_t left, uint32_t top,
DLOG(SHOW_UPDATES, "update %d %d %d %d %d %d\n",
left, top, eright, ebottom, yoffset, pan_display);
#if !defined(CONFIG_MACH_HTCLEO)
// For some reason we need to remove it here, state is 1, we have to look later to this problem
if (msmfb->sleeping != AWAKE)
DLOG(SUSPEND_RESUME, "pan_update in state(%d)\n", msmfb->sleeping);
#endif
#if (defined(CONFIG_USB_FUNCTION_PROJECTOR) || defined(CONFIG_USB_ANDROID_PROJECTOR))
/* Jay, 8/1/09' */
msmfb_set_var(msmfb->fb->screen_base, yoffset);
#endif
restart:
spin_lock_irqsave(&msmfb->update_lock, irq_flags);
@ -264,8 +327,8 @@ restart:
sleeping = msmfb->sleeping;
/* on a full update, if the last frame has not completed, wait for it */
if ((pan_display && msmfb->frame_requested != msmfb->frame_done) ||
sleeping == UPDATING) {
if (pan_display && (msmfb->frame_requested != msmfb->frame_done ||
sleeping == UPDATING)) {
int ret;
spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
ret = wait_event_interruptible_timeout(msmfb->frame_wq,
@ -365,7 +428,6 @@ static void power_on_panel(struct work_struct *work)
container_of(work, struct msmfb_info, resume_work);
struct msm_panel_data *panel = msmfb->panel;
unsigned long irq_flags;
mutex_lock(&msmfb->panel_init_lock);
DLOG(SUSPEND_RESUME, "turning on panel\n");
if (msmfb->sleeping == UPDATING) {
@ -385,6 +447,34 @@ error:
mutex_unlock(&msmfb->panel_init_lock);
}
static BLOCKING_NOTIFIER_HEAD(display_chain_head);
int register_display_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_register(&display_chain_head, nb);
}
static int display_notifier_callback(struct notifier_block *nfb,
unsigned long action,
void *ignored)
{
struct msmfb_info *msm_fb = (struct msmfb_info *)ignored;
switch (action) {
case NOTIFY_MSM_FB:
printk(KERN_DEBUG "NOTIFY_MSM_FB\n");
msmfb_resume(&msm_fb->early_suspend);
break;
case NOTIFY_POWER:
/* nothing to do */
break;
default:
printk(KERN_ERR "%s: unknown action in 0x%lx\n",
__func__, action);
return NOTIFY_BAD;
}
return NOTIFY_OK;
}
/* -------------------------------------------------------------------------- */
#ifdef CONFIG_HAS_EARLYSUSPEND
/* turn off the panel */
static void msmfb_earlier_suspend(struct early_suspend *h)
@ -401,12 +491,12 @@ static void msmfb_earlier_suspend(struct early_suspend *h)
spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
wait_event_timeout(msmfb->frame_wq,
msmfb->frame_requested == msmfb->frame_done, HZ/10);
#ifndef CONFIG_MSM_MDP40
mdp->dma(mdp, virt_to_phys(msmfb->black), 0,
msmfb->fb->var.xres, msmfb->fb->var.yres, 0, 0,
NULL, panel->interface_type);
mdp->dma_wait(mdp, panel->interface_type);
#endif
/* turn off the panel */
panel->blank(panel);
}
@ -417,14 +507,31 @@ static void msmfb_suspend(struct early_suspend *h)
early_suspend);
struct msm_panel_data *panel = msmfb->panel;
/* suspend the panel */
#ifdef CONFIG_FB_MSM_OVERLAY
/*check whether overlay done*/
wait_event_interruptible_timeout(
overlay_event.event_wait,
(overlay_event.waked_up == ~USE_OVERLAY),
10*HZ);
pr_info("wait event : %X\n", overlay_event.waked_up);
#endif
panel->suspend(panel);
msmfb->fb_resumed = 0;
mutex_unlock(&msmfb->panel_init_lock);
}
static void msmfb_resume(struct early_suspend *h)
static void msmfb_resume_handler(struct early_suspend *h)
{
struct msmfb_info *msmfb = container_of(h, struct msmfb_info,
early_suspend);
early_suspend);
queue_work(msmfb->resume_workqueue, &msmfb->msmfb_resume_work);
wait_event_interruptible_timeout(msmfb->frame_wq, msmfb->fb_resumed==1,HZ/2);
}
static void msmfb_resume(struct work_struct *work)
{
struct msmfb_info *msmfb =
container_of(work, struct msmfb_info, msmfb_resume_work);
struct msm_panel_data *panel = msmfb->panel;
unsigned long irq_flags;
@ -438,6 +545,9 @@ static void msmfb_resume(struct early_suspend *h)
msmfb->sleeping = WAKING;
DLOG(SUSPEND_RESUME, "ready, waiting for full update\n");
spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
start_drawing_late_resume(NULL);
msmfb->fb_resumed = 1;
wake_up(&msmfb->frame_wq);
}
#endif
@ -483,7 +593,6 @@ static int msmfb_set_par(struct fb_info *info)
return -1;
mdp->set_output_format(mdp, var->bits_per_pixel);
fix->line_length = var->xres * var->bits_per_pixel / 8;
return 0;
}
@ -502,16 +611,13 @@ int msmfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
var->reserved[1] >> 16, var->reserved[2] & 0xffff,
var->reserved[2] >> 16);
#endif
// printk("+PAN_DISP1\n");
msmfb_pan_update(info, var->reserved[1] & 0xffff,
var->reserved[1] >> 16,
var->reserved[2] & 0xffff,
var->reserved[2] >> 16, var->yoffset, 1);
// printk("-PAN_DISP1\n");
} else {
// printk("+PAN_DISP2\n");
msmfb_pan_update(info, 0, 0, info->var.xres, info->var.yres, var->yoffset, 1);
// printk("-PAN_DISP2\n");
msmfb_pan_update(info, 0, 0, info->var.xres, info->var.yres,
var->yoffset, 1);
}
return 0;
}
@ -566,6 +672,91 @@ static int msmfb_blit(struct fb_info *info,
}
return 0;
}
#ifdef CONFIG_FB_MSM_OVERLAY
static int msmfb_overlay_get(struct fb_info *info, void __user *p)
{
struct mdp_overlay req;
int ret;
if (copy_from_user(&req, p, sizeof(req)))
return -EFAULT;
ret = mdp->overlay_get(mdp, info, &req);
if (ret) {
printk(KERN_ERR "%s: ioctl failed \n",
__func__);
return ret;
}
if (copy_to_user(p, &req, sizeof(req))) {
printk(KERN_ERR "%s: copy2user failed \n",
__func__);
return -EFAULT;
}
return 0;
}
static int msmfb_overlay_set(struct fb_info *info, void __user *p)
{
struct mdp_overlay req;
int ret;
if (copy_from_user(&req, p, sizeof(req)))
return -EFAULT;
ret = mdp->overlay_set(mdp, info, &req);
if (ret) {
printk(KERN_ERR "%s:ioctl failed \n",
__func__);
return ret;
}
if (copy_to_user(p, &req, sizeof(req))) {
printk(KERN_ERR "%s: copy2user failed \n",
__func__);
return -EFAULT;
}
return 0;
}
static int msmfb_overlay_unset(struct fb_info *info, unsigned long *argp)
{
int ret, ndx;
ret = copy_from_user(&ndx, argp, sizeof(ndx));
if (ret) {
printk(KERN_ERR "%s:msmfb_overlay_unset ioctl failed \n",
__func__);
return ret;
}
return mdp->overlay_unset(mdp, info, ndx);
}
static int msmfb_overlay_play(struct fb_info *info, unsigned long *argp)
{
int ret;
struct msmfb_overlay_data req;
struct file *p_src_file = 0;
ret = copy_from_user(&req, argp, sizeof(req));
if (ret) {
printk(KERN_ERR "%s:msmfb_overlay_play ioctl failed \n",
__func__);
return ret;
}
ret = mdp->overlay_play(mdp, info, &req, &p_src_file);
if (p_src_file)
put_pmem_file(p_src_file);
return ret;
}
#endif
DEFINE_MUTEX(mdp_ppp_lock);
@ -580,14 +771,13 @@ static int msmfb_ioctl(struct fb_info *p, unsigned int cmd, unsigned long arg)
switch (cmd) {
case MSMFB_GRP_DISP:
printk("GRP_DISP\n");
mdp->set_grp_disp(mdp, arg);
break;
case MSMFB_BLIT:
#if PRINT_BLIT_TIME
t1 = ktime_get();
#endif
ret = msmfb_blit(p, argp);
ret = msmfb_blit(p, argp);
if (ret)
return ret;
#if PRINT_BLIT_TIME
@ -596,6 +786,38 @@ static int msmfb_ioctl(struct fb_info *p, unsigned int cmd, unsigned long arg)
ktime_to_ns(t2) - ktime_to_ns(t1));
#endif
break;
#ifdef CONFIG_FB_MSM_OVERLAY
case MSMFB_OVERLAY_GET:
printk("CONFIG_FB_MSM_OVERLAY\n");
//down(&mdp_ppp_lock);
ret = msmfb_overlay_get(p, argp);
//up(&mdp_ppp_lock);
break;
case MSMFB_OVERLAY_SET:
printk("MSMFB_OVERLAY_SET\n");
//down(&mdp_ppp_lock);
ret = msmfb_overlay_set(p, argp);
mutex_lock(&overlay_event_lock);
overlay_event.waked_up = USE_OVERLAY;
mutex_unlock(&overlay_event_lock);
//up(&mdp_ppp_lock);
break;
case MSMFB_OVERLAY_UNSET:
printk("MSMFB_OVERLAY_UNSET\n");
//down(&mdp_ppp_lock);
ret = msmfb_overlay_unset(p, argp);
mutex_lock(&overlay_event_lock);
overlay_event.waked_up = ~USE_OVERLAY;
wake_up(&overlay_event.event_wait);
mutex_unlock(&overlay_event_lock);
//up(&mdp_ppp_lock);
break;
case MSMFB_OVERLAY_PLAY:
//down(&mdp_ppp_lock);
ret = msmfb_overlay_play(p, argp);
//up(&mdp_ppp_lock);
break;
#endif
default:
printk(KERN_INFO "msmfb unknown ioctl: %d\n", cmd);
return -EINVAL;
@ -658,6 +880,8 @@ static struct file_operations debug_fops = {
};
#endif
#define BITS_PER_PIXEL 16
static void setup_fb_info(struct msmfb_info *msmfb)
{
struct fb_info *fb_info = msmfb->fb;
@ -673,13 +897,14 @@ static void setup_fb_info(struct msmfb_info *msmfb)
fb_info->fix.type = FB_TYPE_PACKED_PIXELS;
fb_info->fix.visual = FB_VISUAL_TRUECOLOR;
fb_info->fix.line_length = msmfb->xres * 2;
fb_info->var.xres = msmfb->xres;
fb_info->var.yres = msmfb->yres;
fb_info->var.width = msmfb->panel->fb_data->width;
fb_info->var.height = msmfb->panel->fb_data->height;
fb_info->var.xres_virtual = msmfb->xres;
fb_info->var.yres_virtual = msmfb->yres * 2;
fb_info->var.bits_per_pixel = 16;
fb_info->var.bits_per_pixel = BITS_PER_PIXEL;
fb_info->var.accel_flags = 0;
fb_info->var.yoffset = 0;
@ -688,8 +913,11 @@ static void setup_fb_info(struct msmfb_info *msmfb)
/* set the param in the fixed screen, so userspace can't
* change it. This will be used to check for the
* capability. */
/* FIX ME: every panel support partial update?
fb_info->fix.reserved[0] = 0x5444;
fb_info->fix.reserved[1] = 0x5055;
*/
/* This preloads the value so that if userspace doesn't
* change it, it will be a full update */
@ -717,6 +945,14 @@ static void setup_fb_info(struct msmfb_info *msmfb)
PP[0] = 0;
for (r = 1; r < 16; r++)
PP[r] = 0xffffffff;
/* Jay add, 7/1/09' */
#if (defined(CONFIG_USB_FUNCTION_PROJECTOR) || defined(CONFIG_USB_ANDROID_PROJECTOR))
msm_fb_data.xres = msmfb->xres;
msm_fb_data.yres = msmfb->yres;
printk(KERN_INFO "setup_fb_info msmfb->xres %d, msmfb->yres %d\n",
msmfb->xres,msmfb->yres);
#endif
}
static int setup_fbmem(struct msmfb_info *msmfb, struct platform_device *pdev)
@ -781,12 +1017,17 @@ static int msmfb_probe(struct platform_device *pdev)
if (ret)
goto error_setup_fbmem;
#if (defined(CONFIG_USB_FUNCTION_PROJECTOR) || defined(CONFIG_USB_ANDROID_PROJECTOR))
/* Jay, 8/1/09' */
msmfb_set_var(msmfb->fb->screen_base, 0);
#endif
setup_fb_info(msmfb);
spin_lock_init(&msmfb->update_lock);
mutex_init(&msmfb->panel_init_lock);
init_waitqueue_head(&msmfb->frame_wq);
msmfb->resume_workqueue = create_workqueue("panel_on");
msmfb->resume_workqueue = create_rt_workqueue("panel_on");
if (msmfb->resume_workqueue == NULL) {
printk(KERN_ERR "failed to create panel_on workqueue\n");
ret = -ENOMEM;
@ -799,8 +1040,9 @@ static int msmfb_probe(struct platform_device *pdev)
wake_lock_init(&msmfb->idle_lock, WAKE_LOCK_IDLE, "msmfb_idle_lock");
#ifdef CONFIG_HAS_EARLYSUSPEND
INIT_WORK(&msmfb->msmfb_resume_work, msmfb_resume);
msmfb->early_suspend.suspend = msmfb_suspend;
msmfb->early_suspend.resume = msmfb_resume;
msmfb->early_suspend.resume = msmfb_resume_handler;
msmfb->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
register_early_suspend(&msmfb->early_suspend);
@ -822,7 +1064,6 @@ static int msmfb_probe(struct platform_device *pdev)
hrtimer_init(&msmfb->fake_vsync, CLOCK_MONOTONIC,
HRTIMER_MODE_REL);
msmfb->fake_vsync.function = msmfb_fake_vsync;
ret = register_framebuffer(fb);
@ -831,6 +1072,13 @@ static int msmfb_probe(struct platform_device *pdev)
msmfb->sleeping = WAKING;
#ifdef CONFIG_FB_MSM_OVERLAY
/*init wait event*/
init_waitqueue_head(&overlay_event.event_wait);
/*init waked_up value*/
overlay_event.waked_up = ~USE_OVERLAY;
#endif
#ifdef CONFIG_FB_MSM_LOGO
if (!load_565rle_image(INIT_IMAGE_FILE)) {
/* Flip buffer */
@ -842,6 +1090,8 @@ static int msmfb_probe(struct platform_device *pdev)
msmfb->yres, 0, 1);
}
#endif
/* Jay, 29/12/08' */
display_notifier(display_notifier_callback, NOTIFY_MSM_FB);
return 0;
error_register_framebuffer:
@ -854,9 +1104,35 @@ error_setup_fbmem:
return ret;
}
static void msmfb_shutdown(struct platform_device *pdev)
{
struct msm_panel_data *panel = pdev->dev.platform_data;
struct fb_info *fb;
struct msmfb_info *msmfb;
printk(KERN_INFO "%s\n", __func__);
fb = registered_fb[0];
if (!fb) {
printk(KERN_ERR "fb0 unavailable.\n");
return;
}
msmfb = fb->par;
mdp->dma(mdp, virt_to_phys(msmfb->black), 0,
msmfb->fb->var.xres, msmfb->fb->var.yres, 0, 0,
NULL, panel->interface_type);
if (panel->blank)
panel->blank(panel);
if (panel->shutdown)
panel->shutdown(panel);
}
static struct platform_driver msm_panel_driver = {
/* need to write remove */
.probe = msmfb_probe,
.shutdown = msmfb_shutdown,
.driver = {.name = "msm_panel"},
};