Revert "Added video driver cleanup and tweaks by gauner1986. Thanks to huanyu for isolating them."
This reverts commit 935a9ce79780605671c8a80e65a9a0dd3746081a.
This commit is contained in:
parent
94b9f0bea1
commit
3db92cb1bf
@ -7,14 +7,29 @@ config FB_MSM
|
||||
default y
|
||||
|
||||
config FB_MSM_LCDC
|
||||
bool "Support for integrated LCD controller in qsd8x50"
|
||||
depends on FB_MSM && MSM_MDP31
|
||||
bool "Support for integrated LCD controller in qsd8x50 and MSM7x27"
|
||||
depends on FB_MSM && (MSM_MDP31 || MSM_MDP302)
|
||||
default y
|
||||
|
||||
config FB_MSM_TVOUT
|
||||
bool "Support for TV-Out in qsd8x50"
|
||||
depends on FB_MSM && MSM_MDP31
|
||||
default n
|
||||
|
||||
config FB_MSM_OVERLAY
|
||||
bool "Support for overlay in MSM7X30"
|
||||
depends on FB_MSM && MSM_MDP40
|
||||
default y
|
||||
|
||||
config FB_MSM_DTV
|
||||
depends on FB_MSM_OVERLAY
|
||||
bool
|
||||
default n
|
||||
|
||||
config GPU_MSM_KGSL
|
||||
tristate "MSM 3D Graphics driver for QSD8x50 and MSM7x27"
|
||||
default n
|
||||
depends on FB_MSM && ARCH_QSD8X50
|
||||
depends on FB_MSM && (ARCH_QSD8X50 || ARCH_MSM7227 || ARCH_MSM7X30)
|
||||
select GENERIC_ALLOCATOR
|
||||
select CONFIG_FW_LOADER
|
||||
help
|
||||
@ -22,6 +37,24 @@ config GPU_MSM_KGSL
|
||||
use hardware accelerated OpenGL ES 2.0 and 1.1 on these
|
||||
chips.
|
||||
|
||||
config MSM_ROTATOR
|
||||
tristate "MSM Offline Image Rotator Driver"
|
||||
depends on ARCH_MSM7X30 && ANDROID_PMEM
|
||||
default y
|
||||
help
|
||||
This driver provides support for the image rotator HW block in the
|
||||
MSM 7x30 SoC.
|
||||
|
||||
config MSM_ROTATOR_USE_IMEM
|
||||
bool "Enable rotator driver to use iMem"
|
||||
depends on MSM_ROTATOR
|
||||
default y
|
||||
help
|
||||
This option enables the msm_rotator driver to use the move efficient
|
||||
iMem. Some MSM platforms may not have iMem available for the rotator
|
||||
block. Or some systems may want the iMem to be dedicated to a
|
||||
different function.
|
||||
|
||||
config MSM_KGSL_MMU
|
||||
bool "Turn on MMU for graphics driver "
|
||||
depends on GPU_MSM_KGSL && MMU
|
||||
@ -30,6 +63,19 @@ config MSM_KGSL_MMU
|
||||
If enabled, the GPU driver will allocate memory from vmalloc
|
||||
and enable the use of GPU MMU, instead of using pmem.
|
||||
|
||||
config MSM_KGSL_PER_FD_PAGETABLE
|
||||
bool "Turn on per-fd pagetable for MMU of graphics driver "
|
||||
depends on MSM_KGSL_MMU && MMU
|
||||
default n
|
||||
help
|
||||
If enabled, the MMU unit of GPU driver will use seperate
|
||||
pagetables for each file descriptor
|
||||
|
||||
config MSM_HDMI
|
||||
bool "Support for HDMI in QCT platform"
|
||||
depends on MSM_MDP31
|
||||
default n
|
||||
|
||||
config FB_MSM_LOGO
|
||||
bool "Use boot splashscreen"
|
||||
depends on FB_MSM
|
||||
|
@ -1,7 +1,7 @@
|
||||
|
||||
# core framebuffer
|
||||
#
|
||||
obj-y := msm_fb.o
|
||||
obj-y := msm_fb.o logo.o
|
||||
ifeq ($(CONFIG_FB_MSM_LOGO),y)
|
||||
obj-y += logo.o
|
||||
endif
|
||||
@ -9,9 +9,14 @@ endif
|
||||
# MDP DMA/PPP engine
|
||||
#
|
||||
obj-y += mdp.o mdp_ppp.o
|
||||
|
||||
obj-$(CONFIG_FB_MSM_OVERLAY) += mdp4_util.o
|
||||
obj-$(CONFIG_FB_MSM_OVERLAY) += mdp4_overlay.o
|
||||
obj-$(CONFIG_FB_MSM_OVERLAY) += mdp4_overlay_mddi.o
|
||||
obj-$(CONFIG_MSM_MDP40) += msm_rotator.o
|
||||
|
||||
obj-$(CONFIG_MSM_MDP22) += mdp_ppp22.o
|
||||
obj-$(CONFIG_MSM_MDP30) += mdp_ppp22.o
|
||||
obj-$(CONFIG_MSM_MDP302)+= mdp_ppp22.o
|
||||
obj-$(CONFIG_MSM_MDP31) += mdp_ppp31.o
|
||||
|
||||
# MDDI interface
|
||||
@ -21,11 +26,18 @@ obj-y += mddi.o
|
||||
# MDDI client/panel drivers
|
||||
#
|
||||
obj-y += mddi_client_dummy.o
|
||||
obj-y += mddi_client_toshiba.o
|
||||
obj-y += mddi_client_nt35399.o
|
||||
obj-y += mddi_client_epson.o
|
||||
obj-y += mddi_client_novb9f6_5582.o
|
||||
|
||||
# MDP LCD controller driver
|
||||
obj-$(CONFIG_FB_MSM_LCDC) += mdp_lcdc.o
|
||||
obj-$(CONFIG_FB_MSM_TVOUT) += tvenc.o tvfb.o
|
||||
|
||||
# Yamato GL driver
|
||||
ifeq ($(CONFIG_ARCH_MSM7X30),y)
|
||||
obj-$(CONFIG_GPU_MSM_KGSL) += gpu/kgsl_adreno205/
|
||||
else
|
||||
obj-$(CONFIG_GPU_MSM_KGSL) += gpu/kgsl/
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_MSM_HDMI) += hdmi/
|
||||
|
@ -119,10 +119,16 @@ static void kgsl_clk_enable(void)
|
||||
clk_set_rate(kgsl_driver.ebi1_clk, 128000000);
|
||||
clk_enable(kgsl_driver.imem_clk);
|
||||
clk_enable(kgsl_driver.grp_clk);
|
||||
#ifdef CONFIG_ARCH_MSM7227
|
||||
clk_enable(kgsl_driver.grp_pclk);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void kgsl_clk_disable(void)
|
||||
{
|
||||
#ifdef CONFIG_ARCH_MSM7227
|
||||
clk_disable(kgsl_driver.grp_pclk);
|
||||
#endif
|
||||
clk_disable(kgsl_driver.grp_clk);
|
||||
clk_disable(kgsl_driver.imem_clk);
|
||||
clk_set_rate(kgsl_driver.ebi1_clk, 0);
|
||||
@ -163,7 +169,7 @@ static void kgsl_hw_put_locked(bool start_timer)
|
||||
{
|
||||
if ((--kgsl_driver.active_cnt == 0) && start_timer) {
|
||||
mod_timer(&kgsl_driver.standby_timer,
|
||||
jiffies + msecs_to_jiffies(20));
|
||||
jiffies + msecs_to_jiffies(512));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1031,10 +1037,52 @@ static long kgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
|
||||
return result;
|
||||
}
|
||||
|
||||
static int kgsl_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
int result;
|
||||
struct kgsl_memdesc *memdesc = NULL;
|
||||
unsigned long vma_size = vma->vm_end - vma->vm_start;
|
||||
unsigned long vma_offset = vma->vm_pgoff << PAGE_SHIFT;
|
||||
struct kgsl_device *device = NULL;
|
||||
|
||||
mutex_lock(&kgsl_driver.mutex);
|
||||
|
||||
device = &kgsl_driver.yamato_device;
|
||||
|
||||
/*allow yamato memstore to be mapped read only */
|
||||
if (vma_offset == device->memstore.physaddr) {
|
||||
if (vma->vm_flags & VM_WRITE) {
|
||||
result = -EPERM;
|
||||
goto done;
|
||||
}
|
||||
memdesc = &device->memstore;
|
||||
}
|
||||
|
||||
if (memdesc->size != vma_size) {
|
||||
KGSL_MEM_ERR("file %p bad size %ld, should be %d\n",
|
||||
file, vma_size, memdesc->size);
|
||||
result = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
|
||||
|
||||
result = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
|
||||
vma_size, vma->vm_page_prot);
|
||||
if (result != 0) {
|
||||
KGSL_MEM_ERR("remap_pfn_range returned %d\n",
|
||||
result);
|
||||
goto done;
|
||||
}
|
||||
done:
|
||||
mutex_unlock(&kgsl_driver.mutex);
|
||||
return result;
|
||||
}
|
||||
|
||||
static struct file_operations kgsl_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.release = kgsl_release,
|
||||
.open = kgsl_open,
|
||||
.mmap = kgsl_mmap,
|
||||
.unlocked_ioctl = kgsl_ioctl,
|
||||
};
|
||||
|
||||
@ -1096,6 +1144,9 @@ static int __devinit kgsl_platform_probe(struct platform_device *pdev)
|
||||
BUG_ON(kgsl_driver.grp_clk != NULL);
|
||||
BUG_ON(kgsl_driver.imem_clk != NULL);
|
||||
BUG_ON(kgsl_driver.ebi1_clk != NULL);
|
||||
#ifdef CONFIG_ARCH_MSM7227
|
||||
BUG_ON(kgsl_driver.grp_pclk != NULL);
|
||||
#endif
|
||||
|
||||
kgsl_driver.pdev = pdev;
|
||||
|
||||
@ -1126,6 +1177,15 @@ static int __devinit kgsl_platform_probe(struct platform_device *pdev)
|
||||
}
|
||||
kgsl_driver.ebi1_clk = clk;
|
||||
|
||||
#ifdef CONFIG_ARCH_MSM7227
|
||||
clk = clk_get(&pdev->dev, "grp_pclk");
|
||||
if (IS_ERR(clk)) {
|
||||
result = PTR_ERR(clk);
|
||||
KGSL_DRV_ERR("clk_get(grp_pclk) returned %d\n", result);
|
||||
goto done;
|
||||
}
|
||||
kgsl_driver.grp_pclk = clk;
|
||||
#endif
|
||||
/*acquire interrupt */
|
||||
kgsl_driver.interrupt_num = platform_get_irq(pdev, 0);
|
||||
if (kgsl_driver.interrupt_num <= 0) {
|
||||
|
@ -47,7 +47,9 @@ struct kgsl_driver {
|
||||
struct clk *grp_clk;
|
||||
struct clk *imem_clk;
|
||||
struct clk *ebi1_clk;
|
||||
|
||||
#ifdef CONFIG_ARCH_MSM7227
|
||||
struct clk *grp_pclk;
|
||||
#endif
|
||||
struct kgsl_devconfig yamato_config;
|
||||
|
||||
uint32_t flags_debug;
|
||||
|
@ -464,6 +464,9 @@ int kgsl_mmu_map(struct kgsl_pagetable *pagetable,
|
||||
KGSL_MEM_VDBG("enter (pt=%p, physaddr=%08x, range=%08d, gpuaddr=%p)\n",
|
||||
pagetable, address, range, gpuaddr);
|
||||
|
||||
#ifdef CONFIG_CACHE_L2X0
|
||||
l2x0_cache_flush_all();
|
||||
#endif
|
||||
mmu = pagetable->mmu;
|
||||
|
||||
BUG_ON(mmu == NULL);
|
||||
|
@ -63,7 +63,7 @@ inline unsigned int kgsl_ringbuffer_sizelog2quadwords(unsigned int sizedwords)
|
||||
return sizelog2quadwords;
|
||||
}
|
||||
|
||||
|
||||
#if defined(CONFIG_MACH_HTCLEO)
|
||||
// +Cotulla hack
|
||||
#ifndef ARRAYSIZE
|
||||
#define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0]))
|
||||
@ -727,6 +727,7 @@ unsigned char fw_yamato_pm4[9220] =
|
||||
0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
// -Cotulla hack
|
||||
#endif
|
||||
|
||||
/* functions */
|
||||
void kgsl_cp_intrcallback(struct kgsl_device *device)
|
||||
@ -937,13 +938,12 @@ static int kgsl_ringbuffer_load_pm4_ucode(struct kgsl_device *device)
|
||||
{
|
||||
int status = 0;
|
||||
int i;
|
||||
// const struct firmware *fw = NULL;
|
||||
unsigned int *fw_ptr = NULL;
|
||||
size_t fw_word_size = 0;
|
||||
struct firmware foo_fw;
|
||||
struct firmware *fw = &foo_fw;
|
||||
|
||||
#if !defined(CONFIG_MACH_HTCLEO)
|
||||
const struct firmware *fw = NULL;
|
||||
|
||||
/*
|
||||
status = request_firmware(&fw, YAMATO_PM4_FW,
|
||||
kgsl_driver.misc.this_device);
|
||||
if (status != 0) {
|
||||
@ -951,10 +951,13 @@ static int kgsl_ringbuffer_load_pm4_ucode(struct kgsl_device *device)
|
||||
YAMATO_PM4_FW, status);
|
||||
goto done;
|
||||
}
|
||||
*/
|
||||
#else
|
||||
struct firmware foo_fw;
|
||||
struct firmware *fw = &foo_fw;
|
||||
|
||||
fw->data = fw_yamato_pm4;
|
||||
fw->size = ARRAYSIZE(fw_yamato_pm4);
|
||||
|
||||
#endif
|
||||
/*this firmware must come in 3 word chunks. plus 1 word of version*/
|
||||
if ((fw->size % (sizeof(uint32_t)*3)) != 4) {
|
||||
KGSL_DRV_ERR("bad firmware size %d.\n", fw->size);
|
||||
@ -971,32 +974,38 @@ static int kgsl_ringbuffer_load_pm4_ucode(struct kgsl_device *device)
|
||||
kgsl_yamato_regwrite(device, REG_CP_ME_RAM_DATA, fw_ptr[i]);
|
||||
|
||||
done:
|
||||
// release_firmware(fw);
|
||||
// return status;
|
||||
#if !defined(CONFIG_MACH_HTCLEO)
|
||||
release_firmware(fw);
|
||||
return status;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
static int kgsl_ringbuffer_load_pfp_ucode(struct kgsl_device *device)
|
||||
{
|
||||
int status = 0;
|
||||
int i;
|
||||
//const struct firmware *fw = NULL;
|
||||
unsigned int *fw_ptr = NULL;
|
||||
size_t fw_word_size = 0;
|
||||
struct firmware foo_fw;
|
||||
struct firmware *fw = &foo_fw;
|
||||
#if !defined(CONFIG_MACH_HTCLEO)
|
||||
const struct firmware *fw = NULL;
|
||||
|
||||
/* status = request_firmware(&fw, YAMATO_PFP_FW,
|
||||
status = request_firmware(&fw, YAMATO_PFP_FW,
|
||||
kgsl_driver.misc.this_device);
|
||||
if (status != 0) {
|
||||
KGSL_DRV_ERR("request_firmware for %s failed with error %d\n",
|
||||
YAMATO_PFP_FW, status);
|
||||
return status;
|
||||
}
|
||||
*/
|
||||
#else
|
||||
struct firmware foo_fw;
|
||||
struct firmware *fw = &foo_fw;
|
||||
|
||||
fw->data = fw_yamato_pfp;
|
||||
fw->size = ARRAYSIZE(fw_yamato_pfp);
|
||||
|
||||
#endif
|
||||
|
||||
/*this firmware must come in 1 word chunks. */
|
||||
if ((fw->size % sizeof(uint32_t)) != 0) {
|
||||
@ -1013,9 +1022,12 @@ static int kgsl_ringbuffer_load_pfp_ucode(struct kgsl_device *device)
|
||||
for (i = 1; i < fw_word_size; i++)
|
||||
kgsl_yamato_regwrite(device, REG_CP_PFP_UCODE_DATA, fw_ptr[i]);
|
||||
|
||||
// release_firmware(fw);
|
||||
// return status;
|
||||
#if !defined(CONFIG_MACH_HTCLEO)
|
||||
release_firmware(fw);
|
||||
return status;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int kgsl_ringbuffer_start(struct kgsl_ringbuffer *rb)
|
||||
|
9
drivers/video/msm/hdmi/Makefile
Normal file
9
drivers/video/msm/hdmi/Makefile
Normal file
@ -0,0 +1,9 @@
|
||||
msm_hdmi-objs = \
|
||||
transmitter.o \
|
||||
hdmi_lcdc.o \
|
||||
fb-hdmi.o \
|
||||
edid.o
|
||||
|
||||
obj-$(CONFIG_MSM_HDMI) += msm_hdmi.o
|
||||
|
||||
obj-$(CONFIG_MSM_HDMI) += silicon-image/
|
695
drivers/video/msm/hdmi/edid.c
Normal file
695
drivers/video/msm/hdmi/edid.c
Normal file
@ -0,0 +1,695 @@
|
||||
/*
|
||||
* Copyright (C) 2009 HTC
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* Common function for accessing/debugging EDID data.
|
||||
* Reference:
|
||||
http://en.wikipedia.org/wiki/Extended_display_identification_data
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/htc_hdmi.h>
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#include "include/fb-hdmi.h"
|
||||
#include "include/sil902x.h"
|
||||
|
||||
#if 1
|
||||
#define EDID_DBG(s...) printk("[hdmi/edid]" s)
|
||||
#else
|
||||
#define EDID_DBG(s...) do {} while (0)
|
||||
#endif
|
||||
|
||||
static struct video_mode established_timing_db[] = {
|
||||
{800, 600, 60, ASPECT(4, 3), PROGRESSIVE, false, "800x600 @ 60 Hz"},
|
||||
{800, 600, 56, ASPECT(4, 3), PROGRESSIVE, false, "800x600 @ 56 Hz"},
|
||||
{640, 480, 75, ASPECT(4, 3), PROGRESSIVE, false, "800x600 @ 75 Hz"},
|
||||
{640, 480, 72, ASPECT(4, 3), PROGRESSIVE, false, "640x480 @ 72 Hz"},
|
||||
{640, 480, 67, ASPECT(4, 3), PROGRESSIVE, false, "640x480 @ 67 Hz"},
|
||||
{640, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, "640x480 @ 60 Hz"},
|
||||
{720, 400, 88, ASPECT(4, 3), PROGRESSIVE, false, "720x400 @ 88 Hz"},
|
||||
{720, 400, 70, ASPECT(4, 3), PROGRESSIVE, false, "720x400 @ 70 Hz"},
|
||||
|
||||
{1280, 1024, 75, ASPECT(4, 3), PROGRESSIVE, false, "1280x1024@75 Hz"},
|
||||
{1024, 768, 75, ASPECT(4, 3), PROGRESSIVE, false, "1024x768@75 Hz"},
|
||||
{1024, 768, 70, ASPECT(4, 3), PROGRESSIVE, false, "1024x768@70 Hz"},
|
||||
{1024, 768, 60, ASPECT(4, 3), PROGRESSIVE, false, "1024x768@60 Hz"},
|
||||
{1024, 768, 87, ASPECT(4, 3), INTERLACE, false,
|
||||
"1024x768@87 Hz (Interlaced)"},
|
||||
{832, 624, 75, ASPECT(4, 3), PROGRESSIVE, false, "832x624@75 Hz"},
|
||||
{800, 600, 75, ASPECT(4, 3), PROGRESSIVE, false, "800x600@75 Hz"},
|
||||
{800, 600, 72, ASPECT(4, 3), PROGRESSIVE, false, "800x600@72 Hz"},
|
||||
|
||||
{1152, 870, 75, ASPECT(4, 3), PROGRESSIVE, false, "1152x870 @ 75 Hz"},
|
||||
};
|
||||
|
||||
static struct video_mode standard_timing_db[8];
|
||||
|
||||
static struct video_mode additional_timing_db[] = {
|
||||
{640, 480, 60, ASPECT(4, 3), PROGRESSIVE, false,
|
||||
" 1 DMT0659 4:3 640x480p @ 59.94/60Hz"},
|
||||
{720, 480, 60, ASPECT(4, 3), PROGRESSIVE, false,
|
||||
" 2 480p 4:3 720x480p @ 59.94/60Hz"},
|
||||
{720, 480, 60, ASPECT(16, 9), PROGRESSIVE, false,
|
||||
" 3 480pH 16:9 720x480p @ 59.94/60Hz"},
|
||||
{1280, 720, 60, ASPECT(16, 9), PROGRESSIVE, false,
|
||||
" 4 720p 16:9 1280x720p @ 59.94/60Hz"},
|
||||
{1920, 1080, 60, ASPECT(4, 3), INTERLACE, false,
|
||||
" 5 1080i 16:9 1920x1080i @ 59.94/60Hz"},
|
||||
{720, 480, 60, ASPECT(4, 3), INTERLACE, false,
|
||||
" 6 480i 4:3 720(1440)x480i @ 59.94/60Hz"},
|
||||
{720, 480, 60, ASPECT(16, 9), INTERLACE, false,
|
||||
" 7 480iH 16:9 720(1440)x480i @ 59.94/60Hz"},
|
||||
{720, 240, 60, ASPECT(4, 3), PROGRESSIVE, false,
|
||||
" 8 240p 4:3 720(1440)x240p @ 59.94/60Hz"},
|
||||
{720, 480, 60, ASPECT(16, 9), PROGRESSIVE, false,
|
||||
" 9 240pH 16:9 720(1440)x240p @ 59.94/60Hz"},
|
||||
{2880, 480, 60, ASPECT(4, 3), INTERLACE, false,
|
||||
"10 480i4x 4:3 (2880)x480i @ 59.94/60Hz"},
|
||||
{2880, 480, 60, ASPECT(16, 9), INTERLACE, false,
|
||||
"11 480i4xH 16:9 (2880)x480i @ 59.94/60Hz"},
|
||||
{2880, 240, 60, ASPECT(4, 3), PROGRESSIVE, false,
|
||||
"12 240p4x 4:3 (2880)x240p @ 59.94/60Hz"},
|
||||
{2880, 240, 60, ASPECT(16, 9), PROGRESSIVE, false,
|
||||
"13 240p4xH 16:9 (2880)x240p @ 59.94/60Hz"},
|
||||
{1440, 480, 60, ASPECT(4, 3), PROGRESSIVE, false,
|
||||
"14 480p2x 4:3 1440x480p @ 59.94/60Hz"},
|
||||
{1440, 480, 60, ASPECT(16, 9), PROGRESSIVE, false,
|
||||
"15 480p2xH 16:9 1440x480p @ 59.94/60Hz"},
|
||||
{1920, 1080, 60, ASPECT(16, 9), PROGRESSIVE, false,
|
||||
"16 1080p 16:9 1920x1080p @ 59.94/60Hz"},
|
||||
{720, 576, 50, ASPECT(4, 3), PROGRESSIVE, false,
|
||||
"17 576p 4:3 720x576p @ 50Hz"},
|
||||
{720, 576, 50, ASPECT(16, 9), PROGRESSIVE, false,
|
||||
"18 576pH 16:9 720x576p @ 50Hz"},
|
||||
{1280, 720, 50, ASPECT(16, 9), PROGRESSIVE, false,
|
||||
"19 720p50 16:9 1280x720p @ 50Hz"},
|
||||
{1920, 1080, 50, ASPECT(16, 9), INTERLACE, false,
|
||||
"20 1080i25 16:9 1920x1080i @ 50Hz*"},
|
||||
{1440, 576, 50, ASPECT(4, 3), INTERLACE, false,
|
||||
"21 576i 4:3 720(1440)x576i @ 50Hz"},
|
||||
{1440, 576, 50, ASPECT(4, 3), PROGRESSIVE, false,
|
||||
"22 576iH 16:9 720(1440)x576i @ 50Hz"},
|
||||
{720, 288, 50, ASPECT(4, 3), PROGRESSIVE, false,
|
||||
"23 288p 4:3 720(1440)x288p @ 50Hz"},
|
||||
{720, 288, 50, ASPECT(16, 9), PROGRESSIVE, false,
|
||||
"24 288pH 16:9 720(1440)x288p @ 50Hz"},
|
||||
{2880, 576, 50, ASPECT(4, 3), INTERLACE, false,
|
||||
"25 576i4x 4:3 (2880)x576i @ 50Hz"},
|
||||
{2880, 576, 50, ASPECT(16, 9), INTERLACE, false,
|
||||
"26 576i4xH 16:9 (2880)x576i @ 50Hz"},
|
||||
{2880, 288, 50, ASPECT(4, 3), PROGRESSIVE, false,
|
||||
"27 288p4x 4:3 (2880)x288p @ 50Hz"},
|
||||
{2880, 288, 50, ASPECT(16, 9), PROGRESSIVE, false,
|
||||
"28 288p4xH 16:9 (2880)x288p @ 50Hz"},
|
||||
{1440, 576, 50, ASPECT(4, 3), PROGRESSIVE, false,
|
||||
"29 576p2x 4:3 1440x576p @ 50Hz"},
|
||||
{1440, 576, 50, ASPECT(16, 9), PROGRESSIVE, false,
|
||||
"30 576p2xH 16:9 1440x576p @ 50Hz"},
|
||||
{1920, 1080, 50, ASPECT(16, 9), PROGRESSIVE, false,
|
||||
"31 1080p50 16:9 1920x1080p @ 50Hz"},
|
||||
{1920, 1080, 24, ASPECT(16, 9), PROGRESSIVE, false,
|
||||
"32 1080p24 16:9 1920x1080p @ 23.98/24Hz"},
|
||||
{1920, 1080, 25, ASPECT(16, 9), PROGRESSIVE, false,
|
||||
"33 1080p25 16:9 1920x1080p @ 25Hz"},
|
||||
{1920, 1080, 30, ASPECT(16, 9), PROGRESSIVE, false,
|
||||
"34 1080p30 16:9 1920x1080p @ 29.97/30Hz"},
|
||||
{2880, 480, 60, ASPECT(4, 3), PROGRESSIVE, false,
|
||||
"35 480p4x 4:3 (2880)x480p @ 59.94/60Hz"},
|
||||
{2880, 480, 60, ASPECT(16, 9), PROGRESSIVE, false,
|
||||
"36 480p4xH 16:9 (2880)x480p @ 59.94/60Hz"},
|
||||
{2880, 576, 50, ASPECT(4, 3), PROGRESSIVE, false,
|
||||
"37 576p4x 4:3 (2880)x576p @ 50Hz"},
|
||||
{2880, 576, 50, ASPECT(16, 9), PROGRESSIVE, false,
|
||||
"38 576p4xH 16:9 (2880)x576p @ 50Hz"},
|
||||
{1920, 1080, 50, ASPECT(16, 9), INTERLACE, false,
|
||||
"39 108Oi25 16:9 1920x1080i(1250 Total) @ 50Hz*"},
|
||||
{1920, 1080, 100, ASPECT(16, 9), INTERLACE, false,
|
||||
"40 1080i50 16:9 1920x1080i @ 100Hz"},
|
||||
{1280, 720, 100, ASPECT(16, 9), PROGRESSIVE, false,
|
||||
"41 720p100 16:9 1280x720p @ 100Hz"},
|
||||
{720, 576, 100, ASPECT(4, 3), PROGRESSIVE, false,
|
||||
"42 576p100 4:3 720x576p @ 100Hz"},
|
||||
{720, 576, 100, ASPECT(16, 9), PROGRESSIVE, false,
|
||||
"43 576p100H 16:9 720x576p @ 100Hz"},
|
||||
{720, 576, 100, ASPECT(4, 3), INTERLACE, false,
|
||||
"44 576i50 4:3 720(1440)x576i @ 100Hz"},
|
||||
{720, 576, 100, ASPECT(16, 9), INTERLACE, false,
|
||||
"45 576i50H 16:9 720(1440)x576i @ 100Hz"},
|
||||
{1920, 1080, 120, ASPECT(16, 9), INTERLACE, false,
|
||||
"46 1080i60 16:9 1920x1080i @ 119.88/120Hz"},
|
||||
{1280, 720, 120, ASPECT(16, 9), PROGRESSIVE, false,
|
||||
"47 720p120 16:9 1280x720p @ 119.88/120Hz"},
|
||||
{720, 480, 120, ASPECT(4, 3), PROGRESSIVE, false,
|
||||
"48 480p119 4:3 720x480p @ 119.88/120Hz"},
|
||||
{720, 480, 120, ASPECT(16, 9), PROGRESSIVE, false,
|
||||
"49 480p119H 16:9 720x480p @ 119.88/120Hz"},
|
||||
{720, 480, 120, ASPECT(4, 3), INTERLACE, false,
|
||||
"50 480i59 4:3 720(1440)x480i @ 119.88/120Hz"},
|
||||
{720, 480, 120, ASPECT(16, 9), INTERLACE, false,
|
||||
"51 480i59H 16:9 720(1440)x480i @ 119.88/120Hz"},
|
||||
{720, 576, 200, ASPECT(4, 3), PROGRESSIVE, false,
|
||||
"52 576p200 4:3 720x576p @ 200Hz"},
|
||||
{720, 576, 200, ASPECT(16, 9), PROGRESSIVE, false,
|
||||
"53 576p200H 16:9 720x576p @ 200Hz"},
|
||||
{720, 576, 200, ASPECT(4, 3), INTERLACE, false,
|
||||
"54 576i100 4:3 720(1440)x576i @ 200Hz"},
|
||||
{720, 576, 200, ASPECT(16, 9), INTERLACE, false,
|
||||
"55 576i100H 16:9 720(1440)x576i @ 200Hz"},
|
||||
{720, 480, 240, ASPECT(4, 3), PROGRESSIVE, false,
|
||||
"56 480p239 4:3 720x480p @ 239.76/240Hz"},
|
||||
{720, 480, 240, ASPECT(16, 9), PROGRESSIVE, false,
|
||||
"57 480p239H 16:9 720x480p @ 239.76/240Hz"},
|
||||
{720, 480, 240, ASPECT(4, 3), INTERLACE, false,
|
||||
"58 480i119 4:3 720(1440)x480i @ 239.76/240Hz"},
|
||||
{720, 480, 240, ASPECT(16, 9), INTERLACE, false,
|
||||
"59 480i119H 16:9 720(1440)x480i @ 239.76/240Hz"},
|
||||
{1280, 720, 24, ASPECT(16, 9), PROGRESSIVE, false,
|
||||
"60 720p24 16:9 1280x720p @ 23.98/24Hz"},
|
||||
{1280, 720, 25, ASPECT(16, 9), PROGRESSIVE, false,
|
||||
"61 720p25 16:9 1280x720p @ 25Hz"},
|
||||
{1280, 720, 30, ASPECT(16, 9), PROGRESSIVE, false,
|
||||
"62 720p30 16:9 1280x720p @ 29.97/30Hz"},
|
||||
{1920, 1080, 120, ASPECT(16, 9), PROGRESSIVE, false,
|
||||
"63 1080p120 16:9 1920x1080 @ 119.88/120Hz"},
|
||||
};
|
||||
|
||||
/* device supported modes in CEA */
|
||||
enum {
|
||||
CEA_MODE_640X480P_60HZ_4_3 = 0,
|
||||
CEA_MODE_720X480P_60HZ_4_3 = 1,
|
||||
CEA_MODE_720X480P_60HZ_16_9 = 2,
|
||||
CEA_MODE_1280X720P_60HZ_16_9 = 3,
|
||||
CEA_MODE_720X576P_50HZ_4_3 = 16,
|
||||
CEA_MODE_720X576P_50HZ_16_9 = 17,
|
||||
};
|
||||
|
||||
/* device supported modes in established timing */
|
||||
enum {
|
||||
ESTABLISHED_MODE_800X600_60HZ = 0,
|
||||
ESTABLISHED_MODE_640X480_60HZ = 5,
|
||||
};
|
||||
|
||||
int init_edid_info(struct edid_info_struct *edid_info)
|
||||
{
|
||||
edid_info->is_valid = false;
|
||||
mutex_init(&edid_info->access_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Byte 35-37 of block-0 */
|
||||
static char *established_timing_str[] = {
|
||||
"800x600 @ 60 Hz",
|
||||
"800x600 @ 56 Hz",
|
||||
"640x480 @ 75 Hz",
|
||||
"640x480 @ 72 Hz",
|
||||
"640x480 @ 67 Hz",
|
||||
"640x480 @ 60 Hz",
|
||||
"720x400 @ 88 Hz",
|
||||
"720x400 @ 70 Hz",
|
||||
|
||||
"1280x1024@75 Hz",
|
||||
"1024x768@75 Hz",
|
||||
"1024x768@70 Hz",
|
||||
"1024x768@60 Hz",
|
||||
"1024x768@87 Hz (Interlaced)",
|
||||
"832x624@75 Hz",
|
||||
"800x600@75 Hz",
|
||||
"800x600@72 Hz",
|
||||
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"1152x870 @ 75 Hz",
|
||||
};
|
||||
|
||||
/* E-EDID Video data block: */
|
||||
static char *vdb_modes_str[] = {
|
||||
" 1 DMT0659 4:3 640x480p @ 59.94/60Hz",
|
||||
" 2 480p 4:3 720x480p @ 59.94/60Hz",
|
||||
" 3 480pH 16:9 720x480p @ 59.94/60Hz",
|
||||
" 4 720p 16:9 1280x720p @ 59.94/60Hz",
|
||||
" 5 1080i 16:9 1920x1080i @ 59.94/60Hz",
|
||||
" 6 480i 4:3 720(1440)x480i @ 59.94/60Hz",
|
||||
" 7 480iH 16:9 720(1440)x480i @ 59.94/60Hz",
|
||||
" 8 240p 4:3 720(1440)x240p @ 59.94/60Hz",
|
||||
" 9 240pH 16:9 720(1440)x240p @ 59.94/60Hz",
|
||||
"10 480i4x 4:3 (2880)x480i @ 59.94/60Hz",
|
||||
"11 480i4xH 16:9 (2880)x480i @ 59.94/60Hz",
|
||||
"12 240p4x 4:3 (2880)x240p @ 59.94/60Hz",
|
||||
"13 240p4xH 16:9 (2880)x240p @ 59.94/60Hz",
|
||||
"14 480p2x 4:3 1440x480p @ 59.94/60Hz",
|
||||
"15 480p2xH 16:9 1440x480p @ 59.94/60Hz",
|
||||
"16 1080p 16:9 1920x1080p @ 59.94/60Hz",
|
||||
"17 576p 4:3 720x576p @ 50Hz",
|
||||
"18 576pH 16:9 720x576p @ 50Hz",
|
||||
"19 720p50 16:9 1280x720p @ 50Hz",
|
||||
"20 1080i25 16:9 1920x1080i @ 50Hz*",
|
||||
"21 576i 4:3 720(1440)x576i @ 50Hz",
|
||||
"22 576iH 16:9 720(1440)x576i @ 50Hz",
|
||||
"23 288p 4:3 720(1440)x288p @ 50Hz",
|
||||
"24 288pH 16:9 720(1440)x288p @ 50Hz",
|
||||
"25 576i4x 4:3 (2880)x576i @ 50Hz",
|
||||
"26 576i4xH 16:9 (2880)x576i @ 50Hz",
|
||||
"27 288p4x 4:3 (2880)x288p @ 50Hz",
|
||||
"28 288p4xH 16:9 (2880)x288p @ 50Hz",
|
||||
"29 576p2x 4:3 1440x576p @ 50Hz",
|
||||
"30 576p2xH 16:9 1440x576p @ 50Hz",
|
||||
"31 1080p50 16:9 1920x1080p @ 50Hz",
|
||||
"32 1080p24 16:9 1920x1080p @ 23.98/24Hz",
|
||||
"33 1080p25 16:9 1920x1080p @ 25Hz",
|
||||
"34 1080p30 16:9 1920x1080p @ 29.97/30Hz",
|
||||
"35 480p4x 4:3 (2880)x480p @ 59.94/60Hz",
|
||||
"36 480p4xH 16:9 (2880)x480p @ 59.94/60Hz",
|
||||
"37 576p4x 4:3 (2880)x576p @ 50Hz",
|
||||
"38 576p4xH 16:9 (2880)x576p @ 50Hz",
|
||||
"39 108Oi25 16:9 1920x1080i(1250 Total) @ 50Hz*",
|
||||
"40 1080i50 16:9 1920x1080i @ 100Hz",
|
||||
"41 720p100 16:9 1280x720p @ 100Hz",
|
||||
"42 576p100 4:3 720x576p @ 100Hz",
|
||||
"43 576p100H 16:9 720x576p @ 100Hz",
|
||||
"44 576i50 4:3 720(1440)x576i @ 100Hz",
|
||||
"45 576i50H 16:9 720(1440)x576i @ 100Hz",
|
||||
"46 1080i60 16:9 1920x1080i @ 119.88/120Hz",
|
||||
"47 720p120 16:9 1280x720p @ 119.88/120Hz",
|
||||
"48 480p119 4:3 720x480p @ 119.88/120Hz",
|
||||
"49 480p119H 16:9 720x480p @ 119.88/120Hz",
|
||||
"50 480i59 4:3 720(1440)x480i @ 119.88/120Hz",
|
||||
"51 480i59H 16:9 720(1440)x480i @ 119.88/120Hz",
|
||||
"52 576p200 4:3 720x576p @ 200Hz",
|
||||
"53 576p200H 16:9 720x576p @ 200Hz",
|
||||
"54 576i100 4:3 720(1440)x576i @ 200Hz",
|
||||
"55 576i100H 16:9 720(1440)x576i @ 200Hz",
|
||||
"56 480p239 4:3 720x480p @ 239.76/240Hz",
|
||||
"57 480p239H 16:9 720x480p @ 239.76/240Hz",
|
||||
"58 480i119 4:3 720(1440)x480i @ 239.76/240Hz",
|
||||
"59 480i119H 16:9 720(1440)x480i @ 239.76/240Hz",
|
||||
"60 720p24 16:9 1280x720p @ 23.98/24Hz",
|
||||
"61 720p25 16:9 1280x720p @ 25Hz",
|
||||
"62 720p30 16:9 1280x720p @ 29.97/30Hz",
|
||||
"63 1080p120 16:9 1920x1080 @ 119.88/120Hz",
|
||||
};
|
||||
|
||||
int edid_dump_video_modes(u8 *edid_buf)
|
||||
{
|
||||
int i, v1, v2, width, height, ret, aspect;
|
||||
char *str_aspect[] = { "16:10", "4:3", "5:4", "16:9" };
|
||||
|
||||
switch (edid_buf[0]) {
|
||||
case 0:
|
||||
pr_info("block type 0: supported mode:\n");
|
||||
v1 = edid_buf[35] | edid_buf[36] << 8 | edid_buf[37] << 16;
|
||||
/* Established timing */
|
||||
pr_info("established timing: {%02x, %02x, %02x}\n",
|
||||
edid_buf[35], edid_buf[36], edid_buf[37]);
|
||||
for (i = 0 ; i < 18; i++ ) {
|
||||
v1 >>= 1;
|
||||
if (v1 & 1) pr_info("%s\n", established_timing_str[i]);
|
||||
};
|
||||
|
||||
pr_info("Standard timing identification:\n");
|
||||
/* Standard timing identification */
|
||||
for (i = 0; i < 8; i++) {
|
||||
v1 = edid_buf[38+i*2];
|
||||
v2 = edid_buf[38+i*2+1];
|
||||
width = v1 * 8 + 248;
|
||||
aspect = v2 >> 6;
|
||||
switch (aspect) {
|
||||
case 0: height = width * 10 / 16; break;
|
||||
case 1: height = width * 3 / 4; break;
|
||||
case 2: height = width * 4 / 5; break;
|
||||
case 3: height = width * 9 / 16; break;
|
||||
}
|
||||
pr_info("%dx%d, %s, %d Hz\n", width, height,
|
||||
str_aspect[aspect], (v2 & ~(3 << 6)) + 60);
|
||||
}
|
||||
ret = 0;
|
||||
break;
|
||||
case 2:
|
||||
pr_info("block type 2: supported mode:\n");
|
||||
pr_info("edid_buf[4]=%x\n", edid_buf[4]);
|
||||
for( i = 0; i < (edid_buf[4] & 0x1f); i++) {
|
||||
pr_info("%s\n", vdb_modes_str[edid_buf[5+i] & 0x7f]);
|
||||
}
|
||||
ret = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool edid_do_checksum(u8 *data)
|
||||
{
|
||||
int i;
|
||||
u8 sum = 0;
|
||||
|
||||
for (i = 0; i < EDID_BLOCK_SIZE; i++)
|
||||
sum += data[i];
|
||||
EDID_DBG("%s: result=%s\n", __func__, sum ? "fail" : "pass");
|
||||
return sum ? false : true;
|
||||
}
|
||||
|
||||
static bool edid_check_header(u8 *data)
|
||||
{
|
||||
int ret, i = 0;
|
||||
/* EDID 8 bytes header */
|
||||
static u8 header[] = {0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0};
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(header); i++)
|
||||
if (data[i] != header[i])
|
||||
break;
|
||||
ret = (i == ARRAY_SIZE(header)) ? true : false;
|
||||
EDID_DBG("%s: result=%s\n", __func__, ret ? "pass" : "fail");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool edid_check_hdmi_sink(struct hdmi_info *hdmi, int block)
|
||||
{
|
||||
int ret = false, index = 4;
|
||||
u8 *data = &hdmi->edid_buf[block * 128];
|
||||
/* block offset where long descriptors start */
|
||||
int long_desc_offset = data[LONG_DESCR_PTR_IDX];
|
||||
int tag_code, data_block_len;
|
||||
|
||||
while (index < long_desc_offset) {
|
||||
tag_code = (data[index] >> 5) & 0x7;
|
||||
data_block_len = data[index++] & 0x1f;
|
||||
if (tag_code == VENDOR_SPEC_D_BLOCK &&
|
||||
data[index] == 0x03 &&
|
||||
data[index + 1] == 0x0c &&
|
||||
data[index + 2] == 0x00) {
|
||||
ret = true;
|
||||
break;
|
||||
} else
|
||||
index += data_block_len;
|
||||
}
|
||||
hdmi->edid_info.hdmi_sink = ret;
|
||||
EDID_DBG("%s: ret=%s\n", __func__, ret ? "yes" : "no");
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct edid_black_list_info {
|
||||
u8 mfr_model[4];
|
||||
u8 prefer_modes[3];
|
||||
};
|
||||
|
||||
struct edid_black_list_info edid_black_list[] = {
|
||||
{ {0x4c, 0x2d, 0xa5, 0x02}, {0, 0, 0x40} }, // 720p only
|
||||
{ {0x4c, 0x2d, 0x0d, 0x05}, {0, 0, 0x40} }, // 720p only
|
||||
|
||||
//{ {0x5a, 0x63, 0x20, 0x2b}, {0, 0, 0x40} }, // Viewsonic test
|
||||
};
|
||||
|
||||
/* By comparing the Manufacture(0x8, 0x9) and Model field(0xa, 0xb) of EDID,
|
||||
* to check if the attached TV is a known less-compatibile one.
|
||||
*/
|
||||
int edid_fixup_compatibility_list(struct hdmi_info *hdmi)
|
||||
{
|
||||
int i, ret = -1;
|
||||
|
||||
/* FIXME: magic numbers...*/
|
||||
for (i = 0; i < ARRAY_SIZE(edid_black_list); i++) {
|
||||
if (!memcmp(hdmi->edid_buf + 8, edid_black_list[i].mfr_model, 4)){
|
||||
#if 0
|
||||
EDID_DBG("%s: found in blacklist %d\n", __func__, i);
|
||||
EDID_DBG("%s: old timing = {%02x, %02x, %02x}\n",
|
||||
__func__,
|
||||
hdmi->edid_buf[35], hdmi->edid_buf[36],
|
||||
hdmi->edid_buf[37]);
|
||||
memcpy(hdmi->edid_buf + 35,
|
||||
edid_black_list[i].prefer_modes, 3);
|
||||
EDID_DBG("%s: new timing = {%02x, %02x, %02x}\n",
|
||||
__func__,
|
||||
hdmi->edid_buf[35], hdmi->edid_buf[36],
|
||||
hdmi->edid_buf[37]);
|
||||
#else
|
||||
EDID_DBG("%s: found in compatibility %d\n", __func__, i);
|
||||
memcpy(hdmi->edid_buf + 35,
|
||||
edid_black_list[i].prefer_modes, 3);
|
||||
#endif
|
||||
ret = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
u8 edid_simple_parsing(struct hdmi_info *hdmi)
|
||||
{
|
||||
u8 *edid_buf = hdmi->edid_buf;
|
||||
int i, index, ret = -EINVAL;
|
||||
struct edid_info_struct *edid_info = &hdmi->edid_info;
|
||||
unsigned v1, width, height, aspect;
|
||||
unsigned extensions;
|
||||
|
||||
EDID_DBG("%s\n", __func__);
|
||||
// FIXME: integrate with edid_check()
|
||||
if (!edid_do_checksum(edid_buf)) {
|
||||
pr_err("%s: checksum error\n", __func__);
|
||||
//return EDID_CHECKSUM_ERROR;
|
||||
}
|
||||
if (!edid_check_header(edid_buf)) {
|
||||
pr_err("%s: incorrect header\n", __func__);
|
||||
return INCORRECT_EDID_HEADER;
|
||||
}
|
||||
edid_info->under_scan = ((edid_buf[MISC_SUPPORT_IDX]) >> 7) & 0x1;
|
||||
edid_info->basic_audio = ((edid_buf[MISC_SUPPORT_IDX]) >> 6) & 0x1;
|
||||
edid_info->ycbcr_4_4_4 = ((edid_buf[MISC_SUPPORT_IDX]) >> 5) & 0x1;
|
||||
edid_info->ycbcr_4_2_2 = ((edid_buf[MISC_SUPPORT_IDX]) >> 4) & 0x1;
|
||||
|
||||
// FIXME: 0x7e
|
||||
extensions = edid_buf[0x7e];
|
||||
EDID_DBG("%s: extensions=%d\n", __func__, extensions);
|
||||
if (!extensions) {
|
||||
hdmi->edid_info.hdmi_sink = false;
|
||||
return NO_861_EXTENSIONS;
|
||||
}
|
||||
//return;
|
||||
|
||||
/* reset all supported */
|
||||
for (i = 0 ; i < ARRAY_SIZE(additional_timing_db); i++)
|
||||
additional_timing_db[i].supported = false;
|
||||
for (i = 0 ; i < ARRAY_SIZE(established_timing_db); i++)
|
||||
established_timing_db[i].supported = false;
|
||||
|
||||
/* Block 0: established timing */
|
||||
pr_info("established timing: {%02x, %02x, %02x}\n",
|
||||
edid_buf[35], edid_buf[36], edid_buf[37]);
|
||||
|
||||
v1 = edid_buf[35] | edid_buf[36] << 8 | (!!edid_buf[37]) << 16;
|
||||
|
||||
for (i = 0 ; i < 17; i++ ) // 17 bits defined in established timing
|
||||
established_timing_db[i].supported = ((v1 >>= 1) & 1) ;
|
||||
|
||||
#if 0
|
||||
/* standard timing identification */
|
||||
for (i = 0; i < 8; i++) {
|
||||
width = edid_buf[38 + i * 2] * 8 + 248;
|
||||
v1 = edid_buf[38 + i * 2 + 1];
|
||||
switch (v1 >> 6) {
|
||||
case 0: height = width * 10 / 16; aspect = ASPECT(16, 10); break;
|
||||
case 1: height = width * 3 / 4; aspect = ASPECT(4, 3); break;
|
||||
case 2: height = width * 4 / 5; aspect = ASPECT(5, 4); break;
|
||||
case 3: height = width * 9 / 16; aspect = ASPECT(16, 9); break;
|
||||
}
|
||||
standard_timing_db[i].width = width;
|
||||
standard_timing_db[i].height = height;
|
||||
standard_timing_db[i].aspect = aspect;
|
||||
standard_timing_db[i].refresh_rate = (v1 & ~(3 << 6)) + 60;
|
||||
standard_timing_db[i].supported = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (extensions == 1) {
|
||||
EDID_DBG("Block-1: additional timing\n");
|
||||
/* Block-1: additional timing */
|
||||
for( i = 0; i < (edid_buf[128 + 4] & 0x1f); i++) {
|
||||
index = edid_buf[128 + 5 + i] & 0x7f;
|
||||
additional_timing_db[index-1].supported = true;
|
||||
EDID_DBG("%s\n", additional_timing_db[index-1].descrption);
|
||||
}
|
||||
edid_check_hdmi_sink(hdmi, 1);
|
||||
} else {
|
||||
EDID_DBG("Block-2: additional timing\n");
|
||||
for( i = 0; i < (edid_buf[384 + 4] & 0x1f); i++) {
|
||||
index = edid_buf[384 + 5 + i] & 0x7f;
|
||||
additional_timing_db[index-1].supported = true;
|
||||
EDID_DBG("%s\n", additional_timing_db[index-1].descrption);
|
||||
}
|
||||
edid_check_hdmi_sink(hdmi, 3);
|
||||
}
|
||||
|
||||
edid_buf[35] = 0;
|
||||
edid_buf[36] = 0;
|
||||
edid_buf[37] = 0;
|
||||
|
||||
/* edid_buf[37] bit4: 480p, bit5: 576p, bit6: 720p */
|
||||
if (additional_timing_db[CEA_MODE_720X480P_60HZ_4_3].supported ||
|
||||
additional_timing_db[CEA_MODE_720X480P_60HZ_16_9].supported) {
|
||||
EDID_DBG("decide to support 480P\n");
|
||||
edid_buf[37] |= (1<<4);
|
||||
}
|
||||
|
||||
if (additional_timing_db[CEA_MODE_720X576P_50HZ_4_3].supported ||
|
||||
additional_timing_db[CEA_MODE_720X576P_50HZ_16_9].supported) {
|
||||
EDID_DBG("decide to support 576P\n");
|
||||
edid_buf[37] |= (1<<5);
|
||||
}
|
||||
|
||||
if (additional_timing_db[CEA_MODE_1280X720P_60HZ_16_9].supported) {
|
||||
EDID_DBG("decide to support 720P\n");
|
||||
edid_buf[37] |= (1<<6);
|
||||
}
|
||||
|
||||
if (established_timing_db[ESTABLISHED_MODE_800X600_60HZ].supported) {
|
||||
EDID_DBG("decide to support 800x600\n");
|
||||
edid_buf[36] |= (1<<6);
|
||||
}
|
||||
|
||||
if (established_timing_db[ESTABLISHED_MODE_640X480_60HZ].supported) {
|
||||
EDID_DBG("decide to support 640x480\n");
|
||||
edid_buf[35] |= (1<<5);
|
||||
}
|
||||
edid_fixup_compatibility_list(hdmi);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// FIXME: modify the checking routines into inline function.
|
||||
bool edid_is_video_mode_supported(struct video_mode *vmode)
|
||||
{
|
||||
int i;
|
||||
struct video_mode *vmode_db;
|
||||
|
||||
vmode_db = established_timing_db;
|
||||
for (i = 0, vmode_db = established_timing_db;
|
||||
i < ARRAY_SIZE(established_timing_db); i++)
|
||||
if ( (vmode->width == vmode_db[i].width) &&
|
||||
(vmode->height == vmode_db[i].height) &&
|
||||
(vmode->refresh_rate == vmode_db[i].refresh_rate ) &&
|
||||
(vmode_db[i].interlaced == PROGRESSIVE) &&
|
||||
(vmode_db[i].supported == true ))
|
||||
return true;
|
||||
for (i = 0, vmode_db = standard_timing_db;
|
||||
i < ARRAY_SIZE(standard_timing_db); i++)
|
||||
if ( (vmode->width == vmode_db[i].width) &&
|
||||
(vmode->height == vmode_db[i].height) &&
|
||||
(vmode->refresh_rate == vmode_db[i].refresh_rate ) &&
|
||||
(vmode_db[i].interlaced == PROGRESSIVE) &&
|
||||
(vmode_db[i].supported == true ))
|
||||
return true;
|
||||
for (i = 0, vmode_db = additional_timing_db;
|
||||
i < ARRAY_SIZE(additional_timing_db); i++)
|
||||
if ( (vmode->width == vmode_db[i].width) &&
|
||||
(vmode->height == vmode_db[i].height) &&
|
||||
(vmode->refresh_rate == vmode_db[i].refresh_rate ) &&
|
||||
(vmode_db[i].interlaced == PROGRESSIVE) &&
|
||||
(vmode_db[i].supported == true ))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool edid_check_sink_type(struct hdmi_info *hdmi)
|
||||
{
|
||||
EDID_DBG("%s: ret=%d\n", __func__, hdmi->edid_info.hdmi_sink);
|
||||
return hdmi->edid_info.hdmi_sink;
|
||||
}
|
||||
|
||||
int edid_dump_hex(u8 *src, int src_size, char *output, int output_size)
|
||||
{
|
||||
char line[80];
|
||||
static char hextab[] = "0123456789abcdef";
|
||||
int i, j, n = 0, v, len, offset, line_size;
|
||||
|
||||
len = src_size;
|
||||
memset(line, ' ', 79);
|
||||
line[79] = '\0';
|
||||
offset = strlen("0000 | ");
|
||||
line_size = offset + 3 * 16 + 1;
|
||||
for (i = 0; i < len / 16 ; i++) {
|
||||
scnprintf(line, offset + 1, "%04x | ", (i << 4));
|
||||
for (j = 0; j < 16 ; j++) {
|
||||
v = src[i * 16 + j];
|
||||
line[offset + j * 3] = hextab[v / 16];
|
||||
line[offset + j * 3 + 1] = hextab[v % 16];
|
||||
}
|
||||
line[line_size - 1] = '\n';
|
||||
strncpy(output+ n, line, line_size);
|
||||
if ((n + line_size) > output_size)
|
||||
break;
|
||||
else
|
||||
n += line_size;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/*============================================================================*/
|
||||
static ssize_t edid_dbg_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
file->private_data = inode->i_private;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char hex_buff[2048];
|
||||
static ssize_t edid_buffered_read(struct file *filp, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
int n;
|
||||
int extensions;
|
||||
struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
|
||||
|
||||
extensions = hdmi->edid_buf[0x7e] + 1;
|
||||
edid_dump_video_modes(hdmi->edid_buf);// fixme: crashed
|
||||
if (extensions == 1)
|
||||
edid_dump_video_modes(hdmi->edid_buf + 128);
|
||||
else
|
||||
edid_dump_video_modes(hdmi->edid_buf + 384);
|
||||
//edid_simple_parsing(hdmi); // FIXME: crashed...
|
||||
n = edid_dump_hex(hdmi->edid_buf, (hdmi->edid_buf[0x7e] + 1) * 128,
|
||||
hex_buff, 2048);
|
||||
return simple_read_from_buffer(buf, count, ppos, hex_buff, n);
|
||||
}
|
||||
|
||||
static struct file_operations edid_debugfs_fops[] = {
|
||||
{
|
||||
.open = edid_dbg_open,
|
||||
.read = edid_buffered_read,
|
||||
.write = NULL,
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: error handling
|
||||
int edid_debugfs_init(struct hdmi_info *hdmi)
|
||||
{
|
||||
//int ret;
|
||||
struct dentry *edid_dent;
|
||||
|
||||
edid_dent = debugfs_create_dir("edid", hdmi->debug_dir);
|
||||
if (IS_ERR(edid_dent))
|
||||
return PTR_ERR(edid_dent);
|
||||
|
||||
debugfs_create_file("hex_dump", 0444, edid_dent, hdmi,
|
||||
&edid_debugfs_fops[0]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
716
drivers/video/msm/hdmi/fb-hdmi.c
Normal file
716
drivers/video/msm/hdmi/fb-hdmi.c
Normal file
@ -0,0 +1,716 @@
|
||||
/*
|
||||
* Copyright (C) 2009 HTC
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* Referenced from drivers/video/msm/msm_fb.c, Google Incorporated.
|
||||
*
|
||||
*/
|
||||
#define DEBUG
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/wakelock.h>
|
||||
#include <linux/earlysuspend.h>
|
||||
#include <linux/msm_mdp.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <mach/msm_fb.h>
|
||||
#include <mach/board.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/htc_hdmi.h>
|
||||
#ifdef CONFIG_HTC_HEADSET_MGR
|
||||
#include <mach/htc_headset_mgr.h>
|
||||
#endif
|
||||
|
||||
#include "include/fb-hdmi.h"
|
||||
#include "include/sil902x.h"
|
||||
|
||||
#if 1
|
||||
#define HDMI_DBG(s...) printk("[hdmi/fb]" s)
|
||||
#else
|
||||
#define HDMI_DBG(s...) do {} while (0)
|
||||
#endif
|
||||
|
||||
struct update_info_t {
|
||||
int left;
|
||||
int top;
|
||||
int eright; /* exclusive */
|
||||
int ebottom; /* exclusive */
|
||||
unsigned yoffset;
|
||||
};
|
||||
|
||||
struct hdmifb_info {
|
||||
struct fb_info *fb;
|
||||
struct msm_panel_data *panel;
|
||||
struct notifier_block fb_hdmi_event;
|
||||
struct msmfb_callback dma_callback;
|
||||
struct msmfb_callback vsync_callback;
|
||||
struct update_info_t update_info;
|
||||
struct early_suspend earlier_suspend;
|
||||
struct early_suspend early_suspend;
|
||||
spinlock_t update_lock;
|
||||
int xres;
|
||||
int yres;
|
||||
unsigned long state;
|
||||
atomic_t use_count;
|
||||
};
|
||||
|
||||
static struct mdp_device *mdp;
|
||||
static struct hdmi_device *hdmi;
|
||||
|
||||
static unsigned PP[16];
|
||||
|
||||
void hdmi_pre_change(struct hdmi_info *hdmi);
|
||||
void hdmi_post_change(struct hdmi_info *info, struct fb_var_screeninfo *var);
|
||||
|
||||
static int hdmifb_open(struct fb_info *info, int user)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdmifb_release(struct fb_info *info, int user)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdmifb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
|
||||
{
|
||||
u32 size;
|
||||
|
||||
if (mdp->check_output_format(mdp, var->bits_per_pixel))
|
||||
return -EINVAL;
|
||||
if (hdmi->check_res(hdmi, var))
|
||||
return -EINVAL;
|
||||
|
||||
size = var->xres_virtual * var->yres_virtual *
|
||||
(var->bits_per_pixel >> 3);
|
||||
if (size > info->fix.smem_len)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdmifb_set_par(struct fb_info *info)
|
||||
{
|
||||
struct fb_var_screeninfo *var = &info->var;
|
||||
struct fb_fix_screeninfo *fix = &info->fix;
|
||||
struct hdmifb_info *hdmi_fb = info->par;
|
||||
struct msm_panel_data *panel = hdmi_fb->panel;
|
||||
struct msm_lcdc_timing *timing;
|
||||
struct hdmi_info *hinfo = container_of(hdmi, struct hdmi_info,
|
||||
hdmi_dev);
|
||||
|
||||
HDMI_DBG("%s\n", __func__);
|
||||
/* we only support RGB ordering for now */
|
||||
if (var->bits_per_pixel == 32 || var->bits_per_pixel == 24) {
|
||||
var->red.offset = 0;
|
||||
var->red.length = 8;
|
||||
var->green.offset = 8;
|
||||
var->green.length = 8;
|
||||
var->blue.offset = 16;
|
||||
var->blue.length = 8;
|
||||
} else if (var->bits_per_pixel == 16) {
|
||||
var->red.offset = 11;
|
||||
var->red.length = 5;
|
||||
var->green.offset = 5;
|
||||
var->green.length = 6;
|
||||
var->blue.offset = 0;
|
||||
var->blue.length = 5;
|
||||
} else
|
||||
return -1;
|
||||
|
||||
HDMI_DBG("set res (%d, %d)\n", var->xres, var->yres);
|
||||
timing = hdmi->set_res(hdmi, var);
|
||||
panel->adjust_timing(panel, timing, var->xres, var->yres);
|
||||
hdmi_post_change(hinfo, var);
|
||||
|
||||
mdp->set_output_format(mdp, var->bits_per_pixel);
|
||||
|
||||
hdmi_fb->xres = var->xres;
|
||||
hdmi_fb->yres = var->yres;
|
||||
fix->line_length = var->xres * var->bits_per_pixel / 8;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* core update function */
|
||||
static void
|
||||
hdmifb_pan_update(struct fb_info *info, uint32_t left, uint32_t top,
|
||||
uint32_t eright, uint32_t ebottom, uint32_t yoffset)
|
||||
{
|
||||
struct hdmifb_info *hdmi_fb = info->par;
|
||||
struct msm_panel_data *panel = hdmi_fb->panel;
|
||||
unsigned long irq_flags;
|
||||
|
||||
/* printk(KERN_DEBUG "%s\n", __func__); */
|
||||
if ((test_bit(fb_enabled, &hdmi_fb->state) == 0) ||
|
||||
(test_bit(hdmi_enabled, &hdmi_fb->state) == 0))
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&hdmi_fb->update_lock, irq_flags);
|
||||
hdmi_fb->update_info.left = left;
|
||||
hdmi_fb->update_info.top = top;
|
||||
hdmi_fb->update_info.eright = eright;
|
||||
hdmi_fb->update_info.ebottom = ebottom;
|
||||
hdmi_fb->update_info.yoffset = yoffset;
|
||||
spin_unlock_irqrestore(&hdmi_fb->update_lock, irq_flags);
|
||||
panel->request_vsync(panel, &hdmi_fb->vsync_callback);
|
||||
}
|
||||
|
||||
/* fb ops, fb_pan_display */
|
||||
static int
|
||||
hdmifb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
|
||||
{
|
||||
/* full update */
|
||||
hdmifb_pan_update(info, 0, 0, info->var.xres, info->var.yres,
|
||||
var->yoffset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hdmifb_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
|
||||
{
|
||||
cfb_fillrect(p, rect);
|
||||
hdmifb_pan_update(p, rect->dx, rect->dy, rect->dx + rect->width,
|
||||
rect->dy + rect->height, 0);
|
||||
}
|
||||
|
||||
static void hdmifb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
|
||||
{
|
||||
cfb_copyarea(p, area);
|
||||
hdmifb_pan_update(p, area->dx, area->dy, area->dx + area->width,
|
||||
area->dy + area->height, 0);
|
||||
}
|
||||
|
||||
static void hdmifb_imageblit(struct fb_info *p, const struct fb_image *image)
|
||||
{
|
||||
cfb_imageblit(p, image);
|
||||
hdmifb_pan_update(p, image->dx, image->dy, image->dx + image->width,
|
||||
image->dy + image->height, 0);
|
||||
}
|
||||
|
||||
static int hdmifb_change_mode(struct fb_info *info, unsigned int mode)
|
||||
{
|
||||
struct hdmifb_info *hdmi_fb = info->par;
|
||||
|
||||
/* printk(KERN_DEBUG "%s mode = %d\n", __func__, mode); */
|
||||
|
||||
if (mode)
|
||||
set_bit(hdmi_mode, &hdmi_fb->state);
|
||||
else
|
||||
clear_bit(hdmi_mode, &hdmi_fb->state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct hdmifb_info *_hdmi_fb;
|
||||
int hdmifb_get_mode(void)
|
||||
{
|
||||
return test_bit(hdmi_mode, &_hdmi_fb->state);
|
||||
}
|
||||
|
||||
bool hdmifb_suspending = false;
|
||||
|
||||
static int hdmifb_pause(struct fb_info *fb, unsigned int mode)
|
||||
{
|
||||
int ret = 0;
|
||||
struct hdmifb_info *hdmi_fb = fb->par;
|
||||
struct msm_panel_data *panel = hdmi_fb->panel;
|
||||
struct hdmi_info *info = container_of(hdmi, struct hdmi_info,
|
||||
hdmi_dev);
|
||||
|
||||
pr_info("%s: %d %s\n", __func__, atomic_read(&hdmi_fb->use_count),
|
||||
mode == 1 ? "pause" : "resume");
|
||||
|
||||
if (mode == 1) {
|
||||
hdmifb_suspending = false;
|
||||
HDMI_DBG("%s: hdmifb_suspending = false\n", __func__);
|
||||
/* pause */
|
||||
if (atomic_read(&hdmi_fb->use_count) == 0)
|
||||
goto done;
|
||||
if (atomic_dec_return(&hdmi_fb->use_count) == 0) {
|
||||
hdmi_pre_change(info);
|
||||
ret = panel->blank(panel);
|
||||
clear_bit(hdmi_enabled, &hdmi_fb->state);
|
||||
#ifdef CONFIG_HTC_HEADSET_MGR
|
||||
switch_send_event(BIT_HDMI_AUDIO, 0);
|
||||
#endif
|
||||
}
|
||||
} else if (mode == 0) {
|
||||
/* resume */
|
||||
if (atomic_inc_return(&hdmi_fb->use_count) == 1) {
|
||||
hdmi_pre_change(info);
|
||||
ret = panel->unblank(panel);
|
||||
/*
|
||||
// set timing again to prevent TV been out of range
|
||||
var = &fb->var;
|
||||
timing = hdmi->set_res(hdmi, var);
|
||||
panel->adjust_timing(panel, timing, var->xres, var->yres);
|
||||
hdmi_post_change(info, var);
|
||||
*/
|
||||
set_bit(hdmi_enabled, &hdmi_fb->state);
|
||||
#ifdef CONFIG_HTC_HEADSET_MGR
|
||||
switch_send_event(BIT_HDMI_AUDIO, 1);
|
||||
#endif
|
||||
}
|
||||
} else
|
||||
ret = -EINVAL;
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hdmifb_blit(struct fb_info *info, void __user *p)
|
||||
{
|
||||
struct mdp_blit_req req;
|
||||
struct mdp_blit_req_list req_list;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
if (copy_from_user(&req_list, p, sizeof(req_list)))
|
||||
return -EFAULT;
|
||||
|
||||
for (i = 0; i < req_list.count; i++) {
|
||||
struct mdp_blit_req_list *list =
|
||||
(struct mdp_blit_req_list *)p;
|
||||
if (copy_from_user(&req, &list->req[i], sizeof(req)))
|
||||
return -EFAULT;
|
||||
req.flags |= MDP_DITHER;
|
||||
ret = mdp->blit(mdp, info, &req);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum ioctl_cmd_index {
|
||||
CMD_SET_MODE,
|
||||
CMD_GET_MODE,
|
||||
CMD_DISABLE,
|
||||
CMD_ENABLE,
|
||||
CMD_GET_STATE,
|
||||
CMD_BLIT,
|
||||
CMD_CABLE_STAT,
|
||||
CMD_ESTABLISH_TIMING,
|
||||
};
|
||||
|
||||
static char *cmd_str[] = {
|
||||
"HDMI_SET_MODE",
|
||||
"HDMI_GET_MODE",
|
||||
"HDMI_DISABLE",
|
||||
"HDMI_ENABLE",
|
||||
"HDMI_GET_STATE",
|
||||
"HDMI_BLIT",
|
||||
"HDMI_CABLE_STAT",
|
||||
"HDMI_ESTABLISH_TIMING",
|
||||
};
|
||||
|
||||
static int hdmifb_ioctl(struct fb_info *p, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct hdmifb_info *hdmi_fb = p->par;
|
||||
void __user *argp = (void __user *)arg;
|
||||
unsigned int val;
|
||||
int ret = -EINVAL;
|
||||
struct hdmi_info *hinfo = container_of(hdmi, struct hdmi_info,
|
||||
hdmi_dev);
|
||||
|
||||
/*
|
||||
if (cmd != HDMI_BLIT)
|
||||
HDMI_DBG("%s, cmd=%d=%s\n", __func__, cmd - HDMI_SET_MODE,
|
||||
cmd_str[cmd-HDMI_SET_MODE]);
|
||||
*/
|
||||
|
||||
switch (cmd) {
|
||||
case HDMI_SET_MODE:
|
||||
get_user(val, (unsigned __user *) arg);
|
||||
//pr_info("[hdmi] SET_MODE: %d\n", val);
|
||||
ret = hdmifb_change_mode(p, val);
|
||||
break;
|
||||
case HDMI_GET_MODE:
|
||||
/*
|
||||
pr_info("[hdmi] GET_MODE: %d\n",
|
||||
test_bit(hdmi_mode, &hdmi_fb->state));
|
||||
*/
|
||||
ret = put_user(test_bit(hdmi_mode, &hdmi_fb->state),
|
||||
(unsigned __user *) arg);
|
||||
break;
|
||||
case HDMI_DISABLE:
|
||||
get_user(val, (unsigned __user *) arg);
|
||||
ret = hdmifb_pause(p, 1);
|
||||
break;
|
||||
case HDMI_ENABLE:
|
||||
get_user(val, (unsigned __user *) arg);
|
||||
ret = hdmifb_pause(p, 0);
|
||||
break;
|
||||
case HDMI_GET_STATE:
|
||||
ret = put_user(test_bit(hdmi_enabled, &hdmi_fb->state),
|
||||
(unsigned __user *) arg);
|
||||
break;
|
||||
case HDMI_BLIT:
|
||||
if (test_bit(hdmi_enabled, &hdmi_fb->state))
|
||||
ret = hdmifb_blit(p, argp);
|
||||
else
|
||||
ret = -EPERM;
|
||||
break;
|
||||
case HDMI_CABLE_STAT: {
|
||||
int connect;
|
||||
ret = hdmi->get_cable_state(hdmi, &connect);
|
||||
ret = put_user(connect, (unsigned __user *) arg);
|
||||
break;
|
||||
}
|
||||
case HDMI_ESTABLISH_TIMING: {
|
||||
u8 tmp[3];
|
||||
hdmi->get_establish_timing(hdmi, tmp);
|
||||
ret = copy_to_user((unsigned __user *) arg, tmp, 3);
|
||||
if (ret)
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
case HDMI_GET_EDID:
|
||||
ret = copy_to_user((unsigned __user *) arg,
|
||||
hinfo->edid_buf, 512);
|
||||
break;
|
||||
case HDMI_GET_DISPLAY_INFO: {
|
||||
struct display_info dinfo;
|
||||
u8 *ptr = hinfo->edid_buf;
|
||||
dinfo.visible_width =
|
||||
(((u32)ptr[68] & 0xf0) << 4) | ptr[66];
|
||||
dinfo.visible_height =
|
||||
(((u32)ptr[68] & 0x0f) << 8) | ptr[67];
|
||||
dinfo.resolution_width =
|
||||
(((u32)ptr[58] & 0xf0) << 4) | ptr[56];
|
||||
dinfo.resolution_height =
|
||||
(((u32)ptr[61] & 0xf0) << 4) | ptr[59];
|
||||
ret = copy_to_user((unsigned __user *) arg,
|
||||
&dinfo, sizeof(dinfo));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
printk(KERN_ERR "hdmi: unknown cmd, cmd = %d\n", cmd);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct fb_ops hdmi_fb_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.fb_open = hdmifb_open,
|
||||
.fb_release = hdmifb_release,
|
||||
.fb_check_var = hdmifb_check_var,
|
||||
.fb_set_par = hdmifb_set_par,
|
||||
.fb_pan_display = hdmifb_pan_display,
|
||||
.fb_fillrect = hdmifb_fillrect,
|
||||
.fb_copyarea = hdmifb_copyarea,
|
||||
.fb_imageblit = hdmifb_imageblit,
|
||||
.fb_ioctl = hdmifb_ioctl,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_HAS_EARLYSUSPEND
|
||||
static void hdmifb_suspend(struct early_suspend *h)
|
||||
{
|
||||
struct hdmifb_info *hdmi_fb = container_of(h, struct hdmifb_info,
|
||||
early_suspend);
|
||||
struct msm_panel_data *panel = hdmi_fb->panel;
|
||||
|
||||
HDMI_DBG("%s, use_count=%d\n", __func__,
|
||||
atomic_read(&hdmi_fb->use_count));
|
||||
hdmifb_suspending = true;
|
||||
HDMI_DBG("%s: hdmifb_suspending = true\n", __func__);
|
||||
if (atomic_read(&hdmi_fb->use_count) &&
|
||||
false == test_bit(hdmi_enabled, &hdmi_fb->state)
|
||||
) {
|
||||
if (panel->blank)
|
||||
panel->blank(panel);
|
||||
}
|
||||
|
||||
if (panel->suspend)
|
||||
panel->suspend(panel);
|
||||
|
||||
clear_bit(hdmi_enabled, &hdmi_fb->state);
|
||||
clear_bit(fb_enabled, &hdmi_fb->state);
|
||||
}
|
||||
|
||||
static void hdmifb_resume(struct early_suspend *h)
|
||||
{
|
||||
struct hdmifb_info *hdmi_fb = container_of(h, struct hdmifb_info,
|
||||
early_suspend);
|
||||
struct msm_panel_data *panel = hdmi_fb->panel;
|
||||
|
||||
HDMI_DBG("%s\n", __func__);
|
||||
if (panel->resume)
|
||||
panel->resume(panel);
|
||||
|
||||
atomic_set(&hdmi_fb->use_count, 0);
|
||||
set_bit(fb_enabled, &hdmi_fb->state);
|
||||
}
|
||||
#endif
|
||||
|
||||
#define BITS_PER_PIXEL 16
|
||||
|
||||
static void setup_fb_info(struct hdmifb_info *hdmi_fb)
|
||||
{
|
||||
struct fb_info *fb_info = hdmi_fb->fb;
|
||||
int r;
|
||||
|
||||
/* finish setting up the fb_info struct */
|
||||
strncpy(fb_info->fix.id, "hdmi_fb", 16);
|
||||
fb_info->fix.ypanstep = 1;
|
||||
|
||||
fb_info->fbops = &hdmi_fb_ops;
|
||||
fb_info->flags = FBINFO_DEFAULT;
|
||||
|
||||
fb_info->fix.type = FB_TYPE_PACKED_PIXELS;
|
||||
fb_info->fix.visual = FB_VISUAL_TRUECOLOR;
|
||||
fb_info->fix.line_length = hdmi_fb->xres * 2;
|
||||
|
||||
fb_info->var.xres = hdmi_fb->xres;
|
||||
fb_info->var.yres = hdmi_fb->yres;
|
||||
fb_info->var.width = hdmi_fb->panel->fb_data->width;
|
||||
fb_info->var.height = hdmi_fb->panel->fb_data->height;
|
||||
fb_info->var.xres_virtual = hdmi_fb->xres;
|
||||
fb_info->var.yres_virtual = hdmi_fb->yres * 2;
|
||||
fb_info->var.bits_per_pixel = BITS_PER_PIXEL;
|
||||
fb_info->var.accel_flags = 0;
|
||||
fb_info->var.yoffset = 0;
|
||||
|
||||
fb_info->var.red.offset = 11;
|
||||
fb_info->var.red.length = 5;
|
||||
fb_info->var.red.msb_right = 0;
|
||||
fb_info->var.green.offset = 5;
|
||||
fb_info->var.green.length = 6;
|
||||
fb_info->var.green.msb_right = 0;
|
||||
fb_info->var.blue.offset = 0;
|
||||
fb_info->var.blue.length = 5;
|
||||
fb_info->var.blue.msb_right = 0;
|
||||
|
||||
r = fb_alloc_cmap(&fb_info->cmap, 16, 0);
|
||||
fb_info->pseudo_palette = PP;
|
||||
|
||||
PP[0] = 0;
|
||||
for (r = 1; r < 16; r++)
|
||||
PP[r] = 0xffffffff;
|
||||
}
|
||||
|
||||
static int
|
||||
setup_fbmem(struct hdmifb_info *hdmi_fb, struct platform_device *pdev)
|
||||
{
|
||||
struct fb_info *fb = hdmi_fb->fb;
|
||||
struct resource *res;
|
||||
unsigned long size = hdmi_fb->xres * hdmi_fb->yres *
|
||||
(BITS_PER_PIXEL >> 3) * 2;
|
||||
unsigned char *fbram;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -EINVAL;
|
||||
|
||||
/* check the resource is large enough to fit the fb */
|
||||
if (resource_size(res) < size) {
|
||||
printk(KERN_ERR "allocated resource(%d) is too small(%lu)"
|
||||
"for fb\n", resource_size(res), size);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
fb->fix.smem_start = res->start;
|
||||
fb->fix.smem_len = resource_size(res);
|
||||
|
||||
fbram = ioremap(res->start, resource_size(res));
|
||||
if (fbram == 0) {
|
||||
printk(KERN_ERR "hdmi_fb: cannot allocate fbram!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
fb->screen_base = fbram;
|
||||
memset(fbram, 0, resource_size(res));
|
||||
|
||||
printk(KERN_DEBUG "HDMI FB: 0x%x 0x%x\n", res->start, res->end);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called from dma interrupt handler, must not sleep */
|
||||
static void hdmi_handle_dma(struct msmfb_callback *callback)
|
||||
{
|
||||
/* printk(KERN_DEBUG "%s\n", __func__); */
|
||||
}
|
||||
|
||||
/* Called from vsync interrupt handler, must not sleep */
|
||||
static void hdmi_handle_vsync(struct msmfb_callback *callback)
|
||||
{
|
||||
uint32_t x, y, w, h;
|
||||
unsigned yoffset;
|
||||
unsigned addr;
|
||||
unsigned long irq_flags;
|
||||
struct fb_info *mirror_fb = registered_fb[0], *fb_hdmi;
|
||||
struct hdmifb_info *hdmi = container_of(callback, struct hdmifb_info,
|
||||
vsync_callback);
|
||||
struct msm_panel_data *panel = hdmi->panel;
|
||||
|
||||
spin_lock_irqsave(&hdmi->update_lock, irq_flags);
|
||||
x = hdmi->update_info.left;
|
||||
y = hdmi->update_info.top;
|
||||
w = hdmi->update_info.eright - x;
|
||||
h = hdmi->update_info.ebottom - y;
|
||||
yoffset = hdmi->update_info.yoffset;
|
||||
hdmi->update_info.left = hdmi->xres + 1;
|
||||
hdmi->update_info.top = hdmi->yres + 1;
|
||||
hdmi->update_info.eright = 0;
|
||||
hdmi->update_info.ebottom = 0;
|
||||
if (unlikely(w > hdmi->xres || h > hdmi->yres ||
|
||||
w == 0 || h == 0)) {
|
||||
printk(KERN_INFO "invalid update: %d %d %d "
|
||||
"%d\n", x, y, w, h);
|
||||
goto error;
|
||||
}
|
||||
spin_unlock_irqrestore(&hdmi->update_lock, irq_flags);
|
||||
|
||||
addr = ((hdmi->xres * (yoffset + y) + x) * 2);
|
||||
if (test_bit(hdmi_mode, &hdmi->state) == 0) {
|
||||
mdp->dma(mdp, addr + mirror_fb->fix.smem_start,
|
||||
hdmi->xres * 2, w, h, x, y, &hdmi->dma_callback,
|
||||
panel->interface_type);
|
||||
} else {
|
||||
fb_hdmi = hdmi->fb;
|
||||
mdp->dma(mdp, addr + fb_hdmi->fix.smem_start,
|
||||
hdmi->xres * 2, w, h, x, y, &hdmi->dma_callback,
|
||||
panel->interface_type);
|
||||
}
|
||||
return;
|
||||
error:
|
||||
spin_unlock_irqrestore(&hdmi->update_lock, irq_flags);
|
||||
}
|
||||
|
||||
static int hdmifb_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct fb_info *info;
|
||||
struct hdmifb_info *hdmi_fb;
|
||||
struct msm_panel_data *panel = pdev->dev.platform_data;
|
||||
int ret;
|
||||
|
||||
printk(KERN_DEBUG "%s\n", __func__);
|
||||
|
||||
if (!panel) {
|
||||
pr_err("hdmi_fb_probe: no platform data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!panel->fb_data) {
|
||||
pr_err("hdmi_fb_probe: no fb_data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
info = framebuffer_alloc(sizeof(struct hdmifb_info), &pdev->dev);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
hdmi_fb = info->par;
|
||||
_hdmi_fb = hdmi_fb;
|
||||
hdmi_fb->fb = info;
|
||||
hdmi_fb->panel = panel;
|
||||
set_bit(hdmi_mode, &hdmi_fb->state);
|
||||
hdmi_fb->dma_callback.func = hdmi_handle_dma;
|
||||
hdmi_fb->vsync_callback.func = hdmi_handle_vsync;
|
||||
hdmi_fb->xres = panel->fb_data->xres;
|
||||
hdmi_fb->yres = panel->fb_data->yres;
|
||||
spin_lock_init(&hdmi_fb->update_lock);
|
||||
|
||||
ret = setup_fbmem(hdmi_fb, pdev);
|
||||
if (ret)
|
||||
goto error_setup_fbmem;
|
||||
|
||||
setup_fb_info(hdmi_fb);
|
||||
|
||||
ret = register_framebuffer(info);
|
||||
if (ret)
|
||||
goto error_register_fb;
|
||||
|
||||
printk(KERN_INFO "hdmi_fb %d * %d initialed\n",
|
||||
hdmi_fb->xres, hdmi_fb->yres);
|
||||
|
||||
#ifdef CONFIG_HAS_EARLYSUSPEND
|
||||
hdmi_fb->early_suspend.suspend = hdmifb_suspend;
|
||||
hdmi_fb->early_suspend.resume = hdmifb_resume;
|
||||
hdmi_fb->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
|
||||
register_early_suspend(&hdmi_fb->early_suspend);
|
||||
#endif
|
||||
/* blank panel explicitly because we turn on clk on initial */
|
||||
if (panel->blank)
|
||||
panel->blank(panel);
|
||||
set_bit(fb_enabled, &hdmi_fb->state);
|
||||
return 0;
|
||||
|
||||
error_register_fb:
|
||||
error_setup_fbmem:
|
||||
framebuffer_release(hdmi_fb->fb);
|
||||
printk(KERN_ERR "msm probe fail with %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct platform_driver hdmi_frame_buffer = {
|
||||
.probe = hdmifb_probe,
|
||||
.driver = {.name = "msm_hdmi"},
|
||||
};
|
||||
|
||||
static int hdmifb_add_mdp_device(struct device *dev,
|
||||
struct class_interface *class_intf)
|
||||
{
|
||||
/* might need locking if mulitple mdp devices */
|
||||
if (mdp)
|
||||
return 0;
|
||||
mdp = container_of(dev, struct mdp_device, dev);
|
||||
return platform_driver_register(&hdmi_frame_buffer);
|
||||
}
|
||||
|
||||
static void hdmifb_remove_mdp_device(struct device *dev,
|
||||
struct class_interface *class_intf)
|
||||
{
|
||||
/* might need locking if mulitple mdp devices */
|
||||
if (dev != &mdp->dev)
|
||||
return;
|
||||
platform_driver_unregister(&hdmi_frame_buffer);
|
||||
mdp = NULL;
|
||||
}
|
||||
|
||||
static struct class_interface hdmi_fb_interface = {
|
||||
.add_dev = &hdmifb_add_mdp_device,
|
||||
.remove_dev = &hdmifb_remove_mdp_device,
|
||||
};
|
||||
|
||||
static int hdmifb_add_hdmi_device(struct device *dev,
|
||||
struct class_interface *class_intf)
|
||||
{
|
||||
dev_dbg(dev, "%s\n", __func__);
|
||||
|
||||
if (hdmi)
|
||||
return 0;
|
||||
hdmi = container_of(dev, struct hdmi_device, dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct class_interface hdmi_interface = {
|
||||
.add_dev = hdmifb_add_hdmi_device,
|
||||
};
|
||||
|
||||
static int __init hdmifb_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = register_mdp_client(&hdmi_fb_interface);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = register_hdmi_client(&hdmi_interface);
|
||||
if (rc)
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(hdmifb_init);
|
633
drivers/video/msm/hdmi/hdmi_lcdc.c
Normal file
633
drivers/video/msm/hdmi/hdmi_lcdc.c
Normal file
@ -0,0 +1,633 @@
|
||||
/* drivers/video/msm/hdmi_lcdc.c
|
||||
*
|
||||
* Copyright (c) 2009 Google Inc.
|
||||
* Copyright (c) 2009 HTC
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <asm/mach-types.h>
|
||||
|
||||
#include <mach/msm_fb.h>
|
||||
|
||||
#include "../mdp_hw.h"
|
||||
#include "../../../../arch/arm/mach-msm/proc_comm.h"
|
||||
#include "../../../../arch/arm/mach-msm/clock.h"
|
||||
|
||||
|
||||
#if 1
|
||||
#define HDMI_DBG(s...) printk("[hdmi/lcdc]" s)
|
||||
#else
|
||||
#define HDMI_DBG(s...) do {} while (0)
|
||||
#endif
|
||||
|
||||
struct mdp_lcdc_info {
|
||||
struct mdp_info *mdp;
|
||||
struct clk *mdp_clk;
|
||||
struct clk *pclk;
|
||||
struct clk *pad_pclk;
|
||||
struct msm_panel_data fb_panel_data;
|
||||
struct platform_device fb_pdev;
|
||||
struct msm_lcdc_platform_data *pdata;
|
||||
uint32_t fb_start;
|
||||
|
||||
struct msmfb_callback frame_start_cb;
|
||||
wait_queue_head_t vsync_waitq;
|
||||
int got_vsync;
|
||||
|
||||
struct {
|
||||
uint32_t clk_rate;
|
||||
uint32_t hsync_ctl;
|
||||
uint32_t vsync_period;
|
||||
uint32_t vsync_pulse_width;
|
||||
uint32_t display_hctl;
|
||||
uint32_t display_vstart;
|
||||
uint32_t display_vend;
|
||||
uint32_t hsync_skew;
|
||||
uint32_t polarity;
|
||||
} parms;
|
||||
atomic_t blank_count;
|
||||
struct mutex blank_lock;
|
||||
};
|
||||
struct mdp_lcdc_info *_lcdc;
|
||||
|
||||
static struct mdp_device *mdp_dev;
|
||||
|
||||
#define panel_to_lcdc(p) container_of((p), struct mdp_lcdc_info, fb_panel_data)
|
||||
|
||||
/* FIXME: arrange the clock manipulating to proper place,
|
||||
integrate with the counter of fb_hdmi
|
||||
*/
|
||||
int lcdc_enable_video(void)
|
||||
{
|
||||
//struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
|
||||
struct mdp_lcdc_info *lcdc = _lcdc;
|
||||
struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops;
|
||||
|
||||
mutex_lock(&lcdc->blank_lock);
|
||||
if (atomic_read(&lcdc->blank_count))
|
||||
goto end_enable_video;
|
||||
HDMI_DBG("%s: enable clocks\n", __func__);
|
||||
clk_enable(lcdc->mdp_clk);
|
||||
clk_enable(lcdc->pclk);
|
||||
clk_enable(lcdc->pad_pclk);
|
||||
|
||||
/* TODO: need pre-test to see if it make any influence to HDCP,
|
||||
* if ebi1_clk doesn't enabled here.
|
||||
*/
|
||||
//panel_ops->unblank(panel_ops);
|
||||
|
||||
mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN);
|
||||
atomic_inc(&lcdc->blank_count);
|
||||
HDMI_DBG("%s, blank_count=%d\n", __func__,
|
||||
atomic_read(&lcdc->blank_count));
|
||||
end_enable_video:
|
||||
mutex_unlock(&lcdc->blank_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lcdc_disable_video(void)
|
||||
{
|
||||
struct mdp_lcdc_info *lcdc = _lcdc;
|
||||
struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops;
|
||||
|
||||
mutex_lock(&lcdc->blank_lock);
|
||||
if (atomic_read(&lcdc->blank_count) == 0)
|
||||
goto disable_video_done;
|
||||
if (atomic_dec_return(&lcdc->blank_count) == 0) {
|
||||
HDMI_DBG("%s: disable clocks\n", __func__);
|
||||
panel_ops->blank(panel_ops);
|
||||
mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN);
|
||||
clk_disable(lcdc->pclk);
|
||||
clk_disable(lcdc->pad_pclk);
|
||||
clk_disable(lcdc->mdp_clk);
|
||||
}
|
||||
disable_video_done:
|
||||
mutex_unlock(&lcdc->blank_lock);
|
||||
HDMI_DBG("%s, blank_count=%d\n", __func__,
|
||||
atomic_read(&lcdc->blank_count));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lcdc_unblank(struct msm_panel_data *fb_panel)
|
||||
{
|
||||
struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
|
||||
struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops;
|
||||
|
||||
HDMI_DBG("%s\n", __func__);
|
||||
|
||||
#if 0
|
||||
HDMI_DBG("%s: enable clocks\n", __func__);
|
||||
clk_enable(lcdc->mdp_clk);
|
||||
clk_enable(lcdc->pclk);
|
||||
clk_enable(lcdc->pad_pclk);
|
||||
|
||||
panel_ops->unblank(panel_ops);
|
||||
|
||||
mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN);
|
||||
atomic_set(&lcdc->blank_count, 1);
|
||||
#else
|
||||
lcdc_enable_video();
|
||||
/* TODO: need pre-test to see if it make any influence to HDCP,
|
||||
* if ebi1_clk enabled here.
|
||||
*/
|
||||
panel_ops->unblank(panel_ops);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lcdc_blank(struct msm_panel_data *fb_panel)
|
||||
{
|
||||
struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
|
||||
//struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops;
|
||||
|
||||
#if 0
|
||||
mutex_lock(&lcdc->blank_lock);
|
||||
if (atomic_read(&lcdc->blank_count) == 0)
|
||||
goto blank_done;
|
||||
if (atomic_dec_return(&lcdc->blank_count) == 0) {
|
||||
HDMI_DBG("%s: disable clocks\n", __func__);
|
||||
panel_ops->blank(panel_ops);
|
||||
mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN);
|
||||
clk_disable(lcdc->pclk);
|
||||
clk_disable(lcdc->pad_pclk);
|
||||
clk_disable(lcdc->mdp_clk);
|
||||
}
|
||||
blank_done:
|
||||
mutex_unlock(&lcdc->blank_lock);
|
||||
HDMI_DBG("%s, blank_count=%d\n", __func__,
|
||||
atomic_read(&lcdc->blank_count));
|
||||
#else
|
||||
lcdc_disable_video();
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lcdc_suspend(struct msm_panel_data *fb_panel)
|
||||
{
|
||||
struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
|
||||
struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops;
|
||||
|
||||
//pr_info("%s: suspending\n", __func__);
|
||||
HDMI_DBG("%s\n", __func__);
|
||||
|
||||
if (panel_ops->uninit)
|
||||
panel_ops->uninit(panel_ops);
|
||||
lcdc_disable_video();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lcdc_resume(struct msm_panel_data *fb_panel)
|
||||
{
|
||||
struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
|
||||
struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops;
|
||||
|
||||
//pr_info("%s: resuming\n", __func__);
|
||||
HDMI_DBG("%s\n", __func__);
|
||||
|
||||
if (panel_ops->init) {
|
||||
if (panel_ops->init(panel_ops) < 0)
|
||||
printk(KERN_ERR "LCD init fail!\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
lcdc_adjust_timing(struct msm_panel_data *fb_panel,
|
||||
struct msm_lcdc_timing *timing, u32 xres, u32 yres)
|
||||
{
|
||||
struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
|
||||
unsigned int hsync_period;
|
||||
unsigned int hsync_start_x;
|
||||
unsigned int hsync_end_x;
|
||||
unsigned int vsync_period;
|
||||
unsigned int display_vstart;
|
||||
unsigned int display_vend;
|
||||
uint32_t dma_cfg;
|
||||
|
||||
clk_set_rate(lcdc->pclk, timing->clk_rate);
|
||||
clk_set_rate(lcdc->pad_pclk, timing->clk_rate);
|
||||
HDMI_DBG("%s, clk=%d, xres=%d, yres=%d,\n", __func__,
|
||||
clk_get_rate(lcdc->pclk), xres, yres);
|
||||
|
||||
hsync_period = (timing->hsync_pulse_width + timing->hsync_back_porch +
|
||||
xres + timing->hsync_front_porch);
|
||||
hsync_start_x = (timing->hsync_pulse_width + timing->hsync_back_porch);
|
||||
hsync_end_x = hsync_period - timing->hsync_front_porch - 1;
|
||||
|
||||
vsync_period = (timing->vsync_pulse_width + timing->vsync_back_porch +
|
||||
yres + timing->vsync_front_porch);
|
||||
vsync_period *= hsync_period;
|
||||
|
||||
display_vstart = timing->vsync_pulse_width + timing->vsync_back_porch;
|
||||
display_vstart *= hsync_period;
|
||||
display_vstart += timing->hsync_skew;
|
||||
|
||||
display_vend = timing->vsync_front_porch * hsync_period;
|
||||
display_vend = vsync_period - display_vend + timing->hsync_skew - 1;
|
||||
|
||||
/* register values we pre-compute at init time from the timing
|
||||
* information in the panel info */
|
||||
lcdc->parms.hsync_ctl = (((hsync_period & 0xfff) << 16) |
|
||||
(timing->hsync_pulse_width & 0xfff));
|
||||
lcdc->parms.vsync_period = vsync_period & 0xffffff;
|
||||
lcdc->parms.vsync_pulse_width = (timing->vsync_pulse_width *
|
||||
hsync_period) & 0xffffff;
|
||||
|
||||
lcdc->parms.display_hctl = (((hsync_end_x & 0xfff) << 16) |
|
||||
(hsync_start_x & 0xfff));
|
||||
lcdc->parms.display_vstart = display_vstart & 0xffffff;
|
||||
lcdc->parms.display_vend = display_vend & 0xffffff;
|
||||
lcdc->parms.hsync_skew = timing->hsync_skew & 0xfff;
|
||||
lcdc->parms.polarity = ((timing->hsync_act_low << 0) |
|
||||
(timing->vsync_act_low << 1) |
|
||||
(timing->den_act_low << 2));
|
||||
lcdc->parms.clk_rate = timing->clk_rate;
|
||||
|
||||
mdp_writel(lcdc->mdp, lcdc->parms.hsync_ctl, MDP_LCDC_HSYNC_CTL);
|
||||
mdp_writel(lcdc->mdp, lcdc->parms.vsync_period, MDP_LCDC_VSYNC_PERIOD);
|
||||
mdp_writel(lcdc->mdp, lcdc->parms.vsync_pulse_width,
|
||||
MDP_LCDC_VSYNC_PULSE_WIDTH);
|
||||
mdp_writel(lcdc->mdp, lcdc->parms.display_hctl, MDP_LCDC_DISPLAY_HCTL);
|
||||
mdp_writel(lcdc->mdp, lcdc->parms.display_vstart,
|
||||
MDP_LCDC_DISPLAY_V_START);
|
||||
mdp_writel(lcdc->mdp, lcdc->parms.display_vend, MDP_LCDC_DISPLAY_V_END);
|
||||
mdp_writel(lcdc->mdp, lcdc->parms.hsync_skew, MDP_LCDC_HSYNC_SKEW);
|
||||
|
||||
mdp_writel(lcdc->mdp, 0, MDP_LCDC_BORDER_CLR);
|
||||
mdp_writel(lcdc->mdp, 0x0, MDP_LCDC_UNDERFLOW_CTL);
|
||||
mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_HCTL);
|
||||
mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_START);
|
||||
mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_END);
|
||||
mdp_writel(lcdc->mdp, lcdc->parms.polarity, MDP_LCDC_CTL_POLARITY);
|
||||
printk("solomon: polarity=%04x\n", mdp_readl(lcdc->mdp, MDP_LCDC_CTL_POLARITY));
|
||||
|
||||
/* config the dma_p block that drives the lcdc data */
|
||||
mdp_writel(lcdc->mdp, lcdc->fb_start, MDP_DMA_P_IBUF_ADDR);
|
||||
mdp_writel(lcdc->mdp, (((yres & 0x7ff) << 16) |
|
||||
(xres & 0x7ff)),
|
||||
MDP_DMA_P_SIZE);
|
||||
/* TODO: pull in the bpp info from somewhere else? */
|
||||
mdp_writel(lcdc->mdp, xres * 2,
|
||||
MDP_DMA_P_IBUF_Y_STRIDE);
|
||||
mdp_writel(lcdc->mdp, 0, MDP_DMA_P_OUT_XY);
|
||||
|
||||
dma_cfg = (DMA_PACK_ALIGN_LSB |
|
||||
DMA_PACK_PATTERN_RGB |
|
||||
DMA_DITHER_EN);
|
||||
dma_cfg |= DMA_OUT_SEL_LCDC;
|
||||
dma_cfg |= DMA_IBUF_FORMAT_RGB565;
|
||||
dma_cfg |= DMA_DSTC0G_8BITS | DMA_DSTC1B_8BITS | DMA_DSTC2R_8BITS;
|
||||
|
||||
mdp_writel(lcdc->mdp, dma_cfg, MDP_DMA_P_CONFIG);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lcdc_hw_init(struct mdp_lcdc_info *lcdc)
|
||||
{
|
||||
struct msm_panel_data *fb_panel = &lcdc->fb_panel_data;
|
||||
uint32_t dma_cfg;
|
||||
unsigned int clk_id, clk_rate;
|
||||
|
||||
clk_enable(lcdc->mdp_clk);
|
||||
clk_enable(lcdc->pclk);
|
||||
clk_enable(lcdc->pad_pclk);
|
||||
|
||||
clk_set_rate(lcdc->pclk, lcdc->parms.clk_rate);
|
||||
clk_set_rate(lcdc->pad_pclk, lcdc->parms.clk_rate);
|
||||
printk(KERN_DEBUG "pclk = %ld, pad_pclk = %ld\n",
|
||||
clk_get_rate(lcdc->pclk),
|
||||
clk_get_rate(lcdc->pad_pclk));
|
||||
|
||||
/* write the lcdc params */
|
||||
mdp_writel(lcdc->mdp, lcdc->parms.hsync_ctl, MDP_LCDC_HSYNC_CTL);
|
||||
mdp_writel(lcdc->mdp, lcdc->parms.vsync_period, MDP_LCDC_VSYNC_PERIOD);
|
||||
mdp_writel(lcdc->mdp, lcdc->parms.vsync_pulse_width,
|
||||
MDP_LCDC_VSYNC_PULSE_WIDTH);
|
||||
mdp_writel(lcdc->mdp, lcdc->parms.display_hctl, MDP_LCDC_DISPLAY_HCTL);
|
||||
mdp_writel(lcdc->mdp, lcdc->parms.display_vstart,
|
||||
MDP_LCDC_DISPLAY_V_START);
|
||||
mdp_writel(lcdc->mdp, lcdc->parms.display_vend, MDP_LCDC_DISPLAY_V_END);
|
||||
mdp_writel(lcdc->mdp, lcdc->parms.hsync_skew, MDP_LCDC_HSYNC_SKEW);
|
||||
|
||||
mdp_writel(lcdc->mdp, 0, MDP_LCDC_BORDER_CLR);
|
||||
mdp_writel(lcdc->mdp, 0, MDP_LCDC_UNDERFLOW_CTL);
|
||||
mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_HCTL);
|
||||
mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_START);
|
||||
mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_END);
|
||||
mdp_writel(lcdc->mdp, lcdc->parms.polarity, MDP_LCDC_CTL_POLARITY);
|
||||
printk("solomon: polarity=%04x\n", mdp_readl(lcdc->mdp, MDP_LCDC_CTL_POLARITY));
|
||||
|
||||
/* config the dma_p block that drives the lcdc data */
|
||||
mdp_writel(lcdc->mdp, lcdc->fb_start, MDP_DMA_P_IBUF_ADDR);
|
||||
mdp_writel(lcdc->mdp, (((fb_panel->fb_data->yres & 0x7ff) << 16) |
|
||||
(fb_panel->fb_data->xres & 0x7ff)),
|
||||
MDP_DMA_P_SIZE);
|
||||
/* TODO: pull in the bpp info from somewhere else? */
|
||||
mdp_writel(lcdc->mdp, fb_panel->fb_data->xres * 2,
|
||||
MDP_DMA_P_IBUF_Y_STRIDE);
|
||||
mdp_writel(lcdc->mdp, 0, MDP_DMA_P_OUT_XY);
|
||||
|
||||
dma_cfg = (DMA_PACK_ALIGN_LSB |
|
||||
DMA_PACK_PATTERN_RGB |
|
||||
DMA_DITHER_EN);
|
||||
dma_cfg |= DMA_OUT_SEL_LCDC;
|
||||
dma_cfg |= DMA_IBUF_FORMAT_RGB565;
|
||||
dma_cfg |= DMA_DSTC0G_8BITS | DMA_DSTC1B_8BITS | DMA_DSTC2R_8BITS;
|
||||
|
||||
mdp_writel(lcdc->mdp, dma_cfg, MDP_DMA_P_CONFIG);
|
||||
|
||||
/* Send customized command to ARM9 for escalating DMA_P as tier-1
|
||||
* of AXI bus.
|
||||
* Ref: SR#272509
|
||||
*/
|
||||
clk_id = USB_PHY_CLK;
|
||||
clk_rate = 0x1;
|
||||
msm_proc_comm(PCOM_CLKCTL_RPC_MIN_RATE, &clk_id, &clk_rate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lcdc_wait_vsync(struct msm_panel_data *panel)
|
||||
{
|
||||
struct mdp_lcdc_info *lcdc = panel_to_lcdc(panel);
|
||||
int ret;
|
||||
|
||||
ret = wait_event_timeout(lcdc->vsync_waitq, lcdc->got_vsync, HZ / 2);
|
||||
if (ret == 0)
|
||||
pr_err("%s: timeout waiting for VSYNC\n", __func__);
|
||||
lcdc->got_vsync = 0;
|
||||
}
|
||||
|
||||
static void lcdc_request_vsync(struct msm_panel_data *fb_panel,
|
||||
struct msmfb_callback *vsync_cb)
|
||||
{
|
||||
struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
|
||||
|
||||
/* the vsync callback will start the dma */
|
||||
vsync_cb->func(vsync_cb);
|
||||
lcdc->got_vsync = 0;
|
||||
mdp_out_if_req_irq(mdp_dev, MSM_LCDC_INTERFACE, MDP_LCDC_FRAME_START,
|
||||
&lcdc->frame_start_cb);
|
||||
lcdc_wait_vsync(fb_panel);
|
||||
}
|
||||
|
||||
static void lcdc_clear_vsync(struct msm_panel_data *fb_panel)
|
||||
{
|
||||
struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
|
||||
lcdc->got_vsync = 0;
|
||||
mdp_out_if_req_irq(mdp_dev, MSM_LCDC_INTERFACE, 0, NULL);
|
||||
}
|
||||
|
||||
/* called in irq context with mdp lock held, when mdp gets the
|
||||
* MDP_LCDC_FRAME_START interrupt */
|
||||
static void lcdc_frame_start(struct msmfb_callback *cb)
|
||||
{
|
||||
struct mdp_lcdc_info *lcdc;
|
||||
|
||||
lcdc = container_of(cb, struct mdp_lcdc_info, frame_start_cb);
|
||||
|
||||
lcdc->got_vsync = 1;
|
||||
wake_up(&lcdc->vsync_waitq);
|
||||
}
|
||||
|
||||
static void lcdc_dma_start(void *priv, uint32_t addr, uint32_t stride,
|
||||
uint32_t width, uint32_t height, uint32_t x,
|
||||
uint32_t y)
|
||||
{
|
||||
struct mdp_lcdc_info *lcdc = priv;
|
||||
struct mdp_info *mdp = lcdc->mdp;
|
||||
|
||||
#if 0
|
||||
if (mdp->dma_config_dirty) {
|
||||
mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN);
|
||||
mdelay(20);
|
||||
mdp_dev->configure_dma(mdp_dev);
|
||||
mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN);
|
||||
}
|
||||
#endif
|
||||
mdp_writel(lcdc->mdp, stride, MDP_DMA_P_IBUF_Y_STRIDE);
|
||||
mdp_writel(lcdc->mdp, addr, MDP_DMA_P_IBUF_ADDR);
|
||||
}
|
||||
|
||||
static void precompute_timing_parms(struct mdp_lcdc_info *lcdc)
|
||||
{
|
||||
struct msm_lcdc_timing *timing = lcdc->pdata->timing;
|
||||
struct msm_fb_data *fb_data = lcdc->pdata->fb_data;
|
||||
unsigned int hsync_period;
|
||||
unsigned int hsync_start_x;
|
||||
unsigned int hsync_end_x;
|
||||
unsigned int vsync_period;
|
||||
unsigned int display_vstart;
|
||||
unsigned int display_vend;
|
||||
|
||||
hsync_period = (timing->hsync_pulse_width + timing->hsync_back_porch +
|
||||
fb_data->xres + timing->hsync_front_porch);
|
||||
hsync_start_x = (timing->hsync_pulse_width + timing->hsync_back_porch);
|
||||
hsync_end_x = hsync_period - timing->hsync_front_porch - 1;
|
||||
|
||||
vsync_period = (timing->vsync_pulse_width + timing->vsync_back_porch +
|
||||
fb_data->yres + timing->vsync_front_porch);
|
||||
vsync_period *= hsync_period;
|
||||
|
||||
display_vstart = timing->vsync_pulse_width + timing->vsync_back_porch;
|
||||
display_vstart *= hsync_period;
|
||||
display_vstart += timing->hsync_skew;
|
||||
|
||||
display_vend = timing->vsync_front_porch * hsync_period;
|
||||
display_vend = vsync_period - display_vend + timing->hsync_skew - 1;
|
||||
|
||||
/* register values we pre-compute at init time from the timing
|
||||
* information in the panel info */
|
||||
lcdc->parms.hsync_ctl = (((hsync_period & 0xfff) << 16) |
|
||||
(timing->hsync_pulse_width & 0xfff));
|
||||
lcdc->parms.vsync_period = vsync_period & 0xffffff;
|
||||
lcdc->parms.vsync_pulse_width = (timing->vsync_pulse_width *
|
||||
hsync_period) & 0xffffff;
|
||||
|
||||
lcdc->parms.display_hctl = (((hsync_end_x & 0xfff) << 16) |
|
||||
(hsync_start_x & 0xfff));
|
||||
lcdc->parms.display_vstart = display_vstart & 0xffffff;
|
||||
lcdc->parms.display_vend = display_vend & 0xffffff;
|
||||
lcdc->parms.hsync_skew = timing->hsync_skew & 0xfff;
|
||||
lcdc->parms.polarity = ((timing->hsync_act_low << 0) |
|
||||
(timing->vsync_act_low << 1) |
|
||||
(timing->den_act_low << 2));
|
||||
lcdc->parms.clk_rate = timing->clk_rate;
|
||||
}
|
||||
|
||||
static int hdmi_lcdc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct msm_lcdc_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct mdp_lcdc_info *lcdc;
|
||||
int ret = 0;
|
||||
|
||||
printk(KERN_DEBUG "%s\n", __func__);
|
||||
|
||||
if (!pdata) {
|
||||
pr_err("%s: no LCDC platform data found\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
_lcdc = lcdc = kzalloc(sizeof(struct mdp_lcdc_info), GFP_KERNEL);
|
||||
if (!lcdc)
|
||||
return -ENOMEM;
|
||||
|
||||
/* We don't actually own the clocks, the mdp does. */
|
||||
lcdc->mdp_clk = clk_get(mdp_dev->dev.parent, "mdp_clk");
|
||||
if (IS_ERR(lcdc->mdp_clk)) {
|
||||
pr_err("%s: failed to get mdp_clk\n", __func__);
|
||||
ret = PTR_ERR(lcdc->mdp_clk);
|
||||
goto err_get_mdp_clk;
|
||||
}
|
||||
|
||||
lcdc->pclk = clk_get(mdp_dev->dev.parent, "lcdc_pclk_clk");
|
||||
if (IS_ERR(lcdc->pclk)) {
|
||||
pr_err("%s: failed to get lcdc_pclk\n", __func__);
|
||||
ret = PTR_ERR(lcdc->pclk);
|
||||
goto err_get_pclk;
|
||||
}
|
||||
|
||||
lcdc->pad_pclk = clk_get(mdp_dev->dev.parent, "lcdc_pad_pclk_clk");
|
||||
if (IS_ERR(lcdc->pad_pclk)) {
|
||||
pr_err("%s: failed to get lcdc_pad_pclk\n", __func__);
|
||||
ret = PTR_ERR(lcdc->pad_pclk);
|
||||
goto err_get_pad_pclk;
|
||||
}
|
||||
|
||||
init_waitqueue_head(&lcdc->vsync_waitq);
|
||||
mutex_init(&lcdc->blank_lock);
|
||||
lcdc->pdata = pdata;
|
||||
lcdc->frame_start_cb.func = lcdc_frame_start;
|
||||
|
||||
platform_set_drvdata(pdev, lcdc);
|
||||
|
||||
mdp_out_if_register(mdp_dev, MSM_LCDC_INTERFACE, lcdc, MDP_DMA_P_DONE,
|
||||
lcdc_dma_start);
|
||||
|
||||
precompute_timing_parms(lcdc);
|
||||
|
||||
lcdc->fb_start = pdata->fb_resource->start;
|
||||
lcdc->mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
|
||||
|
||||
lcdc->fb_panel_data.suspend = lcdc_suspend;
|
||||
lcdc->fb_panel_data.resume = lcdc_resume;
|
||||
lcdc->fb_panel_data.wait_vsync = lcdc_wait_vsync;
|
||||
lcdc->fb_panel_data.request_vsync = lcdc_request_vsync;
|
||||
lcdc->fb_panel_data.clear_vsync = lcdc_clear_vsync;
|
||||
lcdc->fb_panel_data.blank = lcdc_blank;
|
||||
lcdc->fb_panel_data.unblank = lcdc_unblank;
|
||||
lcdc->fb_panel_data.adjust_timing = lcdc_adjust_timing;
|
||||
lcdc->fb_panel_data.fb_data = pdata->fb_data;
|
||||
lcdc->fb_panel_data.interface_type = MSM_LCDC_INTERFACE;
|
||||
|
||||
ret = lcdc_hw_init(lcdc);
|
||||
atomic_set(&lcdc->blank_count, 1);
|
||||
if (ret) {
|
||||
pr_err("%s: Cannot initialize the mdp_lcdc\n", __func__);
|
||||
goto err_hw_init;
|
||||
}
|
||||
|
||||
lcdc->fb_pdev.name = "msm_hdmi";
|
||||
lcdc->fb_pdev.id = pdata->fb_id;
|
||||
lcdc->fb_pdev.resource = pdata->fb_resource;
|
||||
lcdc->fb_pdev.num_resources = 1;
|
||||
lcdc->fb_pdev.dev.platform_data = &lcdc->fb_panel_data;
|
||||
|
||||
ret = platform_device_register(&lcdc->fb_pdev);
|
||||
if (ret) {
|
||||
pr_err("%s: Cannot register msm_panel pdev\n", __func__);
|
||||
goto err_plat_dev_reg;
|
||||
}
|
||||
|
||||
pr_info("%s: initialized\n", __func__);
|
||||
|
||||
return 0;
|
||||
|
||||
err_plat_dev_reg:
|
||||
err_hw_init:
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
clk_put(lcdc->pad_pclk);
|
||||
err_get_pad_pclk:
|
||||
clk_put(lcdc->pclk);
|
||||
err_get_pclk:
|
||||
clk_put(lcdc->mdp_clk);
|
||||
err_get_mdp_clk:
|
||||
kfree(lcdc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hdmi_lcdc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mdp_lcdc_info *lcdc = platform_get_drvdata(pdev);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
clk_put(lcdc->pclk);
|
||||
clk_put(lcdc->pad_pclk);
|
||||
kfree(lcdc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver mdp_lcdc_driver = {
|
||||
.probe = hdmi_lcdc_probe,
|
||||
.remove = hdmi_lcdc_remove,
|
||||
.driver = {
|
||||
.name = "msm_mdp_hdmi",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int mdp_lcdc_add_mdp_device(struct device *dev,
|
||||
struct class_interface *class_intf)
|
||||
{
|
||||
/* might need locking if mulitple mdp devices */
|
||||
if (mdp_dev)
|
||||
return 0;
|
||||
mdp_dev = container_of(dev, struct mdp_device, dev);
|
||||
return platform_driver_register(&mdp_lcdc_driver);
|
||||
}
|
||||
|
||||
static void mdp_lcdc_remove_mdp_device(struct device *dev,
|
||||
struct class_interface *class_intf)
|
||||
{
|
||||
/* might need locking if mulitple mdp devices */
|
||||
if (dev != &mdp_dev->dev)
|
||||
return;
|
||||
platform_driver_unregister(&mdp_lcdc_driver);
|
||||
mdp_dev = NULL;
|
||||
}
|
||||
|
||||
static struct class_interface mdp_lcdc_interface = {
|
||||
.add_dev = &mdp_lcdc_add_mdp_device,
|
||||
.remove_dev = &mdp_lcdc_remove_mdp_device,
|
||||
};
|
||||
|
||||
static int __init mdp_lcdc_init(void)
|
||||
{
|
||||
return register_mdp_client(&mdp_lcdc_interface);
|
||||
}
|
||||
|
||||
module_init(mdp_lcdc_init);
|
82
drivers/video/msm/hdmi/include/edid.h
Normal file
82
drivers/video/msm/hdmi/include/edid.h
Normal file
@ -0,0 +1,82 @@
|
||||
#ifndef _EDID_H_
|
||||
#define _EDID_H_
|
||||
|
||||
#define MAX_VIDEO_MODES 32
|
||||
struct edid_info_struct {
|
||||
bool is_valid;
|
||||
struct mutex access_lock;
|
||||
bool under_scan;
|
||||
bool basic_audio;
|
||||
bool ycbcr_4_4_4;
|
||||
bool ycbcr_4_2_2;
|
||||
bool hdmi_sink;
|
||||
};
|
||||
|
||||
#define EDID_BLOCK_SIZE 128
|
||||
#define EDID_HDR_NO_OF_FF 0x06
|
||||
|
||||
#define EDID_BLOCK_0_OFFSET 0x00
|
||||
#define EDID_BLOCK_1_OFFSET 0x80
|
||||
#define EDID_BLOCK_SIZE 128
|
||||
#define EDID_HDR_NO_OF_FF 0x06
|
||||
#define NUM_OF_EXTEN_ADDR 0x7E
|
||||
|
||||
#define EDID_TAG_ADDR 0x00
|
||||
#define EDID_REV_ADDR 0x01
|
||||
#define EDID_TAG_IDX 0x02
|
||||
#define LONG_DESCR_PTR_IDX 0x02
|
||||
#define MISC_SUPPORT_IDX 0x03
|
||||
|
||||
#define ESTABLISHED_TIMING_INDEX 35
|
||||
#define NUM_OF_STANDARD_TIMINGS 8
|
||||
#define STANDARD_TIMING_OFFSET 38
|
||||
#define LONG_DESCR_LENi 18
|
||||
#define NUM_OF_DETAILED_DESCRIPTORS 4
|
||||
|
||||
#define DETAILED_TIMING_OFFSET 0x36
|
||||
|
||||
/* Offsets within a Long Descriptors Block */
|
||||
#define PIX_CLK_OFFSET 0
|
||||
#define H_ACTIVE_OFFSET 2
|
||||
#define H_BLANKING_OFFSET 3
|
||||
#define V_ACTIVE_OFFSET 5
|
||||
#define V_BLANKING_OFFSET 6
|
||||
#define H_SYNC_OFFSET 8
|
||||
#define H_SYNC_PW_OFFSET 9
|
||||
#define V_SYNC_OFFSET 10
|
||||
#define V_SYNC_PW_OFFSET 10
|
||||
#define H_IMAGE_SIZE_OFFSET 12
|
||||
#define V_IMAGE_SIZE_OFFSET 13
|
||||
#define H_BORDER_OFFSET 15
|
||||
#define V_BORDER_OFFSET 16
|
||||
#define FLAGS_OFFSET 17
|
||||
|
||||
#define AR16_10 0
|
||||
#define AR4_3 1
|
||||
#define AR5_4 2
|
||||
#define AR16_9 3
|
||||
|
||||
#define EDID_EXTENSION_TAG 0x02
|
||||
#define EDID_REV_THREE 0x03
|
||||
#define EDID_DATA_START 0x04
|
||||
|
||||
#define EDID_BLOCK_0 0x00
|
||||
#define EDID_BLOCK_2_3 0x01
|
||||
|
||||
#define AUDIO_DESCR_SIZE 3
|
||||
|
||||
/* Data Block Tag Codes */
|
||||
#define AUDIO_D_BLOCK 0x01
|
||||
#define VIDEO_D_BLOCK 0x02
|
||||
#define VENDOR_SPEC_D_BLOCK 0x03
|
||||
#define SPKR_ALLOC_D_BLOCK 0x04
|
||||
#define USE_EXTENDED_TAG 0x07
|
||||
|
||||
/* Extended Data Block Tag Codes */
|
||||
#define COLORIMETRY_D_BLOCK 0x05
|
||||
|
||||
#define VIDEO_CAPABILITY_D_BLOCK 0x00
|
||||
#define HDMI_SIGNATURE_LEN 0x03
|
||||
|
||||
#define CEC_PHYS_ADDR_LEN 0x02
|
||||
#endif
|
27
drivers/video/msm/hdmi/include/fb-hdmi.h
Normal file
27
drivers/video/msm/hdmi/include/fb-hdmi.h
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef _FB_HDMI_H_
|
||||
#define _FB_HDMI_H_
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/fb.h>
|
||||
|
||||
enum hd_res {
|
||||
hd_720p = 0, /* 1280 * 720 */
|
||||
svga, /* 800 * 600 */
|
||||
pal, /* 720 * 576 */
|
||||
edtv, /* 720 * 480 */
|
||||
vga, /* 640 * 480 */
|
||||
};
|
||||
|
||||
struct msm_lcdc_timing;
|
||||
struct hdmi_device {
|
||||
struct device dev;
|
||||
int (*check_res)(struct hdmi_device *, struct fb_var_screeninfo *);
|
||||
struct msm_lcdc_timing *(*set_res)(struct hdmi_device *,
|
||||
struct fb_var_screeninfo *);
|
||||
int (*get_cable_state)(struct hdmi_device *, int *);
|
||||
int (*get_establish_timing)(struct hdmi_device *, u8 *);
|
||||
};
|
||||
|
||||
struct class_interface;
|
||||
int register_hdmi_client(struct class_interface *class_intf);
|
||||
#endif
|
399
drivers/video/msm/hdmi/include/sil902x.h
Normal file
399
drivers/video/msm/hdmi/include/sil902x.h
Normal file
@ -0,0 +1,399 @@
|
||||
#ifndef __SIL902X_H_
|
||||
#define __SIL902X_H_
|
||||
|
||||
#include <mach/msm_fb.h>
|
||||
#include "edid.h"
|
||||
|
||||
struct hdmi_info {
|
||||
struct hdmi_device hdmi_dev;
|
||||
struct i2c_client *client;
|
||||
struct msm_lcdc_panel_ops hdmi_lcdc_ops;
|
||||
struct work_struct work;
|
||||
struct delayed_work hdmi_delay_work;
|
||||
struct mutex lock;
|
||||
struct mutex lock2;
|
||||
struct clk *ebi1_clk;
|
||||
int (*power)(int on);
|
||||
void (*hdmi_gpio_on)(void);
|
||||
void (*hdmi_gpio_off)(void);
|
||||
enum hd_res res;
|
||||
// FIXME: move to edid_info_struct
|
||||
u8 edid_buf[128 * 4];
|
||||
enum {
|
||||
SLEEP,
|
||||
AWAKE,
|
||||
} sleeping;
|
||||
bool polling;
|
||||
bool cable_connected;
|
||||
bool isr_enabled;
|
||||
bool first;
|
||||
struct completion hotplug_completion;
|
||||
struct timer_list timer;
|
||||
struct work_struct polling_work;
|
||||
struct dentry *debug_dir;
|
||||
struct edid_info_struct edid_info;
|
||||
struct mutex polling_lock;
|
||||
bool suspending;
|
||||
bool user_playing;
|
||||
bool video_streaming;
|
||||
};
|
||||
|
||||
enum {
|
||||
HDMI_PIXEL_DATA = 0x08,
|
||||
HDMI_AVI_INFO_FRAME = 0x0c,
|
||||
HDMI_AUDIO_INFO_FRAME = 0xbf,
|
||||
HDMI_SYS_CTL = 0x1a,
|
||||
HDMI_POWER = 0x1e,
|
||||
HDMI_IDENTIFY = 0x1b,
|
||||
HDMI_INT_EN = 0x3c,
|
||||
HDMI_INT_STAT = 0x3d,
|
||||
HDMI_EN_REG = 0xc7,
|
||||
};
|
||||
|
||||
/* flag bitmap for register HDMI_INT_STAT */
|
||||
enum {
|
||||
HOT_PLUG_PENDING = (1U << 0),
|
||||
RX_PENDING = (1U << 1),
|
||||
HOT_PLUG_STATE = (1U << 2),
|
||||
RX_STATE = (1U << 3),
|
||||
AUDIO_ERR = (1U << 4),
|
||||
SECURITY_STATE = (1U << 5),
|
||||
HDCP_VALUE = (1U << 6),
|
||||
HDCP_AUTH = (1U << 7),
|
||||
};
|
||||
|
||||
enum ErrorMessages {
|
||||
INIT_SYSTEM_SUCCESSFUL, // 0
|
||||
|
||||
BLACK_BOX_OPEN_FAILURE,
|
||||
BLACK_BOX_OPENED_SUCCESSFULLY,
|
||||
HW_RESET_FAILURE,
|
||||
TPI_ENABLE_FAILURE,
|
||||
INTERRUPT_EN_FAILURE,
|
||||
INTERRUPT_POLLING_FAILED,
|
||||
|
||||
NO_SINK_CONNECTED,
|
||||
DDC_BUS_REQ_FAILURE,
|
||||
HDCP_FAILURE,
|
||||
HDCP_OK, // 10
|
||||
RX_AUTHENTICATED,
|
||||
SINK_DOES_NOT_SUPPORT_HDCP,
|
||||
TX_DOES_NOT_SUPPORT_HDCP,
|
||||
|
||||
ILLEGAL_AKSV,
|
||||
SET_PROTECTION_FAILURE,
|
||||
REVOKED_KEYS_FOUND,
|
||||
REPEATER_AUTHENTICATED,
|
||||
INT_STATUS_READ_FAILURE,
|
||||
|
||||
PROTECTION_OFF_FAILED,
|
||||
PROTECTION_ON_FAILED, // 20
|
||||
INTERRUPT_POLLING_OK,
|
||||
|
||||
EDID_PARSING_FAILURE,
|
||||
VIDEO_SETUP_FAILURE,
|
||||
TPI_READ_FAILURE,
|
||||
TPI_WRITE_FAILURE,
|
||||
|
||||
INIT_VIDEO_FAILURE,
|
||||
DE_CANNOT_BE_SET_WITH_EMBEDDED_SYNC,
|
||||
SET_EMBEDDED_SYC_FAILURE,
|
||||
V_MODE_NOT_SUPPORTED,
|
||||
|
||||
AUD_MODE_NOT_SUPPORTED, // 30
|
||||
I2S_NOT_SET,
|
||||
|
||||
EDID_READ_FAILURE,
|
||||
EDID_CHECKSUM_ERROR,
|
||||
INCORRECT_EDID_HEADER,
|
||||
EDID_EXT_TAG_ERROR,
|
||||
|
||||
EDID_REV_ADDR_ERROR,
|
||||
EDID_V_DESCR_OVERFLOW,
|
||||
INCORRECT_EDID_FILE,
|
||||
UNKNOWN_EDID_TAG_CODE,
|
||||
NO_DETAILED_DESCRIPTORS_IN_THIS_EDID_BLOCK, // 40
|
||||
CONFIG_DATA_VALID,
|
||||
CONFIG_DATA_INVALID,
|
||||
|
||||
GPIO_ACCESS_FAILED,
|
||||
GPIO_CONFIG_ERROR,
|
||||
|
||||
HP_EVENT_GOING_TO_SERVICE_LOOP,
|
||||
EDID_PARSED_OK,
|
||||
VIDEO_MODE_SET_OK,
|
||||
AUDIO_MODE_SET_OK,
|
||||
|
||||
I2S_MAPPING_SUCCESSFUL,
|
||||
I2S_INPUT_CONFIG_SUCCESSFUL, // 50
|
||||
I2S_HEADER_SET_SUCCESSFUL,
|
||||
INTERRUPT_POLLING_SUCCESSFUL,
|
||||
|
||||
HPD_LOOP_EXITED_SUCCESSFULY,
|
||||
HPD_LOOP_FAILED,
|
||||
SINK_CONNECTED,
|
||||
HP_EVENT_RETURNING_FROM_SERVICE_LOOP,
|
||||
AVI_INFOFRAMES_SETTING_FAILED,
|
||||
|
||||
TMDS_ENABLING_FAILED,
|
||||
DE_SET_OK,
|
||||
DE_SET_FAILED,
|
||||
NO_861_EXTENSIONS,
|
||||
|
||||
GO_OK,
|
||||
IMAGE_PKTS_UPDATED_OK,
|
||||
MONITORING_BLOCKED,
|
||||
LINK_NORMAL,
|
||||
LINK_LOST,
|
||||
RENEGOTIATION_REQUIRED,
|
||||
|
||||
LINK_SUSPENDED,
|
||||
EDID_SHORT_DESCRIPTORS_PARSED_OK,
|
||||
EDID_LONG_DESCRIPTORS_PARSED_OK,
|
||||
DDC_BUS_RELEASE_FAILURE,
|
||||
FAILED_GETTING_BKSV,
|
||||
|
||||
PLL_SETUP_FAILUE,
|
||||
ERR_RX_QUEUE_FULL,
|
||||
ERR_TX_QUEUE_FULL,
|
||||
GBD_SET_SUCCESSFULLY,
|
||||
BACKDOOR_SETTING_FAILED,
|
||||
ERR_TX_QUEUE_EMPTY
|
||||
};
|
||||
|
||||
#define BIT_0 0x01
|
||||
#define BIT_1 0x02
|
||||
#define BIT_2 0x04
|
||||
#define BIT_3 0x08
|
||||
#define BIT_4 0x10
|
||||
#define BIT_5 0x20
|
||||
#define BIT_6 0x40
|
||||
#define BIT_7 0x80
|
||||
|
||||
#define INTERVAL_HDCP_POLLING (HZ / 25)
|
||||
|
||||
#define REQUEST_RELEASE_DDC_BEFORE_HDCP
|
||||
#define T_HDCP_ACTIVATION 500
|
||||
#define T_HDCP_DEACTIVATION 200
|
||||
|
||||
#define T_HPD_DELAY 10
|
||||
#define TPI_INTERRUPT_EN 0x3c
|
||||
#define ALL 0xff
|
||||
#define DUMMY 0xFD
|
||||
|
||||
#define SiI_DEVICE_ID 0xB0
|
||||
|
||||
#define T_DDC_ACCESS 50
|
||||
// TPI Control Masks
|
||||
// =================
|
||||
#define BIT_OUTPUT_MODE 0x01
|
||||
#define BIT_DDC_BUS_GRANT 0x02
|
||||
#define BIT_DDC_BUS_REQ 0x04
|
||||
#define BIT_TMDS_OUTPUT 0x10
|
||||
|
||||
#define TPI_INTERNAL_PAGE_REG 0xBC
|
||||
#define TPI_REGISTER_OFFSET_REG 0xBD
|
||||
#define TPI_REGISTER_VALUE_REG 0xBE
|
||||
|
||||
/* HDCP Control Masks */
|
||||
#define BIT_PROTECT_LEVEL 0x01
|
||||
#define BIT_PROTECT_TYPE 0x02
|
||||
#define BIT_REPEATER 0x08
|
||||
#define BIT_LOCAL_PROTECT 0x40
|
||||
#define BIT_EXT_PROTECT 0x80
|
||||
|
||||
#define BITS_LINK_LOST 0x10
|
||||
#define BITS_RENEGOTIATION 0x20
|
||||
|
||||
#define BIT_TMDS_OUTPUT 0x10
|
||||
|
||||
#define BIT_AUDIO_MUTE 0x10
|
||||
|
||||
#define TPI_HDCP_REVISION_DATA_REG (0x30)
|
||||
#define HDCP_MAJOR_REVISION_MASK (BIT_7 | BIT_6 | BIT_5 | BIT_4)
|
||||
#define HDCP_MAJOR_REVISION_VALUE (0x10)
|
||||
|
||||
#define HDCP_MINOR_REVISION_MASK (BIT_3 | BIT_2 | BIT_1 | BIT_0)
|
||||
#define HDCP_MINOR_REVISION_VALUE (0x02)
|
||||
|
||||
#define HDCP_REVISION 0x12
|
||||
#define SET_PROT_ATTEMPTS 0x05
|
||||
|
||||
#define AKSV_SIZE 5
|
||||
#define BYTE_SIZE 8
|
||||
#define NUM_OF_ONES_IN_KSV 20
|
||||
|
||||
// Interrupt Masks
|
||||
//================
|
||||
#define HOT_PLUG_EVENT 0x01
|
||||
#define RX_SENSE_EVENT 0x02
|
||||
#define TPI_HOT_PLUG_STATE 0x04
|
||||
#define RX_SENSE_STATE 0x08
|
||||
|
||||
#define AUDIO_ERROR_EVENT 0x10
|
||||
#define SECURITY_CHANGE_EVENT 0x20
|
||||
#define V_READY_EVENT 0x40
|
||||
#define HDCP_CHANGE_EVENT 0x80
|
||||
|
||||
#define NON_MASKABLE_INT 0xFF
|
||||
|
||||
/* Protection Levels */
|
||||
#define NO_PROTECTION 0x00
|
||||
#define LOCAL_PROTECTION 0x01
|
||||
#define EXTENDED_PROTECTION 0x03
|
||||
|
||||
#define LINK_NORMAL 0
|
||||
#define MAX_V_DESCRIPTORS 20
|
||||
#define MAX_A_DESCRIPTORS 10
|
||||
#define MAX_SPEAKER_CONFIGURATIONS 4
|
||||
|
||||
#define HDMI_DEBUGFS_ROOT "hdmi"
|
||||
|
||||
/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ///
|
||||
/*\
|
||||
| | HDCP Implementation
|
||||
| |
|
||||
| | HDCP link security logic is implemented in certain transmitters; unique
|
||||
| | keys are embedded in each chip as part of the solution. The security
|
||||
| | scheme is fully automatic and handled completely by the hardware.
|
||||
\*/
|
||||
/// HDCP Query Data Register ============================================== ///
|
||||
|
||||
#define TPI_HDCP_QUERY_DATA_REG (0x29)
|
||||
|
||||
#define EXTENDED_LINK_PROTECTION_MASK (BIT_7)
|
||||
#define EXTENDED_LINK_PROTECTION_NONE (0x00)
|
||||
#define EXTENDED_LINK_PROTECTION_SECURE (0x80)
|
||||
|
||||
#define LOCAL_LINK_PROTECTION_MASK (BIT_6)
|
||||
#define LOCAL_LINK_PROTECTION_NONE (0x00)
|
||||
#define LOCAL_LINK_PROTECTION_SECURE (0x40)
|
||||
|
||||
#define LINK_STATUS_MASK (BIT_5 | BIT_4)
|
||||
#define LINK_STATUS_NORMAL (0x00)
|
||||
#define LINK_STATUS_LINK_LOST (0x10)
|
||||
#define LINK_STATUS_RENEGOTIATION_REQ (0x20)
|
||||
#define LINK_STATUS_LINK_SUSPENDED (0x30)
|
||||
|
||||
#define HDCP_REPEATER_MASK (BIT_3)
|
||||
#define HDCP_REPEATER_NO (0x00)
|
||||
#define HDCP_REPEATER_YES (0x08)
|
||||
|
||||
#define CONNECTOR_TYPE_MASK (BIT_2 | BIT_0)
|
||||
#define CONNECTOR_TYPE_DVI (0x00)
|
||||
#define CONNECTOR_TYPE_RSVD (0x01)
|
||||
#define CONNECTOR_TYPE_HDMI (0x04)
|
||||
#define CONNECTOR_TYPE_FUTURE (0x05)
|
||||
|
||||
#define PROTECTION_TYPE_MASK (BIT_1)
|
||||
#define PROTECTION_TYPE_NONE (0x00)
|
||||
#define PROTECTION_TYPE_HDCP (0x02)
|
||||
|
||||
/// HDCP Control Data Register ============================================ ///
|
||||
|
||||
#define TPI_HDCP_CONTROL_DATA_REG (0x2A)
|
||||
|
||||
#define PROTECTION_LEVEL_MASK (BIT_0)
|
||||
#define PROTECTION_LEVEL_MIN (0x00)
|
||||
#define PROTECTION_LEVEL_MAX (0x01)
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
#if 0
|
||||
/* Caller: ChangeVideoMode(), HDCP_Poll(), HotPlugServiceLoop(), RestartHDCP()
|
||||
*/
|
||||
#define EnableTMDS(hdmi) ReadClearWriteTPI(hdmi, TPI_SYSTEM_CONTROL, BIT_TMDS_OUTPUT) // 0x1A[4] = 0
|
||||
|
||||
/* Caller: ChangeVideoMode(), HDCP_Poll(), TPI_Poll(), RestartHDCP(),
|
||||
* OnHdmiCableDisconnected()
|
||||
*/
|
||||
#define DisableTMDS(hdmi) ReadSetWriteTPI(hdmi, TPI_SYSTEM_CONTROL, BIT_TMDS_OUTPUT) // 0x1A[4] = 1
|
||||
#else
|
||||
|
||||
void EnableTMDS(struct hdmi_info *hdmi);
|
||||
void DisableTMDS(struct hdmi_info *hdmi);
|
||||
|
||||
#endif
|
||||
|
||||
// FIXME: fix the global variables
|
||||
extern u8 pvid_mode, vid_mode;
|
||||
extern u8 LinkProtectionLevel;
|
||||
extern u8 systemInitialized;
|
||||
/*---------------------------------------------------------------------------*/
|
||||
int hdmi_read(struct i2c_client *client, u8 cmd);
|
||||
int hdmi_write_byte(struct i2c_client *client, u8 reg, u8 val);
|
||||
int hdmi_enable_int(struct i2c_client *client);
|
||||
int hdmi_disable_int(struct i2c_client *client);
|
||||
int hdmi_read_edid(struct hdmi_info *info, struct i2c_client *client);
|
||||
int hdmi_standby(struct hdmi_info *hdmi);
|
||||
int hdmi_wakeup(struct hdmi_info *hdmi);
|
||||
int read_backdoor_register(struct hdmi_info *hdmi, u8 PageNum, u8 RegOffset);
|
||||
void ReadSetWriteTPI(struct hdmi_info *hdmi, u8 Offset, u8 Pattern);
|
||||
void ReadModifyWriteTPI(struct hdmi_info *hdmi, u8 Offset, u8 Mask, u8 Value);
|
||||
void tpi_clear_interrupt(struct hdmi_info *hdmi, u8 pattern);
|
||||
bool tpi_init(struct hdmi_info *hdmi);
|
||||
void ReadClearWriteTPI(struct hdmi_info *hdmi, u8 Offset, u8 Pattern);
|
||||
s32 ReadBlockTPI(struct hdmi_info *hdmi, u8 TPI_Offset, u16 NBytes, u8 *pData);
|
||||
bool IsRepeater(struct hdmi_info *hdmi);
|
||||
bool GetDDC_Access(struct hdmi_info *hdmi, u8* SysCtrlRegVal);
|
||||
bool ReleaseDDC(struct hdmi_info *hdmi, u8 SysCtrlRegVal);
|
||||
int tpi_read_backdoor_register(struct hdmi_info *hdmi, u8 PageNum, u8 RegOffset)
|
||||
;
|
||||
void tpi_write_backdoor_register(struct hdmi_info *hdmi, u8 PageNum, u8 RegOffset, u8 RegValue);
|
||||
|
||||
int HotPlugServiceLoop(struct hdmi_info *hdmi);
|
||||
|
||||
int hdmi_active9022(struct i2c_client *client);
|
||||
int hdmi_active9022_dup(struct i2c_client *client);
|
||||
bool avc_send_avi_info_frames(struct hdmi_info *hdmi);
|
||||
bool avc_init_video(struct hdmi_info *hdmi, u8 mode, u8 TclkSel, bool Init);
|
||||
//void hdcp_on(struct hdmi_info *hdmi);
|
||||
void hdcp_off(struct hdmi_info *hdmi);
|
||||
void hdcp_check_status(struct hdmi_info *hdmi, u8 InterruptStatusImage);
|
||||
void hdcp_init(struct hdmi_info *hdmi);
|
||||
int hdcp_debugfs_init(struct hdmi_info *hdmi);
|
||||
|
||||
extern u8 EDID_TempData[];
|
||||
u8 edid_simple_parsing(struct hdmi_info *hdmi);
|
||||
|
||||
int edid_dump_hex(u8 *src, int src_size, char *output, int output_size);
|
||||
bool edid_is_video_mode_supported(struct video_mode *vmode);
|
||||
int edid_debugfs_init(struct hdmi_info *hdmi);
|
||||
bool edid_check_sink_type(struct hdmi_info *hdmi);
|
||||
int HotPlugServiceLoop(struct hdmi_info *hdmi);
|
||||
int tpi_prepare(struct hdmi_info *hdmi);
|
||||
|
||||
//bool InitVideo(struct hdmi_info *hdmi, u8 Mode, u8 TclkSel, bool Init);
|
||||
void avc_set_basic_audio(struct hdmi_info *hdmi);
|
||||
|
||||
int hdmi_debugfs_init(struct hdmi_info *hdmi);
|
||||
int tpi_debugfs_init(struct hdmi_info *hdmi);
|
||||
ssize_t hdmi_dbgfs_open(struct inode *inode, struct file *file);
|
||||
|
||||
void SetAudioMute(struct hdmi_info *hdmi, u8 audioMute);
|
||||
void SetInputColorSpace(struct hdmi_info *hdmi, u8 inputColorSpace);
|
||||
|
||||
void WriteBackDoorRegister(struct hdmi_info *hdmi, u8 PageNum, u8 RegOffset, u8 RegValue);
|
||||
|
||||
#define TPI_INPUT_FORMAT 0x09
|
||||
#define TPI_OUTPUT_FORMAT 0x0A
|
||||
|
||||
#define TPI_SYSTEM_CONTROL 0x1A
|
||||
|
||||
#define TPI_DEVICE_POWER_STATE_CTRL_REG (0x1E)
|
||||
|
||||
#define CTRL_PIN_CONTROL_MASK (BIT_4)
|
||||
#define CTRL_PIN_TRISTATE (0x00)
|
||||
#define CTRL_PIN_DRIVEN_TX_BRIDGE (0x10)
|
||||
|
||||
#define TX_POWER_STATE_D0 (0x00)
|
||||
#define TX_POWER_STATE_D1 (0x01)
|
||||
#define TX_POWER_STATE_D2 (0x02)
|
||||
#define TX_POWER_STATE_D3 (0x03)
|
||||
|
||||
#define TPI_AUDIO_INTERFACE 0x26
|
||||
|
||||
#define TPI_HDCP_QUERY_DATA 0x29
|
||||
#define TPI_HDCP_CTRL 0x2A
|
||||
|
||||
#endif
|
411
drivers/video/msm/hdmi/include/tpi.h
Normal file
411
drivers/video/msm/hdmi/include/tpi.h
Normal file
@ -0,0 +1,411 @@
|
||||
#ifndef _TPI_H_
|
||||
#define _TPI_H_
|
||||
|
||||
#define TPI_PIX_CLK_LSB (0x00)
|
||||
#define TPI_PIX_CLK_MSB (0x01)
|
||||
|
||||
#define TPI_VERT_FREQ_LSB (0x02)
|
||||
#define TPI_VERT_FREQ_MSB (0x03)
|
||||
|
||||
#define TPI_TOTAL_PIX_LSB (0x04)
|
||||
#define TPI_TOTAL_PIX_MSB (0x05)
|
||||
|
||||
#define TPI_TOTAL_LINES_LSB (0x06)
|
||||
#define TPI_TOTAL_LINES_MSB (0x07)
|
||||
|
||||
// Pixel Repetition Data
|
||||
//======================
|
||||
|
||||
#define TPI_PIX_REPETITION (0x08)
|
||||
|
||||
// TPI AVI Input and Output Format Data
|
||||
//=====================================
|
||||
|
||||
/// AVI Input Format Data ================================================= ///
|
||||
|
||||
#define TPI_INPUT_FORMAT_REG (0x09)
|
||||
|
||||
//Finish this...
|
||||
|
||||
#define INPUT_COLOR_SPACE_MASK (BIT_1 | BIT_0)
|
||||
#define INPUT_COLOR_SPACE_RGB (0x00)
|
||||
#define INPUT_COLOR_SPACE_YCBCR444 (0x01)
|
||||
#define INPUT_COLOR_SPACE_YCBCR422 (0x02)
|
||||
#define INPUT_COLOR_SPACE_BLACK_MODE (0x03)
|
||||
|
||||
/// AVI Output Format Data ================================================ ///
|
||||
|
||||
#define TPI_OUTPUT_FORMAT_REG (0x0A)
|
||||
|
||||
#define TPI_YC_Input_Mode (0x0B)
|
||||
|
||||
// TPI AVI InfoFrame Data
|
||||
//=======================
|
||||
|
||||
#define TPI_AVI_BYTE_0 (0x0C)
|
||||
#define TPI_AVI_BYTE_1 (0x0D)
|
||||
#define TPI_AVI_BYTE_2 (0x0E)
|
||||
#define TPI_AVI_BYTE_3 (0x0F)
|
||||
#define TPI_AVI_BYTE_4 (0x10)
|
||||
#define TPI_AVI_BYTE_5 (0x11)
|
||||
|
||||
#define TPI_AUDIO_BYTE_0 (0xBF)
|
||||
|
||||
#define TPI_INFO_FRM_DBYTE5 0xC8
|
||||
#define TPI_INFO_FRM_DBYTE6 0xC9
|
||||
|
||||
#define TPI_END_TOP_BAR_LSB (0x12)
|
||||
#define TPI_END_TOP_BAR_MSB (0x13)
|
||||
|
||||
#define TPI_START_BTM_BAR_LSB (0x14)
|
||||
#define TPI_START_BTM_BAR_MSB (0x15)
|
||||
|
||||
#define TPI_END_LEFT_BAR_LSB (0x16)
|
||||
#define TPI_END_LEFT_BAR_MSB (0x17)
|
||||
|
||||
#define TPI_END_RIGHT_BAR_LSB (0x18)
|
||||
#define TPI_END_RIGHT_BAR_MSB (0x19)
|
||||
|
||||
// Colorimetry
|
||||
//============
|
||||
#define SET_EX_COLORIMETRY 0x0C // Set TPI_AVI_BYTE_2 to extended colorimetry and use
|
||||
//TPI_AVI_BYTE_3
|
||||
|
||||
// ===================================================== //
|
||||
|
||||
#define TPI_SYSTEM_CONTROL_DATA_REG (0x1A)
|
||||
|
||||
#define LINK_INTEGRITY_MODE_MASK (BIT_6)
|
||||
#define LINK_INTEGRITY_STATIC (0x00)
|
||||
#define LINK_INTEGRITY_DYNAMIC (0x40)
|
||||
|
||||
#define TMDS_OUTPUT_CONTROL_MASK (BIT_4)
|
||||
#define TMDS_OUTPUT_CONTROL_ACTIVE (0x00)
|
||||
#define TMDS_OUTPUT_CONTROL_POWER_DOWN (0x10)
|
||||
|
||||
#define AV_MUTE_MASK (BIT_3)
|
||||
#define AV_MUTE_NORMAL (0x00)
|
||||
#define AV_MUTE_MUTED (0x08)
|
||||
|
||||
#define DDC_BUS_REQUEST_MASK (BIT_2)
|
||||
#define DDC_BUS_REQUEST_NOT_USING (0x00)
|
||||
#define DDC_BUS_REQUEST_REQUESTED (0x04)
|
||||
|
||||
#define DDC_BUS_GRANT_MASK (BIT_1)
|
||||
#define DDC_BUS_GRANT_NOT_AVAILABLE (0x00)
|
||||
#define DDC_BUS_GRANT_GRANTED (0x02)
|
||||
|
||||
#define OUTPUT_MODE_MASK (BIT_0)
|
||||
#define OUTPUT_MODE_DVI (0x00)
|
||||
#define OUTPUT_MODE_HDMI (0x01)
|
||||
|
||||
|
||||
// TPI Identification Registers
|
||||
//=============================
|
||||
|
||||
#define TPI_DEVICE_ID (0x1B)
|
||||
#define TPI_DEVICE_REV_ID (0x1C)
|
||||
|
||||
#define TPI_RESERVED2 (0x1D)
|
||||
|
||||
// ===================================================== //
|
||||
|
||||
#define TPI_DEVICE_POWER_STATE_CTRL_REG (0x1E)
|
||||
|
||||
#define CTRL_PIN_CONTROL_MASK (BIT_4)
|
||||
#define CTRL_PIN_TRISTATE (0x00)
|
||||
#define CTRL_PIN_DRIVEN_TX_BRIDGE (0x10)
|
||||
|
||||
#define TX_POWER_STATE_MASK (BIT_1 | BIT_0)
|
||||
#define TX_POWER_STATE_D0 (0x00)
|
||||
#define TX_POWER_STATE_D1 (0x01)
|
||||
#define TX_POWER_STATE_D2 (0x02)
|
||||
#define TX_POWER_STATE_D3 (0x03)
|
||||
|
||||
// Configuration of I2S Interface
|
||||
//===============================
|
||||
|
||||
#define TPI_I2S_EN (0x1F)
|
||||
#define TPI_I2S_IN_CFG (0x20)
|
||||
|
||||
// Available only when TPI 0x26[7:6]=10 to select I2S input
|
||||
//=========================================================
|
||||
#define TPI_I2S_CHST_0 (0x21)
|
||||
#define TPI_I2S_CHST_1 (0x22)
|
||||
#define TPI_I2S_CHST_2 (0x23)
|
||||
#define TPI_I2S_CHST_3 (0x24)
|
||||
#define TPI_I2S_CHST_4 (0x25)
|
||||
|
||||
|
||||
// Available only when 0x26[7:6]=01
|
||||
//=================================
|
||||
#define TPI_SPDIF_HEADER (0x24)
|
||||
#define TPI_AUDIO_HANDLING (0x25)
|
||||
|
||||
|
||||
// Audio Configuration Regiaters
|
||||
//==============================
|
||||
#define TPI_AUDIO_INTERFACE_REG (0x26)
|
||||
|
||||
// Finish this...
|
||||
|
||||
#define AUDIO_MUTE_MASK (BIT_4)
|
||||
#define AUDIO_MUTE_NORMAL (0x00)
|
||||
#define AUDIO_MUTE_MUTED (0x10)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#define TPI_AUDIO_SAMPLE_CTRL (0x27)
|
||||
|
||||
#define TPI_SPEAKER_CFG (0xC7)
|
||||
#define TPI_CHANNEL_COUNT (0xC4)
|
||||
|
||||
/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ///
|
||||
|
||||
/*\
|
||||
| | HDCP Implementation
|
||||
| |
|
||||
| | HDCP link security logic is implemented in certain transmitters; unique
|
||||
| | keys are embedded in each chip as part of the solution. The security
|
||||
| | scheme is fully automatic and handled completely by the hardware.
|
||||
\*/
|
||||
|
||||
/// HDCP Query Data Register ============================================== ///
|
||||
|
||||
#define TPI_HDCP_QUERY_DATA_REG (0x29)
|
||||
|
||||
#define EXTENDED_LINK_PROTECTION_MASK (BIT_7)
|
||||
#define EXTENDED_LINK_PROTECTION_NONE (0x00)
|
||||
#define EXTENDED_LINK_PROTECTION_SECURE (0x80)
|
||||
|
||||
#define LOCAL_LINK_PROTECTION_MASK (BIT_6)
|
||||
#define LOCAL_LINK_PROTECTION_NONE (0x00)
|
||||
#define LOCAL_LINK_PROTECTION_SECURE (0x40)
|
||||
|
||||
#define LINK_STATUS_MASK (BIT_5 | BIT_4)
|
||||
#define LINK_STATUS_NORMAL (0x00)
|
||||
#define LINK_STATUS_LINK_LOST (0x10)
|
||||
#define LINK_STATUS_RENEGOTIATION_REQ (0x20)
|
||||
#define LINK_STATUS_LINK_SUSPENDED (0x30)
|
||||
|
||||
#define HDCP_REPEATER_MASK (BIT_3)
|
||||
#define HDCP_REPEATER_NO (0x00)
|
||||
#define HDCP_REPEATER_YES (0x08)
|
||||
|
||||
#define CONNECTOR_TYPE_MASK (BIT_2 | BIT_0)
|
||||
#define CONNECTOR_TYPE_DVI (0x00)
|
||||
#define CONNECTOR_TYPE_RSVD (0x01)
|
||||
#define CONNECTOR_TYPE_HDMI (0x04)
|
||||
#define CONNECTOR_TYPE_FUTURE (0x05)
|
||||
|
||||
#define PROTECTION_TYPE_MASK (BIT_1)
|
||||
#define PROTECTION_TYPE_NONE (0x00)
|
||||
#define PROTECTION_TYPE_HDCP (0x02)
|
||||
|
||||
/// HDCP Control Data Register ============================================ ///
|
||||
|
||||
#define TPI_HDCP_CONTROL_DATA_REG (0x2A)
|
||||
|
||||
#define PROTECTION_LEVEL_MASK (BIT_0)
|
||||
#define PROTECTION_LEVEL_MIN (0x00)
|
||||
#define PROTECTION_LEVEL_MAX (0x01)
|
||||
|
||||
/// HDCP BKSV Registers =================================================== ///
|
||||
|
||||
#define TPI_BKSV_1_REG (0x2B)
|
||||
#define TPI_BKSV_2_REG (0x2C)
|
||||
#define TPI_BKSV_3_REG (0x2D)
|
||||
#define TPI_BKSV_4_REG (0x2E)
|
||||
#define TPI_BKSV_5_REG (0x2F)
|
||||
|
||||
/// HDCP Revision Data Register =========================================== ///
|
||||
|
||||
#define TPI_HDCP_REVISION_DATA_REG (0x30)
|
||||
|
||||
#define HDCP_MAJOR_REVISION_MASK (BIT_7 | BIT_6 | BIT_5 | BIT_4)
|
||||
#define HDCP_MAJOR_REVISION_VALUE (0x10)
|
||||
|
||||
#define HDCP_MINOR_REVISION_MASK (BIT_3 | BIT_2 | BIT_1 | BIT_0)
|
||||
#define HDCP_MINOR_REVISION_VALUE (0x02)
|
||||
|
||||
/// HDCP KSV and V' Value Data Register =================================== ///
|
||||
|
||||
#define TPI_V_PRIME_SELECTOR_REG (0x31)
|
||||
|
||||
/// V' Value Readback Registers =========================================== ///
|
||||
|
||||
#define TPI_V_PRIME_7_0_REG (0x32)
|
||||
#define TPI_V_PRIME_15_9_REG (0x33)
|
||||
#define TPI_V_PRIME_23_16_REG (0x34)
|
||||
#define TPI_V_PRIME_31_24_REG (0x35)
|
||||
|
||||
/// HDCP AKSV Registers =================================================== ///
|
||||
|
||||
#define TPI_AKSV_1_REG (0x36)
|
||||
#define TPI_AKSV_2_REG (0x37)
|
||||
#define TPI_AKSV_3_REG (0x38)
|
||||
#define TPI_AKSV_4_REG (0x39)
|
||||
#define TPI_AKSV_5_REG (0x3A)
|
||||
|
||||
/*\
|
||||
| | Interrupt Service
|
||||
| |
|
||||
| | TPI can be configured to generate an interrupt to the host to notify it of
|
||||
| | various events. The host can either poll for activity or use an interrupt
|
||||
| | handler routine. TPI generates on a single interrupt (INT) to the host.
|
||||
\*/
|
||||
|
||||
/// Interrupt Enable Register ============================================= ///
|
||||
|
||||
#define TPI_INTERRUPT_ENABLE_REG (0x3C)
|
||||
|
||||
#define HDCP_AUTH_STATUS_CHANGE_EN_MASK (BIT_7)
|
||||
#define HDCP_AUTH_STATUS_CHANGE_DISABLE (0x00)
|
||||
#define HDCP_AUTH_STATUS_CHANGE_ENABLE (0x80)
|
||||
|
||||
#define HDCP_VPRIME_VALUE_READY_EN_MASK (BIT_6)
|
||||
#define HDCP_VPRIME_VALUE_READY_DISABLE (0x00)
|
||||
#define HDCP_VPRIME_VALUE_READY_ENABLE (0x40)
|
||||
|
||||
#define HDCP_SECURITY_CHANGE_EN_MASK (BIT_5)
|
||||
#define HDCP_SECURITY_CHANGE_DISABLE (0x00)
|
||||
#define HDCP_SECURITY_CHANGE_ENABLE (0x20)
|
||||
|
||||
#define AUDIO_ERROR_EVENT_EN_MASK (BIT_4)
|
||||
#define AUDIO_ERROR_EVENT_DISABLE (0x00)
|
||||
#define AUDIO_ERROR_EVENT_ENABLE (0x10)
|
||||
|
||||
#define CPI_EVENT_NO_RX_SENSE_MASK (BIT_3)
|
||||
#define CPI_EVENT_NO_RX_SENSE_DISABLE (0x00)
|
||||
#define CPI_EVENT_NO_RX_SENSE_ENABLE (0x08)
|
||||
|
||||
#define RECEIVER_SENSE_EVENT_EN_MASK (BIT_1)
|
||||
#define RECEIVER_SENSE_EVENT_DISABLE (0x00)
|
||||
#define RECEIVER_SENSE_EVENT_ENABLE (0x02)
|
||||
|
||||
#define HOT_PLUG_EVENT_EN_MASK (BIT_0)
|
||||
#define HOT_PLUG_EVENT_DISABLE (0x00)
|
||||
#define HOT_PLUG_EVENT_ENABLE (0x01)
|
||||
|
||||
/// Interrupt Status Register ============================================= ///
|
||||
|
||||
#define TPI_INTERRUPT_STATUS_REG (0x3D)
|
||||
|
||||
#define HDCP_AUTH_STATUS_CHANGE_EVENT_MASK (BIT_7)
|
||||
#define HDCP_AUTH_STATUS_CHANGE_EVENT_NO (0x00)
|
||||
#define HDCP_AUTH_STATUS_CHANGE_EVENT_YES (0x80)
|
||||
|
||||
#define HDCP_VPRIME_VALUE_READY_EVENT_MASK (BIT_6)
|
||||
#define HDCP_VPRIME_VALUE_READY_EVENT_NO (0x00)
|
||||
#define HDCP_VPRIME_VALUE_READY_EVENT_YES (0x40)
|
||||
|
||||
#define HDCP_SECURITY_CHANGE_EVENT_MASK (BIT_5)
|
||||
#define HDCP_SECURITY_CHANGE_EVENT_NO (0x00)
|
||||
#define HDCP_SECURITY_CHANGE_EVENT_YES (0x20)
|
||||
|
||||
#define AUDIO_ERROR_EVENT_MASK (BIT_4)
|
||||
#define AUDIO_ERROR_EVENT_NO (0x00)
|
||||
#define AUDIO_ERROR_EVENT_YES (0x10)
|
||||
|
||||
#define CPI_EVENT_MASK (BIT_3)
|
||||
#define CPI_EVENT_NO (0x00)
|
||||
#define CPI_EVENT_YES (0x08)
|
||||
#define RX_SENSE_MASK (BIT_3) // This bit is dual purpose depending on the value of 0x3C[3]
|
||||
#define RX_SENSE_NOT_ATTACHED (0x00)
|
||||
#define RX_SENSE_ATTACHED (0x08)
|
||||
|
||||
#define HOT_PLUG_PIN_STATE_MASK (BIT_2)
|
||||
#define HOT_PLUG_PIN_STATE_LOW (0x00)
|
||||
#define HOT_PLUG_PIN_STATE_HIGH (0x04)
|
||||
|
||||
#define RECEIVER_SENSE_EVENT_MASK (BIT_1)
|
||||
#define RECEIVER_SENSE_EVENT_NO (0x00)
|
||||
#define RECEIVER_SENSE_EVENT_YES (0x02)
|
||||
|
||||
#define HOT_PLUG_EVENT_MASK (BIT_0)
|
||||
#define HOT_PLUG_EVENT_NO (0x00)
|
||||
#define HOT_PLUG_EVENT_YES (0x01)
|
||||
|
||||
/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ///
|
||||
|
||||
// Sync Register Configuration and Sync Monitoring Registers
|
||||
//==========================================================
|
||||
|
||||
#define TPI_SYNC_GEN_CTRL (0x60)
|
||||
#define TPI_SYNC_POLAR_DETECT (0x61)
|
||||
|
||||
// Explicit Sync DE Generator Registers (TPI 0x60[7]=0)
|
||||
//=====================================================
|
||||
|
||||
#define TPI_DE_DLY (0x62)
|
||||
#define TPI_DE_CTRL (0x63)
|
||||
#define TPI_DE_TOP (0x64)
|
||||
|
||||
#define TPI_RESERVED4 (0x65)
|
||||
|
||||
#define TPI_DE_CNT_7_0 (0x66)
|
||||
#define TPI_DE_CNT_11_8 (0x67)
|
||||
|
||||
#define TPI_DE_LIN_7_0 (0x68)
|
||||
#define TPI_DE_LIN_10_8 (0x69)
|
||||
|
||||
#define TPI_DE_H_RES_7_0 (0x6A)
|
||||
#define TPI_DE_H_RES_10_8 (0x6B)
|
||||
|
||||
#define TPI_DE_V_RES_7_0 (0x6C)
|
||||
#define TPI_DE_V_RES_10_8 (0x6D)
|
||||
|
||||
// Embedded Sync Register Set (TPI 0x60[7]=1)
|
||||
//===========================================
|
||||
|
||||
#define TPI_HBIT_TO_HSYNC_7_0 (0x62)
|
||||
#define TPI_HBIT_TO_HSYNC_9_8 (0x63)
|
||||
#define TPI_FIELD_2_OFFSET_7_0 (0x64)
|
||||
#define TPI_FIELD_2_OFFSET_11_8 (0x65)
|
||||
#define TPI_HWIDTH_7_0 (0x66)
|
||||
#define TPI_HWIDTH_8_9 (0x67)
|
||||
#define TPI_VBIT_TO_VSYNC (0x68)
|
||||
#define TPI_VWIDTH (0x69)
|
||||
|
||||
// TPI Enable Register
|
||||
//====================
|
||||
|
||||
#define TPI_ENABLE (0xC7)
|
||||
|
||||
// Misc InfoFrames
|
||||
//================
|
||||
#define MISC_INFO_FRAMES_CTRL (0xBF)
|
||||
#define MISC_INFO_FRAMES_TYPE (0xC0)
|
||||
#define MISC_INFO_FRAMES_VER (0xC1)
|
||||
#define MISC_INFO_FRAMES_LEN (0xC2)
|
||||
#define MISC_INFO_FRAMES_CHKSUM (0xC3)
|
||||
|
||||
// Backdoor Register Offsets
|
||||
//==========================
|
||||
#define INTERNAL_PAGE_0 0x00
|
||||
#define INTERNAL_PAGE_1 0x01
|
||||
#define DEVICE_ID_LOW_BYTE 0x02
|
||||
#define DEVICE_ID_HI_BYTE 0x03
|
||||
#define AUDIO_INPUT_LENGTH 0x24
|
||||
|
||||
|
||||
#define SW_RESET 0x05
|
||||
#define POWER_DOWN 0x6F
|
||||
|
||||
|
||||
// Backdoor constants
|
||||
//===================
|
||||
|
||||
#define DIV_BY_2 0x00
|
||||
#define MULT_BY_1 0x01
|
||||
#define MULT_BY_2 0x02
|
||||
#define MULT_BY_4 0x03
|
||||
|
||||
#define INTERNAL_PAGE_1 0x01
|
||||
#define INTERNAL_PAGE_2 0x02
|
||||
#define TMDS_CONT_REG 0x82
|
||||
|
||||
#endif
|
7
drivers/video/msm/hdmi/silicon-image/Makefile
Normal file
7
drivers/video/msm/hdmi/silicon-image/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
sil-objs = \
|
||||
tpi.o \
|
||||
hdcp.o \
|
||||
av_config.o \
|
||||
debug-sil902x.o
|
||||
|
||||
obj-$(CONFIG_MSM_HDMI) += sil.o
|
365
drivers/video/msm/hdmi/silicon-image/av_config.c
Normal file
365
drivers/video/msm/hdmi/silicon-image/av_config.c
Normal file
@ -0,0 +1,365 @@
|
||||
/*
|
||||
* Copyright (C) 2009 HTC
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
/* TODO:
|
||||
* 1. Mutex while active
|
||||
*/
|
||||
#include <linux/delay.h>
|
||||
#include <mach/msm_fb.h>
|
||||
#include "../include/fb-hdmi.h"
|
||||
#include "../include/sil902x.h"
|
||||
#include "../include/tpi.h"
|
||||
|
||||
#if 1
|
||||
#define HDMI_DBG(s...) printk("[hdmi/avc]" s)
|
||||
#else
|
||||
#define HDMI_DBG(s...) do {} while (0)
|
||||
#endif
|
||||
|
||||
static u8 video_param[][8] = {
|
||||
[hd_720p] = {0x01, 0x1d, 0x70, 0x17, 0x72, 0x06, 0xee, 0x02},
|
||||
[svga] = {0xa0, 0x0f, 0x70, 0x17, 0x20, 0x04, 0x74, 0x02},
|
||||
[pal] = {0x8c, 0x0a, 0x88, 0x13, 0x60, 0x03, 0x71, 0x02}, /* 576p50 */
|
||||
[edtv] = {0x8c, 0x0a, 0x70, 0x17, 0x5a, 0x03, 0x0d, 0x02},/* 480p60 */
|
||||
[vga] = {0x8c, 0x0a, 0x70, 0x17, 0x20, 0x03, 0x0d, 0x02},
|
||||
};
|
||||
|
||||
#if 0
|
||||
static u8 avi_info_frame[][14] = {
|
||||
[hd_720p] = {0x32, 0x0d, 0xa8, 0x84, 0x04, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00},
|
||||
[svga] = {0xd1, 0x0e, 0x08, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00},
|
||||
[pal] = {0x64, 0x0d, 0x68, 0x84, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00}, /* 576p50 */
|
||||
[edtv] = {0x73, 0x0d, 0x68, 0x84, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00},
|
||||
[vga] = {0x5E, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00},
|
||||
};
|
||||
#else
|
||||
static u8 avi_info_frame[][14] = {
|
||||
[hd_720p] = {0x43, 0x00, 0x28, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00},
|
||||
[svga] = {0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00},
|
||||
[pal] = {0x46, 0x00, 0x18, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00}, /* 576p50 */
|
||||
[edtv] = {0x55, 0x00, 0x18, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00},
|
||||
[vga] = {0x5E, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00},
|
||||
};
|
||||
#endif
|
||||
|
||||
static u8 audio_info_frame[] =
|
||||
{ 0xc2, 0x84, 0x01, 0x0a, 0x71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
#if 0
|
||||
int hdmi_active9022(struct i2c_client *client)
|
||||
{
|
||||
int i, ret = -EIO;
|
||||
struct hdmi_info *info = i2c_get_clientdata(client);
|
||||
u8 *video_parm = &video_param[info->res][0];
|
||||
|
||||
HDMI_DBG("%s+\n", __func__);
|
||||
mutex_lock(&info->polling_lock);
|
||||
mutex_lock(&info->lock);
|
||||
|
||||
//hdcp_off(info);
|
||||
DisableTMDS(info);
|
||||
msleep(128);
|
||||
|
||||
/* choose video mode */
|
||||
ret = i2c_smbus_write_i2c_block_data(client, 0, 8, video_parm);
|
||||
|
||||
/* wakeup Sil9022 to D0 state */
|
||||
ret = hdmi_write_byte(client, HDMI_POWER, 0);
|
||||
|
||||
if (edid_check_sink_type(info)) {
|
||||
/* HDMI Output */
|
||||
ReadModifyWriteTPI(info, TPI_SYSTEM_CONTROL,
|
||||
OUTPUT_MODE_MASK, OUTPUT_MODE_HDMI);
|
||||
/* audio configuration */
|
||||
ret = hdmi_write_byte(client, 0x26, 0x91);
|
||||
ret = hdmi_write_byte(client, 0x25, 0x03);
|
||||
ret = hdmi_write_byte(client, 0x27, 0x59);
|
||||
ret = hdmi_write_byte(client, 0x28, 0x00);
|
||||
ret = hdmi_write_byte(client, 0x1f, 0x80);
|
||||
ret = hdmi_write_byte(client, 0x20, 0x90);
|
||||
ret = hdmi_write_byte(client, 0x21, 0x00);
|
||||
//ret = hdmi_write_byte(client, 0x24, 0x00);//0x00 for 44.1k
|
||||
ret = hdmi_write_byte(client, 0x24, 0x02);//0x02 for 48k
|
||||
ret = hdmi_write_byte(client, 0x25, 0x00);
|
||||
ret = hdmi_write_byte(client, 0xbc, 0x02);
|
||||
ret = hdmi_write_byte(client, 0xbd, 0x24);
|
||||
ret = hdmi_write_byte(client, 0xbe, 0x92);
|
||||
ret = hdmi_write_byte(client, 0xbc, 0x02);
|
||||
ret = hdmi_write_byte(client, 0xbd, 0x2f);
|
||||
ret = hdmi_write_byte(client, 0xbe, 0);
|
||||
ret = hdmi_write_byte(client, 0x26, 0x81);
|
||||
} else {
|
||||
ReadModifyWriteTPI(info, TPI_SYSTEM_CONTROL,
|
||||
OUTPUT_MODE_MASK, OUTPUT_MODE_DVI);
|
||||
SetAudioMute(info, AUDIO_MUTE_MUTED);
|
||||
}
|
||||
ret = hdmi_write_byte(client, HDMI_PIXEL_DATA, 0x60);
|
||||
|
||||
ret = i2c_smbus_write_i2c_block_data(client, HDMI_AUDIO_INFO_FRAME,
|
||||
15, audio_info_frame);
|
||||
hdmi_write_byte(client, 0x09, 0);
|
||||
hdmi_write_byte(client, 0x0a, 0);
|
||||
for (i = 0; i < 14 ;i++)
|
||||
hdmi_write_byte(client, 0xc + i, avi_info_frame[info->res][i]);
|
||||
|
||||
EnableTMDS(info);
|
||||
HDMI_DBG("%s-\n", __func__);
|
||||
mutex_unlock(&info->lock);
|
||||
mutex_unlock(&info->polling_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
int avc_set_video_parm(struct hdmi_info *hdmi)
|
||||
{
|
||||
int ret;
|
||||
u8 *video_parm = video_param[hdmi->res];
|
||||
|
||||
ret = i2c_smbus_write_i2c_block_data(hdmi->client, 0, 8, video_parm);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int avc_set_blank_screen(struct hdmi_info *hdmi)
|
||||
{
|
||||
HDMI_DBG("%s+\n", __func__);
|
||||
hdmi_write_byte(hdmi->client, 0x09, 0x03);
|
||||
hdmi_write_byte(hdmi->client, 0x19, 0x00);
|
||||
hdmi_write_byte(hdmi->client, 0x26,
|
||||
hdmi_read(hdmi->client, 0x26) | 0x10);
|
||||
}
|
||||
|
||||
int hdmi_active9022_dup(struct i2c_client *client)
|
||||
{
|
||||
int i, ret = -EIO;
|
||||
struct hdmi_info *info = i2c_get_clientdata(client);
|
||||
u8 *video_parm = &video_param[info->res][0];
|
||||
|
||||
HDMI_DBG("%s+\n", __func__);
|
||||
|
||||
//hdcp_off(info);
|
||||
DisableTMDS(info);
|
||||
msleep(128);
|
||||
|
||||
/* choose video mode */
|
||||
ret = i2c_smbus_write_i2c_block_data(client, 0, 8, video_parm);
|
||||
|
||||
/* wakeup Sil9022 to D0 state */
|
||||
ret = hdmi_write_byte(client, HDMI_POWER, 0);
|
||||
|
||||
if (edid_check_sink_type(info)) {
|
||||
/* HDMI Output */
|
||||
ReadModifyWriteTPI(info, TPI_SYSTEM_CONTROL,
|
||||
OUTPUT_MODE_MASK, OUTPUT_MODE_HDMI);
|
||||
/* audio configuration */
|
||||
ret = hdmi_write_byte(client, 0x26, 0x91);
|
||||
ret = hdmi_write_byte(client, 0x25, 0x03);
|
||||
ret = hdmi_write_byte(client, 0x27, 0x59);
|
||||
ret = hdmi_write_byte(client, 0x28, 0x00);
|
||||
ret = hdmi_write_byte(client, 0x1f, 0x80);
|
||||
ret = hdmi_write_byte(client, 0x20, 0x90);
|
||||
ret = hdmi_write_byte(client, 0x21, 0x00);
|
||||
//ret = hdmi_write_byte(client, 0x24, 0x00);//0x00 for 44.1k
|
||||
ret = hdmi_write_byte(client, 0x24, 0x02);//0x02 for 48k
|
||||
ret = hdmi_write_byte(client, 0x25, 0x00);
|
||||
ret = hdmi_write_byte(client, 0xbc, 0x02);
|
||||
ret = hdmi_write_byte(client, 0xbd, 0x24);
|
||||
ret = hdmi_write_byte(client, 0xbe, 0x92);
|
||||
ret = hdmi_write_byte(client, 0xbc, 0x02);
|
||||
ret = hdmi_write_byte(client, 0xbd, 0x2f);
|
||||
ret = hdmi_write_byte(client, 0xbe, 0);
|
||||
ret = hdmi_write_byte(client, 0x26, 0x81);
|
||||
} else {
|
||||
ReadModifyWriteTPI(info, TPI_SYSTEM_CONTROL,
|
||||
OUTPUT_MODE_MASK, OUTPUT_MODE_DVI);
|
||||
SetAudioMute(info, AUDIO_MUTE_MUTED);
|
||||
}
|
||||
ret = hdmi_write_byte(client, HDMI_PIXEL_DATA, 0x60);
|
||||
|
||||
ret = i2c_smbus_write_i2c_block_data(client, HDMI_AUDIO_INFO_FRAME,
|
||||
15, audio_info_frame);
|
||||
hdmi_write_byte(client, 0x09, 0x03);
|
||||
hdmi_write_byte(client, 0x0a, 0);
|
||||
for (i = 0; i < 14 ;i++)
|
||||
hdmi_write_byte(client, 0xc + i, avi_info_frame[info->res][i]);
|
||||
|
||||
EnableTMDS(info);
|
||||
|
||||
HDMI_DBG("%s-\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool avc_send_avi_info_frames(struct hdmi_info *hdmi)
|
||||
{
|
||||
int i;
|
||||
|
||||
HDMI_DBG("%s res=%d\n", __func__, hdmi->res);
|
||||
for (i = 0; i < 14 ;i++)
|
||||
hdmi_write_byte(hdmi->client, 0xc + i,
|
||||
avi_info_frame[hdmi->res][i]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* FIXME: intergrate with active9022 */
|
||||
bool InitVideo(struct hdmi_info *hdmi, u8 Mode, u8 TclkSel, bool Init)
|
||||
{
|
||||
int Pattern, ret;
|
||||
u8 *video_parm = &video_param[hdmi->res][0];
|
||||
|
||||
/* Use TPI 0x08[7:6] for 9022A/24A video clock multiplier */
|
||||
Pattern = (1 << 6) & 0xc0;
|
||||
ReadSetWriteTPI(hdmi, TPI_PIX_REPETITION, Pattern);
|
||||
|
||||
ret = i2c_smbus_write_block_data(hdmi->client, 0, 8, video_parm);
|
||||
|
||||
/* input format */
|
||||
hdmi_write_byte(hdmi->client, 0x09, 0);
|
||||
hdmi_write_byte(hdmi->client, 0x0a, 0);
|
||||
if(Init) {
|
||||
hdmi_write_byte(hdmi->client, 0x08, 0x60);
|
||||
hdmi_write_byte(hdmi->client, 0x09, 0);
|
||||
hdmi_write_byte(hdmi->client, 0x0a, 0);
|
||||
hdmi_write_byte(hdmi->client, 0x60, 0x4);
|
||||
}
|
||||
|
||||
// termination ???
|
||||
//ret = (read_back_door_register(0x1, 0x82) & 0x3f ) | 0x25;
|
||||
//write_back_door_register();
|
||||
#if 0
|
||||
for (i = 0; i < 14 ;i++) {
|
||||
hdmi_write_byte(hdmi->client, 0xc + i, avi_info_frame[info->res][i]);
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool avc_init_video(struct hdmi_info *hdmi, u8 mode, u8 TclkSel, bool Init)
|
||||
{
|
||||
int ret;
|
||||
u8 *video_parm = &video_param[hdmi->res][0];
|
||||
|
||||
HDMI_DBG("%s\n", __func__);
|
||||
/* Use TPI 0x08[7:6] for 9022A/24A video clock multiplier */
|
||||
hdmi_write_byte(hdmi->client, HDMI_PIXEL_DATA, 0x60);
|
||||
|
||||
ret = i2c_smbus_write_i2c_block_data(hdmi->client, 0, 8, video_parm);
|
||||
|
||||
/* input format */
|
||||
hdmi_write_byte(hdmi->client, TPI_INPUT_FORMAT_REG, 0x00);
|
||||
hdmi_write_byte(hdmi->client, TPI_OUTPUT_FORMAT_REG, 0x00);
|
||||
#if 0
|
||||
if (Init) {
|
||||
hdmi_write_byte(hdmi->client, 0x08, 0x60);
|
||||
hdmi_write_byte(hdmi->client, 0x09, 0);
|
||||
hdmi_write_byte(hdmi->client, 0x0a, 0);
|
||||
/* Default to External Sync mode + disable VSync adjustment */
|
||||
hdmi_write_byte(hdmi->client, 0x60, 0x4);
|
||||
/* Disable DE generator by default */
|
||||
hdmi_write_byte(hdmi->client, 0x63, 0x0);
|
||||
}
|
||||
|
||||
#endif
|
||||
ret = (tpi_read_backdoor_register(hdmi, INTERNAL_PAGE_1, TMDS_CONT_REG)
|
||||
& 0x3f ) | 0x25;
|
||||
tpi_write_backdoor_register(hdmi, INTERNAL_PAGE_1, TMDS_CONT_REG, ret);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void avc_set_basic_audio(struct hdmi_info *hdmi)
|
||||
{
|
||||
int ret;
|
||||
struct i2c_client *client = hdmi->client;
|
||||
HDMI_DBG("%s\n", __func__);
|
||||
|
||||
ret = hdmi_write_byte(client, 0x26, 0x91);
|
||||
ret = hdmi_write_byte(client, 0x25, 0x03);
|
||||
ret = hdmi_write_byte(client, 0x27, 0x59);
|
||||
ret = hdmi_write_byte(client, 0x28, 0x00);
|
||||
ret = hdmi_write_byte(client, 0x1f, 0x80);
|
||||
ret = hdmi_write_byte(client, 0x20, 0x90);
|
||||
ret = hdmi_write_byte(client, 0x21, 0x00);
|
||||
//ret = hdmi_write_byte(client, 0x24, 0x00);//0x00 for 44.1k
|
||||
ret = hdmi_write_byte(client, 0x24, 0x02);//0x02 for 48k
|
||||
ret = hdmi_write_byte(client, 0x25, 0x00);
|
||||
ret = hdmi_write_byte(client, 0xbc, 0x02);
|
||||
ret = hdmi_write_byte(client, 0xbd, 0x24);
|
||||
ret = hdmi_write_byte(client, 0xbe, 0x92);
|
||||
ret = hdmi_write_byte(client, 0xbc, 0x02);
|
||||
ret = hdmi_write_byte(client, 0xbd, 0x2f);
|
||||
ret = hdmi_write_byte(client, 0xbe, 0);
|
||||
ret = hdmi_write_byte(client, 0x26, 0x81);
|
||||
|
||||
ret = i2c_smbus_write_i2c_block_data(client, HDMI_AUDIO_INFO_FRAME,
|
||||
15, audio_info_frame);
|
||||
}
|
||||
|
||||
/* simplifier version of ChangeVideoMode() */
|
||||
u8 avc_change_video_mode(struct hdmi_info *hdmi, int *resolution)
|
||||
{
|
||||
HDMI_DBG("%s\n", __func__);
|
||||
|
||||
hdcp_off(hdmi);
|
||||
DisableTMDS(hdmi);
|
||||
/* allow control InfoFrames to pass through to the sink device. */
|
||||
mdelay(128);
|
||||
|
||||
// FIXME: video mode
|
||||
avc_init_video(hdmi, vid_mode, 0, 0);
|
||||
|
||||
hdmi_write_byte(hdmi->client, TPI_PIX_REPETITION, 0x60);
|
||||
hdmi_write_byte(hdmi->client, TPI_INPUT_FORMAT, 0);
|
||||
|
||||
if (edid_check_sink_type(hdmi))
|
||||
ReadSetWriteTPI(hdmi, 0x1a, 0x01);
|
||||
else
|
||||
ReadSetWriteTPI(hdmi, 0x1a, 0x00);
|
||||
|
||||
/* FIXME: 720p/480p ?*/
|
||||
hdmi_write_byte(hdmi->client, TPI_OUTPUT_FORMAT, 0);
|
||||
|
||||
/* set 0x60[7] = 0 for External Sync */
|
||||
ReadClearWriteTPI(hdmi, TPI_SYNC_GEN_CTRL, 0x80);
|
||||
/* clear 0x63[6] = 0 to disable internal DE */
|
||||
ReadClearWriteTPI(hdmi, 0x63, 1 << 6);
|
||||
|
||||
/* InfoFrames - only if output mode is HDMI */
|
||||
if (edid_check_sink_type(hdmi))
|
||||
avc_send_avi_info_frames(hdmi);
|
||||
|
||||
/* SETTING UP AVI InfoFrames CLEARS 0x63 and 0x60[5] */
|
||||
hdmi_write_byte(hdmi->client, TPI_SYNC_GEN_CTRL, 1 << 2);
|
||||
/* SETTING UP AVI InfoFrames CLEARS 0x63 */
|
||||
hdmi_write_byte(hdmi->client, 0x63, 0);
|
||||
|
||||
/* YC Input Mode Select */
|
||||
hdmi_write_byte(hdmi->client, 0x0b, 0); // 0x0b
|
||||
EnableTMDS(hdmi);
|
||||
|
||||
return VIDEO_MODE_SET_OK;
|
||||
}
|
109
drivers/video/msm/hdmi/silicon-image/debug-sil902x.c
Normal file
109
drivers/video/msm/hdmi/silicon-image/debug-sil902x.c
Normal file
@ -0,0 +1,109 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#include <mach/msm_fb.h>
|
||||
#include <mach/msm_hdmi.h>
|
||||
|
||||
#include "../include/fb-hdmi.h"
|
||||
#include "../include/sil902x.h"
|
||||
|
||||
//#define HDMI_DEBUGFS
|
||||
|
||||
#if defined(HDMI_DEBUGFS)
|
||||
static spinlock_t hdmi_dbgfs_lock;
|
||||
ssize_t hdmi_dbgfs_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
file->private_data = inode->i_private;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t hdmi_dbgfs_read(struct file *filp, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
static char line[80], buffer[80*8*4];
|
||||
static char hextab[] = "0123456789abcdefg";
|
||||
int i, j, n = 0, v, len, offset, line_size;
|
||||
unsigned long irq_flags;
|
||||
struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
|
||||
|
||||
len = ((int)hdmi->edid_buf[0x7e]+1) * 128;
|
||||
spin_lock_irqsave(&hdmi_dbgfs_lock, irq_flags);
|
||||
memset(line, ' ', 79);
|
||||
line[79] = '\0';
|
||||
offset = strlen("0000 | ");
|
||||
line_size = offset + 3 * 16 + 1;
|
||||
|
||||
for (i = 0; i < len / 16 ; i++) {
|
||||
scnprintf(line, offset + 1, "%04x | ", (i << 4));
|
||||
for (j = 0; j < 16 ; j++) {
|
||||
v = hdmi->edid_buf[i * 16 + j];
|
||||
line[offset + j * 3] = hextab[v / 16];
|
||||
line[offset + j * 3 + 1] = hextab[v % 16];
|
||||
}
|
||||
line[line_size - 1] = '\n';
|
||||
strncpy(buffer + i * line_size, line, line_size);
|
||||
n += line_size;
|
||||
}
|
||||
spin_unlock_irqrestore(&hdmi_dbgfs_lock, irq_flags);
|
||||
return simple_read_from_buffer(buf, count, ppos, buffer, n);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static ssize_t hdmi_dbgfs_write(struct file *filp, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
unsigned long v;
|
||||
unsigned long irq_flags;
|
||||
char buff[80];
|
||||
struct tv_reg_data *trd = (struct tv_reg_data *)filp->private_data;
|
||||
|
||||
if (count >= sizeof(buff))
|
||||
return -EINVAL;
|
||||
if (copy_from_user(&buff, buf, 80))
|
||||
return -EFAULT;
|
||||
buff[count] = 0;
|
||||
|
||||
#if 0
|
||||
spin_lock_irqsave(&hdmi_dbgfs_lock, irq_flags);
|
||||
strict_strtoul(buff, 16, &v);
|
||||
buff[strlen(buff)]=0;
|
||||
writel(v, tvenc_base+trd->offset);
|
||||
spin_unlock_irqrestore(&hdmi_dbgfs_lock, irq_flags);
|
||||
#endif
|
||||
|
||||
return count;
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct file_operations hdmi_fops[] = {
|
||||
{
|
||||
.open = hdmi_dbgfs_open,
|
||||
.read = hdmi_dbgfs_read,
|
||||
}
|
||||
};
|
||||
|
||||
int hdmi_debugfs_init(struct hdmi_info *hdmi)
|
||||
{
|
||||
HDMI_DBG("%s\n", __func__);
|
||||
spin_lock_init(&hdmi_dbgfs_lock);
|
||||
hdmi->debug_dir = debugfs_create_dir(HDMI_DEBUGFS_ROOT, 0);
|
||||
if (IS_ERR(hdmi->debug_dir))
|
||||
return PTR_ERR(hdmi->debug_dir);
|
||||
|
||||
// FIXME: error handling
|
||||
debugfs_create_file("dummy", 0644, hdmi->debug_dir, hdmi,
|
||||
&hdmi_fops[0]);
|
||||
edid_debugfs_init(hdmi);
|
||||
tpi_debugfs_init(hdmi);
|
||||
hdcp_debugfs_init(hdmi);
|
||||
/*
|
||||
int ret;
|
||||
if (!ret) {
|
||||
pr_err("%s: failure on debugfs_create_file()\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
354
drivers/video/msm/hdmi/silicon-image/hdcp.c
Normal file
354
drivers/video/msm/hdmi/silicon-image/hdcp.c
Normal file
@ -0,0 +1,354 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <mach/msm_fb.h>
|
||||
#include <mach/msm_hdmi.h>
|
||||
|
||||
#include "../include/fb-hdmi.h"
|
||||
#include "../include/sil902x.h"
|
||||
#include "../include/tpi.h"
|
||||
|
||||
#if 1
|
||||
#define HDCP_DBG(fmt, arg...) printk( "[hdmi/hdcp]%s: " fmt, __func__, ##arg)
|
||||
#else
|
||||
#define HDCP_DBG(fmt...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#define hdcp_err(fmt, arg...) pr_err( "[hdmi/hdcp]%s: " fmt, __func__, ##arg)
|
||||
|
||||
//#define HDCP_DEBUG /* Remove this definition when releasing!!! */
|
||||
#if defined(HDCP_DEBUG)
|
||||
#define HDCP_OVERWRITE_CONTROL 0x1
|
||||
#define HDCP_SUPPORT 0x2
|
||||
#define HDCP_AVALABLE 0x4
|
||||
static int hdcp_control = 0x7;
|
||||
module_param_named(hdcp_control, hdcp_control, int,
|
||||
S_IRUGO | S_IWUSR | S_IWGRP);
|
||||
#endif
|
||||
|
||||
bool HDCP_TxSupports;
|
||||
bool HDCP_Started;
|
||||
u8 HDCP_LinkProtectionLevel;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// FUNCTION : IsHDCP_Supported()
|
||||
// PURPOSE : Check Tx revision number to find if this Tx supports HDCP
|
||||
// by reading the HDCP revision number from TPI register 0x30.
|
||||
// RETURNS : true if Tx supports HDCP. false if not.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
bool hdcp_check_support(struct hdmi_info *hdmi)
|
||||
{
|
||||
u8 HDCP_Rev;
|
||||
bool HDCP_Supported;
|
||||
|
||||
HDCP_Supported = true;
|
||||
/* Check Device ID */
|
||||
HDCP_Rev = hdmi_read(hdmi->client, TPI_HDCP_REVISION_DATA_REG);
|
||||
if (HDCP_Rev !=
|
||||
(HDCP_MAJOR_REVISION_VALUE | HDCP_MINOR_REVISION_VALUE))
|
||||
HDCP_Supported = false;
|
||||
|
||||
HDCP_DBG("ret=%d\n", HDCP_Supported);
|
||||
return HDCP_Supported;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// FUNCTION : AreAKSV_OK()
|
||||
// PURPOSE : Check if AKSVs contain 20 '0' and 20 '1'
|
||||
// INPUT PARAMS : None
|
||||
// OUTPUT PARAMS : None
|
||||
// GLOBALS USED : TBD
|
||||
// RETURNS : true if 20 zeros and 20 ones found in AKSV. false OTHERWISE
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
static bool hdcp_check_aksv(struct hdmi_info *hdmi)
|
||||
{
|
||||
int ret;
|
||||
u8 B_Data[AKSV_SIZE];
|
||||
u8 i, j, NumOfOnes = 0;
|
||||
|
||||
memset(B_Data, 0, AKSV_SIZE);
|
||||
#if 0
|
||||
ReadBlockTPI(hdmi, TPI_AKSV_1_REG, AKSV_SIZE, B_Data);
|
||||
#else
|
||||
for (i = 0; i < 5; i++) {
|
||||
B_Data[i] = hdmi_read(hdmi->client, TPI_AKSV_1_REG+i);
|
||||
}
|
||||
#endif
|
||||
HDCP_DBG(" askv={%02x, %02x, %02x, %02x, %02x}\n",
|
||||
B_Data[0], B_Data[1], B_Data[2], B_Data[3], B_Data[4]);
|
||||
for (i=0; i < AKSV_SIZE; i++)
|
||||
for (j=0; j < BYTE_SIZE; j++) {
|
||||
if (B_Data[i] & 0x01)
|
||||
NumOfOnes++;
|
||||
B_Data[i] >>= 1;
|
||||
}
|
||||
if (NumOfOnes != NUM_OF_ONES_IN_KSV)
|
||||
ret = false;
|
||||
else ret = true;
|
||||
|
||||
HDCP_DBG(":ret=%s\n", ret ? "true" : "false");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// FUNCTION : HDCP_On()
|
||||
// PURPOSE : Switch hdcp on.
|
||||
// INPUT PARAMS : None
|
||||
// OUTPUT PARAMS: None
|
||||
// GLOBALS USED : HDCP_Started set to true
|
||||
// RETURNS : None
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//void hdcp_on(struct hdmi_info *hdmi)
|
||||
void hdcp_on(struct hdmi_info *hdmi, const char *caller)
|
||||
{
|
||||
HDCP_DBG(", caller=%s\n", caller);
|
||||
hdmi_write_byte(hdmi->client, TPI_HDCP_CONTROL_DATA_REG, PROTECTION_LEVEL_MAX);
|
||||
HDCP_Started = true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// FUNCTION : HDCP_Off()
|
||||
// PURPOSE : Switch hdcp off.
|
||||
// GLOBALS USED : HDCP_Started set to false
|
||||
// RETURNS : None
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
void hdcp_off(struct hdmi_info *hdmi)
|
||||
{
|
||||
HDCP_DBG("\n");
|
||||
|
||||
SetInputColorSpace(hdmi, INPUT_COLOR_SPACE_BLACK_MODE);
|
||||
SetAudioMute(hdmi, AUDIO_MUTE_MUTED);
|
||||
hdmi_write_byte(hdmi->client, TPI_HDCP_CONTROL_DATA_REG, PROTECTION_LEVEL_MIN);
|
||||
HDCP_Started = false;
|
||||
HDCP_LinkProtectionLevel = EXTENDED_LINK_PROTECTION_NONE | LOCAL_LINK_PROTECTION_NONE;
|
||||
}
|
||||
|
||||
void hdcp_init(struct hdmi_info *hdmi)
|
||||
{
|
||||
HDCP_DBG("\n");
|
||||
|
||||
HDCP_TxSupports = false;
|
||||
HDCP_Started = false;
|
||||
HDCP_LinkProtectionLevel = EXTENDED_LINK_PROTECTION_NONE | LOCAL_LINK_PROTECTION_NONE;
|
||||
|
||||
/* TX-related... need only be done once. */
|
||||
if (!hdcp_check_support(hdmi)) {
|
||||
hdcp_err("TX does not support HDCP\n");
|
||||
return;
|
||||
}
|
||||
if (!hdcp_check_aksv(hdmi)) {
|
||||
hdcp_err("Illegal AKSV\n");
|
||||
return;
|
||||
}
|
||||
HDCP_TxSupports = true;
|
||||
}
|
||||
|
||||
void hdcp_restart(struct hdmi_info *hdmi)
|
||||
{
|
||||
HDCP_DBG("\n");
|
||||
DisableTMDS(hdmi);
|
||||
hdcp_off(hdmi);
|
||||
EnableTMDS(hdmi);
|
||||
}
|
||||
|
||||
void hdcp_check_status(struct hdmi_info *hdmi, u8 InterruptStatusImage)
|
||||
{
|
||||
u8 QueryData, LinkStatus, RegImage, NewLinkProtectionLevel;
|
||||
|
||||
if (HDCP_TxSupports == false)
|
||||
return;
|
||||
|
||||
if ((HDCP_LinkProtectionLevel ==
|
||||
(EXTENDED_LINK_PROTECTION_NONE | LOCAL_LINK_PROTECTION_NONE)) &&
|
||||
(HDCP_Started == false)) {
|
||||
QueryData = hdmi_read(hdmi->client, TPI_HDCP_QUERY_DATA_REG);
|
||||
/* Is HDCP avaialable */
|
||||
if (QueryData & PROTECTION_TYPE_MASK) {
|
||||
hdcp_on(hdmi, __func__);
|
||||
}
|
||||
}
|
||||
/* Check if Link Status has changed: */
|
||||
if (InterruptStatusImage & SECURITY_CHANGE_EVENT) {
|
||||
HDCP_DBG("SECURITY_CHANGE_EVENT\n");
|
||||
LinkStatus = hdmi_read(hdmi->client, TPI_HDCP_QUERY_DATA_REG);
|
||||
LinkStatus &= LINK_STATUS_MASK;
|
||||
tpi_clear_interrupt(hdmi, SECURITY_CHANGE_EVENT);
|
||||
switch (LinkStatus) {
|
||||
case LINK_STATUS_NORMAL:
|
||||
HDCP_DBG("Link = Normal\n");
|
||||
break;
|
||||
case LINK_STATUS_LINK_LOST:
|
||||
HDCP_DBG("Link = Lost\n");
|
||||
hdcp_restart(hdmi);
|
||||
break;
|
||||
case LINK_STATUS_RENEGOTIATION_REQ:
|
||||
HDCP_DBG("Link = Renegotiation Required\n");
|
||||
hdcp_off(hdmi);
|
||||
hdcp_on(hdmi, __func__);
|
||||
break;
|
||||
case LINK_STATUS_LINK_SUSPENDED:
|
||||
HDCP_DBG("Link = Suspended\n");
|
||||
hdcp_on(hdmi, __func__);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Check if HDCP state has changed: */
|
||||
if (InterruptStatusImage & HDCP_CHANGE_EVENT) {
|
||||
HDCP_DBG("HDCP_CHANGE_EVENT\n");
|
||||
RegImage = hdmi_read(hdmi->client, TPI_HDCP_QUERY_DATA_REG);
|
||||
NewLinkProtectionLevel = RegImage &
|
||||
(EXTENDED_LINK_PROTECTION_MASK | LOCAL_LINK_PROTECTION_MASK);
|
||||
if (NewLinkProtectionLevel != HDCP_LinkProtectionLevel) {
|
||||
HDCP_LinkProtectionLevel = NewLinkProtectionLevel;
|
||||
switch (HDCP_LinkProtectionLevel) {
|
||||
case (EXTENDED_LINK_PROTECTION_NONE | LOCAL_LINK_PROTECTION_NONE):
|
||||
HDCP_DBG("Protection = None\n");
|
||||
hdcp_restart(hdmi);
|
||||
break;
|
||||
case LOCAL_LINK_PROTECTION_SECURE:
|
||||
SetAudioMute(hdmi, AUDIO_MUTE_NORMAL);
|
||||
//SetInputColorSpace (hdmi, INPUT_COLOR_SPACE_YCBCR422);
|
||||
SetInputColorSpace (hdmi, INPUT_COLOR_SPACE_RGB);
|
||||
HDCP_DBG("Protection = Local, Video Unmuted\n");
|
||||
break;
|
||||
case (EXTENDED_LINK_PROTECTION_SECURE | LOCAL_LINK_PROTECTION_SECURE):
|
||||
HDCP_DBG("Protection = Extended\n");
|
||||
break;
|
||||
default:
|
||||
HDCP_DBG("Protection = Extended but not Local?\n");
|
||||
hdcp_restart(hdmi);
|
||||
break;
|
||||
}
|
||||
}
|
||||
tpi_clear_interrupt(hdmi, HDCP_CHANGE_EVENT);
|
||||
}
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
#if defined(HDMI_DEBUGFS)
|
||||
static ssize_t hdcp_supported_read(struct file *filp, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
int n=0;
|
||||
char buffer[80];
|
||||
//struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
|
||||
|
||||
HDMI_DBG("%s\n", __func__);
|
||||
//n = scnprintf(buffer, 80, "%d\n", is_hdcp_supported(hdmi));
|
||||
n++;
|
||||
buffer[n] = 0;
|
||||
return simple_read_from_buffer(buf, count, ppos, buffer, n);
|
||||
}
|
||||
|
||||
static ssize_t hdcp_available_read(struct file *filp, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
int n=0;
|
||||
char buffer[80];
|
||||
//struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
|
||||
|
||||
HDMI_DBG("%s\n", __func__);
|
||||
//n = scnprintf(buffer, 80, "%d\n", is_hdcp_available(hdmi));
|
||||
n++;
|
||||
buffer[n] = 0;
|
||||
return simple_read_from_buffer(buf, count, ppos, buffer, n);
|
||||
}
|
||||
|
||||
static ssize_t hdcp_on_read(struct file *filp, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
|
||||
hdcp_on(hdmi, __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t hdcp_off_read(struct file *filp, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
|
||||
hdcp_off(hdmi);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t hdcp_restart_read(struct file *filp, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
|
||||
hdcp_restart(hdmi);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t hdcp_handle_read(struct file *filp, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
//struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
|
||||
//handle_hdcp(hdmi);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t hdcp_poll_read(struct file *filp, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
//struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
|
||||
//hdcp_poll(hdmi);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t hdcp_aksv_read(struct file *filp, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
|
||||
hdcp_check_aksv(hdmi);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t hdcp_bksv_read(struct file *filp, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
//struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
|
||||
//hdcp_check_bksv(hdmi);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct file_operations hdcp_debugfs_fops[] = {
|
||||
{ .open = hdmi_dbgfs_open, .read = hdcp_supported_read, },
|
||||
{ .open = hdmi_dbgfs_open, .read = hdcp_available_read, },
|
||||
{ .open = hdmi_dbgfs_open, .read = hdcp_on_read, },
|
||||
{ .open = hdmi_dbgfs_open, .read = hdcp_off_read, },
|
||||
{ .open = hdmi_dbgfs_open, .read = hdcp_restart_read, },
|
||||
{ .open = hdmi_dbgfs_open, .read = hdcp_poll_read, },
|
||||
{ .open = hdmi_dbgfs_open, .read = hdcp_aksv_read, },
|
||||
{ .open = hdmi_dbgfs_open, .read = hdcp_bksv_read, },
|
||||
};
|
||||
|
||||
// mutex ?
|
||||
int hdcp_debugfs_init(struct hdmi_info *hdmi)
|
||||
{
|
||||
struct dentry *hdcp_dent;
|
||||
|
||||
hdcp_dent = debugfs_create_dir("hdcp", hdmi->debug_dir);
|
||||
if (IS_ERR(hdcp_dent))
|
||||
return PTR_ERR(hdcp_dent);
|
||||
|
||||
//FIXME: error handling
|
||||
debugfs_create_file("supported", 0444, hdcp_dent, hdmi,
|
||||
&hdcp_debugfs_fops[0]);
|
||||
debugfs_create_file("available", 0444, hdcp_dent, hdmi,
|
||||
&hdcp_debugfs_fops[1]);
|
||||
debugfs_create_file("on", 0444, hdcp_dent, hdmi,
|
||||
&hdcp_debugfs_fops[2]);
|
||||
debugfs_create_file("off", 0444, hdcp_dent, hdmi,
|
||||
&hdcp_debugfs_fops[3]);
|
||||
debugfs_create_file("restart", 0444, hdcp_dent, hdmi,
|
||||
&hdcp_debugfs_fops[4]);
|
||||
debugfs_create_file("poll", 0444, hdcp_dent, hdmi,
|
||||
&hdcp_debugfs_fops[5]);
|
||||
debugfs_create_file("aksv", 0444, hdcp_dent, hdmi,
|
||||
&hdcp_debugfs_fops[6]);
|
||||
debugfs_create_file("bksv", 0444, hdcp_dent, hdmi,
|
||||
&hdcp_debugfs_fops[7]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
960
drivers/video/msm/hdmi/silicon-image/tpi.c
Normal file
960
drivers/video/msm/hdmi/silicon-image/tpi.c
Normal file
@ -0,0 +1,960 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <mach/msm_fb.h>
|
||||
#include <mach/msm_hdmi.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
// FIXME: remove this if unnecessary in the future
|
||||
#ifdef CONFIG_HTC_HEADSET_MGR
|
||||
#include <mach/htc_headset_mgr.h>
|
||||
#endif
|
||||
|
||||
#if 1
|
||||
#define HDMI_DBG(s...) printk("[hdmi/tpi]" s)
|
||||
#else
|
||||
#define HDMI_DBG(s...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#include "../include/fb-hdmi.h"
|
||||
#include "../include/sil902x.h"
|
||||
#include "../include/tpi.h"
|
||||
|
||||
#define NEW_INTEGRATE
|
||||
#define DBG_POLLING 0x1
|
||||
static int debug_mask;
|
||||
module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
|
||||
|
||||
#define DLOG(mask, fmt, args...) \
|
||||
do { \
|
||||
if (debug_mask & mask) \
|
||||
printk(KERN_INFO "[hdmi/sil]: "fmt, ##args); \
|
||||
} while (0)
|
||||
|
||||
#define X1 0x01
|
||||
#define AFTER_INIT 1
|
||||
|
||||
void HotPlugService (struct hdmi_info *hdmi);
|
||||
// FIXME: should be decide by detection
|
||||
static bool dsRxPoweredUp;
|
||||
static bool edidDataValid;
|
||||
static bool tmdsPoweredUp;
|
||||
u8 pvid_mode, vid_mode = 16;
|
||||
u8 systemInitialized;
|
||||
|
||||
void tpi_clear_interrupt(struct hdmi_info *hdmi, u8 pattern)
|
||||
{
|
||||
/* write "1" to clear interrupt bit, and 0 won't effect origin value. */
|
||||
hdmi_write_byte(hdmi->client, TPI_INTERRUPT_STATUS_REG, pattern);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// FUNCTION : EnableInterrupts()
|
||||
// PURPOSE : Enable the interrupts specified in the input parameter
|
||||
// INPUT PARAMS : A bit pattern with "1" for each interrupt that needs to be
|
||||
// set in the Interrupt Enable Register (TPI offset 0x3C)
|
||||
// OUTPUT PARAMS : void
|
||||
// GLOBALS USED : None
|
||||
// RETURNS : TRUE
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
bool tpi_enable_interrupts(struct hdmi_info *hdmi, u8 Interrupt_Pattern)
|
||||
{
|
||||
HDMI_DBG("%s, reg=%02x, pat=%02x\n", __func__, TPI_INTERRUPT_EN, Interrupt_Pattern);
|
||||
ReadSetWriteTPI(hdmi, TPI_INTERRUPT_EN, Interrupt_Pattern);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void tpi_disable_interrupts(struct hdmi_info *hdmi, u8 pattern)
|
||||
{
|
||||
/*
|
||||
HDMI_DBG("%s, reg=%02x, pat=%02x\n", __func__,
|
||||
TPI_INTERRUPT_EN, pattern);
|
||||
*/
|
||||
ReadClearWriteTPI(hdmi, TPI_INTERRUPT_EN, pattern);
|
||||
}
|
||||
|
||||
static void tpi_clear_pending_event(struct hdmi_info *hdmi)
|
||||
{
|
||||
int retry = 100;
|
||||
|
||||
if (hdmi->sleeping == SLEEP) return;
|
||||
while (retry--) {
|
||||
hdmi_write_byte(hdmi->client, 0x3c, 1);
|
||||
hdmi_write_byte(hdmi->client, 0x3d, 1);
|
||||
if (hdmi_read(hdmi->client, 0x3d) & 0x01)
|
||||
msleep(1);
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (retry < 19) HDMI_DBG("%s: retry=%d\n", __func__, 19 - retry);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// FUNCTION : ReadBackDoorRegister()
|
||||
// PURPOSE : Read a 922x register value from a backdoor register
|
||||
// Write:
|
||||
// 1. 0xBC => Internal page num
|
||||
// 2. 0xBD => Backdoor register offset
|
||||
// Read:
|
||||
// 3. 0xBE => Returns the backdoor register value
|
||||
// INPUT PARAMS : Internal page number, backdoor register offset, pointer to
|
||||
// buffer to store read value
|
||||
// OUTPUT PARAMS: Buffer that stores the read value
|
||||
// RETURNS : TRUE
|
||||
// NOTE : This workaround is needed for the 9220/2 only.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
int tpi_read_backdoor_register(struct hdmi_info *hdmi, u8 PageNum, u8 RegOffset)
|
||||
{
|
||||
// FIXME: error handling
|
||||
struct i2c_client *client = hdmi->client;
|
||||
|
||||
/* Internal page */
|
||||
hdmi_write_byte(client, TPI_INTERNAL_PAGE_REG, PageNum);
|
||||
/* Indexed register */
|
||||
hdmi_write_byte(client, TPI_REGISTER_OFFSET_REG, RegOffset);
|
||||
/* Read value into buffer */
|
||||
return hdmi_read(client, TPI_REGISTER_VALUE_REG);
|
||||
}
|
||||
|
||||
void tpi_write_backdoor_register(struct hdmi_info *hdmi, u8 PageNum, u8 RegOffset, u8 RegValue) {
|
||||
/* Internal page */
|
||||
hdmi_write_byte(hdmi->client, TPI_INTERNAL_PAGE_REG, PageNum);
|
||||
/* Indexed register */
|
||||
hdmi_write_byte(hdmi->client, TPI_REGISTER_OFFSET_REG, RegOffset);
|
||||
/* Read value into buffer */
|
||||
hdmi_write_byte(hdmi->client, TPI_REGISTER_VALUE_REG, RegValue);
|
||||
}
|
||||
|
||||
#define TPI_INTERNAL_PAGE_REG 0xBC
|
||||
#define TPI_INDEXED_OFFSET_REG 0xBD
|
||||
#define TPI_INDEXED_VALUE_REG 0xBE
|
||||
#define INDEXED_PAGE_0 0x01
|
||||
#define INDEXED_PAGE_1 0x02
|
||||
#define INDEXED_PAGE_2 0x03
|
||||
|
||||
void ReadModifyWriteIndexedRegister(struct hdmi_info *hdmi, u8 PageNum, u8 RegOffset, u8 Mask, u8 Value)
|
||||
{
|
||||
u8 Tmp;
|
||||
|
||||
hdmi_write_byte(hdmi->client, TPI_INTERNAL_PAGE_REG, PageNum);
|
||||
hdmi_write_byte(hdmi->client, TPI_INDEXED_OFFSET_REG, RegOffset);
|
||||
Tmp = hdmi_read(hdmi->client, TPI_INDEXED_VALUE_REG);
|
||||
|
||||
Tmp &= ~Mask;
|
||||
Tmp |= (Value & Mask);
|
||||
|
||||
hdmi_write_byte(hdmi->client, TPI_INDEXED_VALUE_REG, Tmp);
|
||||
}
|
||||
|
||||
void ReadSetWriteTPI(struct hdmi_info *hdmi, u8 Offset, u8 Pattern)
|
||||
{
|
||||
u8 Tmp;
|
||||
struct i2c_client *client = hdmi->client;
|
||||
|
||||
Tmp = hdmi_read(client, Offset);
|
||||
Tmp |= Pattern;
|
||||
hdmi_write_byte(client, Offset, Tmp);
|
||||
}
|
||||
|
||||
int tpi_set_bit(struct hdmi_info *hdmi, u8 reg, u8 pattern)
|
||||
{
|
||||
return hdmi_write_byte(hdmi->client, reg,
|
||||
hdmi_read(hdmi->client, reg) | pattern);
|
||||
}
|
||||
////
|
||||
void ReadModifyWriteTPI(struct hdmi_info *hdmi, u8 Offset, u8 Mask, u8 Value)
|
||||
{
|
||||
u8 Tmp;
|
||||
struct i2c_client *client = hdmi->client;
|
||||
|
||||
Tmp = hdmi_read(client, Offset);
|
||||
Tmp &= ~Mask;
|
||||
Tmp |= (Value & Mask);
|
||||
hdmi_write_byte(client, Offset, Tmp);
|
||||
}
|
||||
////
|
||||
void ReadClearWriteTPI(struct hdmi_info *hdmi, u8 Offset, u8 Pattern)
|
||||
{
|
||||
u8 Tmp;
|
||||
|
||||
Tmp = hdmi_read(hdmi->client, Offset);
|
||||
Tmp &= ~Pattern;
|
||||
hdmi_write_byte(hdmi->client, Offset, Tmp);
|
||||
}
|
||||
void tpi_clear_bit(struct hdmi_info *hdmi, u8 reg, u8 pattern)
|
||||
{
|
||||
hdmi_write_byte(hdmi->client, reg,
|
||||
hdmi_read(hdmi->client, reg) & pattern);
|
||||
}
|
||||
////
|
||||
|
||||
/* Caller: ChangeVideoMode(), HDCP_Poll(), HotPlugServiceLoop(), RestartHDCP()
|
||||
*/
|
||||
|
||||
void EnableTMDS(struct hdmi_info *hdmi)
|
||||
{
|
||||
u8 val;
|
||||
#if 1
|
||||
/* 0x1A[4] = 0 */
|
||||
ReadClearWriteTPI(hdmi, TPI_SYSTEM_CONTROL, BIT_TMDS_OUTPUT);
|
||||
|
||||
if (edid_check_sink_type(hdmi))
|
||||
hdmi_write_byte(hdmi->client, 0x26,
|
||||
hdmi_read(hdmi->client, 0x26) & ~0x10);
|
||||
|
||||
#else
|
||||
struct i2c_client *client = hdmi->i2c_client;
|
||||
|
||||
val = hdmi_read(client, TPI_SYSTEM_CONTROL);
|
||||
hdmi_write_byte(client, TPI_SYSTEM_CONTROL, val & ~BIT_TMDS_OUTPUT);
|
||||
HDMI_DBG("%s, reg 0x1a: %02x->%02x\n", __func__,
|
||||
val, val & ~BIT_TMDS_OUTPUT);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/* Caller: ChangeVideoMode(), HDCP_Poll(), TPI_Poll(), RestartHDCP(),
|
||||
* OnHdmiCableDisconnected()
|
||||
*/
|
||||
|
||||
void DisableTMDS(struct hdmi_info *hdmi)
|
||||
{
|
||||
/* 0x1A[4] = 1 */
|
||||
//ReadClearWriteTPI(hdmi, TPI_SYSTEM_CONTROL, BIT_TMDS_OUTPUT);
|
||||
ReadSetWriteTPI(hdmi, TPI_SYSTEM_CONTROL, BIT_TMDS_OUTPUT);
|
||||
}
|
||||
static void OnDownstreamRxPoweredDown(struct hdmi_info *hdmi)
|
||||
{
|
||||
HDMI_DBG("%s\n", __func__);
|
||||
dsRxPoweredUp = false;
|
||||
hdcp_off(hdmi);
|
||||
}
|
||||
|
||||
static void OnDownstreamRxPoweredUp(struct hdmi_info *hdmi)
|
||||
{
|
||||
HDMI_DBG("%s\n", __func__);
|
||||
dsRxPoweredUp = true;
|
||||
HotPlugService(hdmi);
|
||||
#ifdef CONFIG_HTC_HEADSET_MGR
|
||||
/* send cable in event */
|
||||
switch_send_event(BIT_HDMI_CABLE, 1);
|
||||
HDMI_DBG("Cable inserted.\n");
|
||||
#endif
|
||||
pvid_mode = vid_mode;
|
||||
hdmi_active9022_dup(hdmi->client);
|
||||
}
|
||||
|
||||
bool GetDDC_Access(struct hdmi_info *hdmi, u8* SysCtrlRegVal)
|
||||
{
|
||||
u8 sysCtrl, TPI_ControlImage, DDCReqTimeout = T_DDC_ACCESS;
|
||||
|
||||
HDMI_DBG("%s\n", __func__);
|
||||
/* Read and store original value. Will be passed into ReleaseDDC() */
|
||||
sysCtrl = hdmi_read(hdmi->client, TPI_SYSTEM_CONTROL);
|
||||
*SysCtrlRegVal = sysCtrl;
|
||||
|
||||
sysCtrl |= BIT_DDC_BUS_REQ;
|
||||
hdmi_write_byte(hdmi->client, TPI_SYSTEM_CONTROL, sysCtrl);
|
||||
|
||||
/* Loop till 0x1A[1] reads "1" */
|
||||
while (DDCReqTimeout--) {
|
||||
TPI_ControlImage = hdmi_read(hdmi->client, TPI_SYSTEM_CONTROL);
|
||||
|
||||
/* When 0x1A[1] reads "1" */
|
||||
if (TPI_ControlImage & BIT_DDC_BUS_GRANT) {
|
||||
sysCtrl |= BIT_DDC_BUS_GRANT;
|
||||
/* lock host DDC bus access (0x1A[2:1] = 11) */
|
||||
hdmi_write_byte(hdmi->client, TPI_SYSTEM_CONTROL, sysCtrl);
|
||||
return true;
|
||||
}
|
||||
/* 0x1A[2] = "1" - Requst the DDC bus */
|
||||
hdmi_write_byte(hdmi->client, TPI_SYSTEM_CONTROL, sysCtrl);
|
||||
mdelay(200);
|
||||
}
|
||||
|
||||
/* Failure... restore original value. */
|
||||
hdmi_write_byte(hdmi->client, TPI_SYSTEM_CONTROL, sysCtrl);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ReleaseDDC(struct hdmi_info *hdmi, u8 SysCtrlRegVal)
|
||||
{
|
||||
u8 DDCReqTimeout = T_DDC_ACCESS, TPI_ControlImage;
|
||||
|
||||
HDMI_DBG("%s\n", __func__);
|
||||
/* Just to be sure bits [2:1] are 0 before it is written */
|
||||
SysCtrlRegVal &= ~(0x6);
|
||||
/* Loop till 0x1A[1] reads "0" */
|
||||
while (DDCReqTimeout--) {
|
||||
/* Cannot use ReadClearWriteTPI() here. A read of
|
||||
* TPI_SYSTEM_CONTROL is invalid while DDC is granted.
|
||||
* Doing so will return 0xFF, and cause an invalid value to be
|
||||
* written back.
|
||||
*/
|
||||
/* 0x1A[2:1] = "0" - release the DDC bus */
|
||||
//ReadClearWriteTPI(TPI_SYSTEM_CONTROL,BITS_2_1);
|
||||
|
||||
hdmi_write_byte(hdmi->client, TPI_SYSTEM_CONTROL, SysCtrlRegVal);
|
||||
TPI_ControlImage = hdmi_read(hdmi->client, TPI_SYSTEM_CONTROL);
|
||||
/* When 0x1A[2:1] read "0" */
|
||||
if (!(TPI_ControlImage & 0x6))
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Failed to release DDC bus control */
|
||||
return false;
|
||||
}
|
||||
|
||||
int tpi_read_edid(struct hdmi_info *hdmi)
|
||||
{
|
||||
u8 SysCtrlReg;
|
||||
int ret, edid_blocks = 0;
|
||||
struct i2c_msg msg;
|
||||
u8 i2c_buff[2];
|
||||
u8 pbuf[] = {1, 0, 1, 128} ;
|
||||
|
||||
struct i2c_msg paging_msg[] = {
|
||||
{
|
||||
.addr = 0x30, .flags = 0, .len = 1, .buf = &pbuf[0],
|
||||
},
|
||||
{
|
||||
.addr = 0x50, .flags = 0, .len = 1, .buf = &pbuf[1],
|
||||
},
|
||||
{ //Block-2
|
||||
.addr = 0x50, .flags = I2C_M_RD, .len = 128, .buf = &hdmi->edid_buf[256],
|
||||
},
|
||||
{
|
||||
.addr = 0x30, .flags = 0, .len = 1, .buf = &pbuf[2],
|
||||
},
|
||||
{
|
||||
.addr = 0x50, .flags = 0, .len = 1, .buf = &pbuf[3],
|
||||
},
|
||||
{ //Block-3
|
||||
.addr = 0x50, .flags = I2C_M_RD, .len = 128, .buf = &hdmi->edid_buf[384],
|
||||
},
|
||||
};
|
||||
|
||||
HDMI_DBG("%s\n", __func__);
|
||||
#if 0
|
||||
DisableTMDS(hdmi);
|
||||
#else
|
||||
u8 val;
|
||||
val = hdmi_read(hdmi->client, TPI_SYSTEM_CONTROL);
|
||||
//hdmi_write_byte(hdmi->client, TPI_SYSTEM_CONTROL, val|BIT_4|BIT_6);
|
||||
hdmi_write_byte(hdmi->client, TPI_SYSTEM_CONTROL, val|BIT_4);
|
||||
#endif
|
||||
|
||||
if (!GetDDC_Access(hdmi, &SysCtrlReg)) {
|
||||
pr_err("%s: DDC bus request failed\n", __func__);
|
||||
return DDC_BUS_REQ_FAILURE;
|
||||
}
|
||||
|
||||
// Block-0
|
||||
memset(hdmi->edid_buf, 0, 512);
|
||||
|
||||
msg.addr = 0x50;
|
||||
msg.flags = 0;
|
||||
msg.len = 1;
|
||||
msg.buf = hdmi->edid_buf;
|
||||
ret = i2c_transfer(hdmi->client->adapter, &msg, 1);
|
||||
if (ret < 0)
|
||||
dev_err(&hdmi->client->dev, "%s: i2c transfer error\n", __func__);
|
||||
|
||||
msg.addr = 0x50;
|
||||
msg.flags = I2C_M_RD;
|
||||
msg.len = 128;
|
||||
msg.buf = hdmi->edid_buf;
|
||||
ret = i2c_transfer(hdmi->client->adapter, &msg, 1);
|
||||
if (ret < 0) {
|
||||
dev_err(&hdmi->client->dev, "%s: i2c transfer error\n", __func__);
|
||||
goto end_read_edid;
|
||||
} else {
|
||||
if (hdmi->edid_buf[0x7e] <= 3)
|
||||
edid_blocks = hdmi->edid_buf[0x7e] ;
|
||||
|
||||
dev_info(&hdmi->client->dev, "EDID blocks = %d\n", edid_blocks);
|
||||
|
||||
if (edid_blocks == 0 ) {
|
||||
goto end_read_edid;
|
||||
}
|
||||
// Block-1
|
||||
msg.addr = 0x50;
|
||||
msg.flags = 0;
|
||||
msg.len = 1;
|
||||
i2c_buff[0] = 128;
|
||||
msg.buf = i2c_buff;
|
||||
ret = i2c_transfer(hdmi->client->adapter, &msg, 1);
|
||||
|
||||
msg.addr = 0x50;
|
||||
msg.flags = I2C_M_RD;
|
||||
msg.len = 128;
|
||||
msg.buf = &hdmi->edid_buf[128];
|
||||
ret = i2c_transfer(hdmi->client->adapter, &msg, 1);
|
||||
}
|
||||
|
||||
if (edid_blocks > 1) {
|
||||
// block 2/3
|
||||
i2c_transfer(hdmi->client->adapter, paging_msg, 3);
|
||||
i2c_transfer(hdmi->client->adapter, &paging_msg[3], 3);
|
||||
}
|
||||
|
||||
end_read_edid:
|
||||
if (!ReleaseDDC(hdmi, SysCtrlReg)) {
|
||||
pr_err("%s: DDC bus release failed\n", __func__);
|
||||
return DDC_BUS_REQ_FAILURE;
|
||||
}
|
||||
|
||||
edid_simple_parsing(hdmi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// FUNCTION : HotPlugService()
|
||||
// PURPOSE : Implement Hot Plug Service Loop activities
|
||||
// INPUT PARAMS : None
|
||||
// OUTPUT PARAMS: void
|
||||
// GLOBALS USED : LinkProtectionLevel
|
||||
// RETURNS : An error code that indicates success or cause of failure
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
extern bool HDCP_TxSupports;
|
||||
static bool tmdsPoweredUp;
|
||||
void HotPlugService (struct hdmi_info *hdmi)
|
||||
{
|
||||
HDMI_DBG("%s\n", __func__);
|
||||
|
||||
mutex_lock(&hdmi->lock);
|
||||
tpi_disable_interrupts(hdmi, 0xFF);
|
||||
|
||||
/*
|
||||
// use 1st mode supported by sink
|
||||
//vid_mode = EDID_Data.VideoDescriptor[0];
|
||||
vid_mode = 0;
|
||||
*/
|
||||
avc_init_video(hdmi, vid_mode, X1, AFTER_INIT);
|
||||
|
||||
hdmi_write_byte(hdmi->client, HDMI_POWER, 0);
|
||||
if (edid_check_sink_type(hdmi))
|
||||
avc_send_avi_info_frames(hdmi);
|
||||
|
||||
/* This check needs to be changed to if HDCP is required by the content
|
||||
once support has been added by RX-side library. */
|
||||
if (HDCP_TxSupports == true) {
|
||||
HDMI_DBG("TMDS -> Enabled\n");
|
||||
/* turn on black mode will lost around 3 secs frames thus remove it */
|
||||
//SetInputColorSpace(hdmi, INPUT_COLOR_SPACE_BLACK_MODE);
|
||||
#if 1
|
||||
ReadModifyWriteTPI(hdmi, TPI_SYSTEM_CONTROL,
|
||||
LINK_INTEGRITY_MODE_MASK | TMDS_OUTPUT_CONTROL_MASK,
|
||||
LINK_INTEGRITY_DYNAMIC | TMDS_OUTPUT_CONTROL_ACTIVE);
|
||||
#else
|
||||
ReadModifyWriteTPI(hdmi, TPI_SYSTEM_CONTROL,
|
||||
LINK_INTEGRITY_MODE_MASK | TMDS_OUTPUT_CONTROL_MASK,
|
||||
LINK_INTEGRITY_DYNAMIC);
|
||||
#endif
|
||||
tmdsPoweredUp = true;
|
||||
} else {
|
||||
EnableTMDS(hdmi);
|
||||
}
|
||||
|
||||
if (edid_check_sink_type(hdmi))
|
||||
avc_set_basic_audio(hdmi);
|
||||
else
|
||||
SetAudioMute(hdmi, AUDIO_MUTE_MUTED);
|
||||
|
||||
tpi_enable_interrupts(hdmi, HOT_PLUG_EVENT | RX_SENSE_EVENT |
|
||||
AUDIO_ERROR_EVENT | SECURITY_CHANGE_EVENT |
|
||||
V_READY_EVENT | HDCP_CHANGE_EVENT);
|
||||
|
||||
//complete(&hdmi->hotplug_completion);
|
||||
mutex_unlock(&hdmi->lock);
|
||||
}
|
||||
|
||||
static bool tpi_start(struct hdmi_info *hdmi)
|
||||
{
|
||||
u8 devID = 0x00;
|
||||
u16 wID = 0x0000;
|
||||
|
||||
hdmi_write_byte(hdmi->client, TPI_ENABLE, 0x00); // Write "0" to 72:C7 to start HW TPI mode
|
||||
mdelay(100);
|
||||
|
||||
devID = tpi_read_backdoor_register(hdmi, 0x00, 0x03);
|
||||
wID = devID;
|
||||
wID <<= 8;
|
||||
devID = tpi_read_backdoor_register(hdmi, 0x00, 0x02);
|
||||
wID |= devID;
|
||||
devID = hdmi_read(hdmi->client, TPI_DEVICE_ID);
|
||||
HDMI_DBG("%s, ID=%04X\n", __func__, (u32)wID);
|
||||
|
||||
if (devID == SiI_DEVICE_ID) {
|
||||
return true;
|
||||
}
|
||||
|
||||
pr_err("%s: Unsupported TX\n", __func__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tpi_init(struct hdmi_info *hdmi)
|
||||
{
|
||||
tmdsPoweredUp = false;
|
||||
hdmi->cable_connected = false;
|
||||
dsRxPoweredUp = false;
|
||||
edidDataValid = false;
|
||||
|
||||
/* Enable HW TPI mode, check device ID */
|
||||
if (tpi_start(hdmi)) {
|
||||
hdcp_init(hdmi);
|
||||
return true;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SetAudioMute(struct hdmi_info *hdmi, u8 audioMute)
|
||||
{
|
||||
ReadModifyWriteTPI(hdmi, TPI_AUDIO_INTERFACE_REG, AUDIO_MUTE_MASK, audioMute);
|
||||
}
|
||||
|
||||
void SetInputColorSpace(struct hdmi_info *hdmi, u8 inputColorSpace)
|
||||
{
|
||||
ReadModifyWriteTPI(hdmi, TPI_INPUT_FORMAT_REG, INPUT_COLOR_SPACE_MASK, inputColorSpace);
|
||||
/* Must be written for previous write to take effect. Just write read value unmodified. */
|
||||
ReadModifyWriteTPI(hdmi, TPI_END_RIGHT_BAR_MSB, 0x00, 0x00);
|
||||
}
|
||||
|
||||
static char edid_hex_buff[2048];
|
||||
int lcdc_enable_video(void);
|
||||
int lcdc_disable_video(void);
|
||||
void tpi_cable_conn(struct hdmi_info *hdmi)
|
||||
{
|
||||
HDMI_DBG("%s\n", __func__);
|
||||
|
||||
hdmi->cable_connected = true;
|
||||
tpi_write_backdoor_register(hdmi, INTERNAL_PAGE_0, 0xCE, 0x00); // Clear BStatus
|
||||
tpi_write_backdoor_register(hdmi, INTERNAL_PAGE_0, 0xCF, 0x00);
|
||||
|
||||
//-----------------------------------------------
|
||||
hdmi_write_byte(hdmi->client, 0x09, 0x03);
|
||||
hdmi_write_byte(hdmi->client, 0x19, 0x00); // go to blank mode, avoid screen noise
|
||||
|
||||
/*
|
||||
HDMI_DBG("solomon: H/V total=%02x, %02x, %02x, %02x\n",
|
||||
hdmi_read(hdmi->client, 0x6a),
|
||||
hdmi_read(hdmi->client, 0x6b),
|
||||
hdmi_read(hdmi->client, 0x6c),
|
||||
hdmi_read(hdmi->client, 0x6d)
|
||||
);
|
||||
*/
|
||||
|
||||
lcdc_enable_video();
|
||||
msleep(160);
|
||||
/*
|
||||
//clk_set_rate(hdmi->ebi1_clk, 120000000);
|
||||
HDMI_DBG("solomon: H/V total=%02x, %02x, %02x, %02x\n",
|
||||
hdmi_read(hdmi->client, 0x6a),
|
||||
hdmi_read(hdmi->client, 0x6b),
|
||||
hdmi_read(hdmi->client, 0x6c),
|
||||
hdmi_read(hdmi->client, 0x6d)
|
||||
);
|
||||
*/
|
||||
EnableTMDS(hdmi);
|
||||
|
||||
//-----------------------------------------------
|
||||
|
||||
tpi_read_edid(hdmi);
|
||||
memset(edid_hex_buff, 0, 2048);
|
||||
edid_dump_hex(hdmi->edid_buf, 256, edid_hex_buff, 2048);
|
||||
printk("EDID data:\n%s\n=====", edid_hex_buff);
|
||||
/* select output mode (HDMI/DVI) according to sink capabilty */
|
||||
if (edid_check_sink_type(hdmi))
|
||||
ReadModifyWriteTPI(hdmi, TPI_SYSTEM_CONTROL, OUTPUT_MODE_MASK, OUTPUT_MODE_HDMI);
|
||||
else
|
||||
ReadModifyWriteTPI(hdmi, TPI_SYSTEM_CONTROL, OUTPUT_MODE_MASK, OUTPUT_MODE_DVI);
|
||||
|
||||
hdmi->first = false;
|
||||
#if 0
|
||||
#ifdef CONFIG_HTC_HEADSET_MGR
|
||||
/* send cable in event */
|
||||
switch_send_event(BIT_HDMI_CABLE, 1);
|
||||
HDMI_DBG("Cable inserted.\n");
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void tpi_cable_disconn(struct hdmi_info *hdmi, bool into_d3)
|
||||
{
|
||||
HDMI_DBG("%s, into_d3=%d\n", __func__, into_d3);
|
||||
|
||||
hdmi->cable_connected = false;
|
||||
dsRxPoweredUp = false;
|
||||
edidDataValid = false;
|
||||
hdcp_off(hdmi);
|
||||
DisableTMDS(hdmi);
|
||||
#if 1
|
||||
/* wait for debounce */
|
||||
msleep(20);
|
||||
tpi_clear_pending_event(hdmi);
|
||||
#else
|
||||
reg = hdmi_read(hdmi->client, 0x3d);
|
||||
if (!(reg & 0x0c))
|
||||
tpi_clear_pending_event(hdmi);
|
||||
#endif
|
||||
if (into_d3) {
|
||||
mutex_lock(&hdmi->lock);
|
||||
HDMI_DBG("%s, playing=%d\n", __func__, hdmi->user_playing);
|
||||
if (false == hdmi->user_playing)
|
||||
lcdc_disable_video();
|
||||
clk_set_rate(hdmi->ebi1_clk, 0);
|
||||
hdmi_standby(hdmi);
|
||||
hdmi->power(2);
|
||||
memset(hdmi->edid_buf, 0, 512);
|
||||
mutex_unlock(&hdmi->lock);
|
||||
}
|
||||
#ifdef CONFIG_HTC_HEADSET_MGR
|
||||
HDMI_DBG("Cable unplugged.\n");
|
||||
switch_send_event(BIT_HDMI_CABLE, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static char *str_debug_interrupt[] = {
|
||||
"HOT_PLUG_EVENT\t\t\t",
|
||||
"RECEIVER_SENSE_EVENT\t\t",
|
||||
"HOT_PLUG_PIN_STATE\t\t",
|
||||
"RX_SENSE_MASK\t\t\t",
|
||||
"AUDIO_ERROR_EVENT\t\t",
|
||||
"HDCP_SECURITY_CHANGE_EVENT\t",
|
||||
"HDCP_VPRIME_VALUE_READY_EVENT\t",
|
||||
"HDCP_AUTH_STATUS_CHANGE_EVENT\t",
|
||||
};
|
||||
|
||||
void tpi_debug_interrupt(struct hdmi_info *hdmi, u8 old_status, u8 new_status)
|
||||
{
|
||||
int i, diff, on_off;
|
||||
HDMI_DBG("%s: status changed, %02x to %02x\n", __func__,
|
||||
old_status, new_status);
|
||||
for (i = 7; i >= 0; i--) {
|
||||
diff = (old_status ^ new_status) & (1 << i);
|
||||
if (!diff)
|
||||
continue;
|
||||
on_off = new_status & (1 << i);
|
||||
HDMI_DBG("%d-%s->%s\n", i, str_debug_interrupt[i],
|
||||
on_off ? "on" : "off");
|
||||
}
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// FUNCTION : TPI_Poll ()
|
||||
// PURPOSE : Poll Interrupt Status register for new interrupts
|
||||
// INPUT PARAMS : None
|
||||
// OUTPUT PARAMS: None
|
||||
// GLOBALS USED : LinkProtectionLevel
|
||||
// RETURNS : None
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
static u8 last_status = 0;
|
||||
static void tpi_poll(struct hdmi_info *hdmi)
|
||||
{
|
||||
u8 status, orig_status;
|
||||
int retry = 20;
|
||||
|
||||
mutex_lock(&hdmi->polling_lock);
|
||||
orig_status = status = hdmi_read(hdmi->client, TPI_INTERRUPT_STATUS_REG);
|
||||
if (last_status != status) {
|
||||
tpi_debug_interrupt(hdmi, last_status, status);
|
||||
}
|
||||
last_status = status;
|
||||
DLOG(DBG_POLLING, "%s, INT_STAT=%02x\n", __func__, status);
|
||||
#if 0
|
||||
if (status & HOT_PLUG_EVENT) {
|
||||
#else
|
||||
if (hdmi->first || status & HOT_PLUG_EVENT) {
|
||||
if (hdmi->first) hdmi->first = false;
|
||||
#endif
|
||||
// Enable HPD interrupt bit
|
||||
ReadSetWriteTPI(hdmi, TPI_INTERRUPT_ENABLE_REG, HOT_PLUG_EVENT);
|
||||
// Repeat this loop while cable is bouncing:
|
||||
do {
|
||||
//DLOG(DBG_POLLING, "TPI: Interrupt status image - 2= %02x\n", status);
|
||||
hdmi_write_byte(hdmi->client, TPI_INTERRUPT_STATUS_REG, HOT_PLUG_EVENT);
|
||||
// Delay for metastability protection and to help filter out connection bouncing
|
||||
mdelay(T_HPD_DELAY);
|
||||
// Read Interrupt status register
|
||||
status = hdmi_read(hdmi->client, TPI_INTERRUPT_STATUS_REG);
|
||||
//DLOG(DBG_POLLING, "TPI: Interrupt status image - 3= %02x\n", status);
|
||||
if (!retry--) {
|
||||
HDMI_DBG("%s: retry failed\n", __func__);
|
||||
break;
|
||||
}
|
||||
|
||||
} while (status & HOT_PLUG_EVENT);// loop as long as HP interrupts recur
|
||||
DLOG(DBG_POLLING, "int status: %02x, after debouncing: %02x\n",
|
||||
orig_status, status);
|
||||
|
||||
//DLOG(DBG_POLLING, "TPI->hdmiCableConnected = %d\n", hdmi->cable_connected);
|
||||
if (((status & HOT_PLUG_STATE) >> 2) != hdmi->cable_connected) {
|
||||
DLOG(DBG_POLLING, "cable status changed: from %d to %d\n",
|
||||
hdmi->cable_connected, !!(status & HOT_PLUG_STATE));
|
||||
//DLOG(DBG_POLLING, "TPI-> CONDITION\n");
|
||||
if (hdmi->cable_connected == true)
|
||||
tpi_cable_disconn(hdmi, status & 0x8 ? false : true);
|
||||
else {
|
||||
tpi_cable_conn(hdmi);
|
||||
ReadModifyWriteIndexedRegister(hdmi, INDEXED_PAGE_0, 0x0A, 0x08, 0x08);
|
||||
}
|
||||
if (hdmi->cable_connected == false) {
|
||||
mutex_unlock(&hdmi->polling_lock);
|
||||
return;
|
||||
}
|
||||
} else if ( false == hdmi->cable_connected)
|
||||
/* only occur while booting without cable attached. */
|
||||
tpi_cable_disconn(hdmi, true);
|
||||
}
|
||||
|
||||
// Check rx power
|
||||
if (((status & RX_SENSE_STATE) >> 3) != dsRxPoweredUp)
|
||||
{
|
||||
if (hdmi->cable_connected == true) {
|
||||
if (dsRxPoweredUp == true)
|
||||
OnDownstreamRxPoweredDown(hdmi);
|
||||
else
|
||||
OnDownstreamRxPoweredUp(hdmi);
|
||||
}
|
||||
tpi_clear_interrupt(hdmi, RX_SENSE_EVENT);
|
||||
}
|
||||
|
||||
// Check if Audio Error event has occurred:
|
||||
if (status & AUDIO_ERROR_EVENT)
|
||||
// The hardware handles the event without need for host intervention (PR, p. 31)
|
||||
tpi_clear_interrupt(hdmi, AUDIO_ERROR_EVENT);
|
||||
|
||||
if (hdmi->video_streaming) {
|
||||
if ((hdmi->cable_connected == true) && (dsRxPoweredUp == true))
|
||||
hdcp_check_status(hdmi, status);
|
||||
}
|
||||
mutex_unlock(&hdmi->polling_lock);
|
||||
}
|
||||
|
||||
static void tpi_work_func(struct work_struct *work)
|
||||
{
|
||||
u8 reg = 0;
|
||||
struct hdmi_info *hdmi =
|
||||
container_of(work, struct hdmi_info, polling_work);
|
||||
|
||||
if (hdmi->sleeping == SLEEP) {
|
||||
mutex_lock(&hdmi->lock);
|
||||
hdmi->power(3);
|
||||
hdmi_wakeup(hdmi);
|
||||
tpi_init(hdmi);
|
||||
hdcp_off(hdmi);
|
||||
mutex_unlock(&hdmi->lock);
|
||||
}
|
||||
|
||||
tpi_poll(hdmi);
|
||||
#if 1
|
||||
mutex_lock(&hdmi->lock);
|
||||
if (hdmi->sleeping == AWAKE)
|
||||
reg = hdmi_read(hdmi->client, 0x3d) & 0x0c;
|
||||
if (hdmi->cable_connected || reg) {
|
||||
hdmi->polling = true;
|
||||
mod_timer(&hdmi->timer, jiffies + INTERVAL_HDCP_POLLING);
|
||||
} else {
|
||||
enable_irq(hdmi->client->irq);
|
||||
hdmi->isr_enabled = true;
|
||||
hdmi->polling = false;
|
||||
}
|
||||
mutex_unlock(&hdmi->lock);
|
||||
#else
|
||||
if (hdmi->sleeping == AWAKE) {
|
||||
reg = hdmi_read(hdmi->client, 0x3d);
|
||||
if (reg & 0x0c) {
|
||||
hdmi->polling = true;
|
||||
mod_timer(&hdmi->timer, jiffies + INTERVAL_HDCP_POLLING);
|
||||
} else {
|
||||
tpi_clear_pending_event(hdmi);
|
||||
}
|
||||
}
|
||||
|
||||
if (hdmi->cable_connected ) {
|
||||
hdmi->polling = true;
|
||||
mod_timer(&hdmi->timer, jiffies + INTERVAL_HDCP_POLLING);
|
||||
} else {
|
||||
enable_irq(hdmi->client->irq);
|
||||
hdmi->isr_enabled = true;
|
||||
hdmi->polling = false;
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
HDMI_DBG("after polling: reg=%02x, conn=%d, isr=%d, polling=%d\n",
|
||||
reg, hdmi->cable_connected, hdmi->isr_enabled, hdmi->polling);
|
||||
*/
|
||||
}
|
||||
|
||||
static void tpi_timer_func(unsigned long arg)
|
||||
{
|
||||
struct hdmi_info *hdmi = (struct hdmi_info *) arg;
|
||||
|
||||
schedule_work(&hdmi->polling_work);
|
||||
}
|
||||
|
||||
int tpi_prepare(struct hdmi_info *hdmi)
|
||||
{
|
||||
HDMI_DBG("%s\n", __func__);
|
||||
init_timer(&hdmi->timer);
|
||||
hdmi->timer.data = (unsigned long)hdmi;
|
||||
hdmi->timer.function = tpi_timer_func;
|
||||
hdmi->cable_connected = false;
|
||||
|
||||
init_completion(&hdmi->hotplug_completion);
|
||||
INIT_WORK(&hdmi->polling_work, tpi_work_func);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*============================================================================*/
|
||||
#if defined(HDMI_DEBUGFS)
|
||||
static ssize_t tpi_dbg_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
file->private_data = inode->i_private;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t tpi_ddc_request_read(struct file *filp, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
//struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t tpi_ddc_request_write(struct file *filp, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t tpi_isr_read(struct file *filp, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
int n = 0;
|
||||
char buffer[4];
|
||||
struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
|
||||
|
||||
HDMI_DBG("%s\n", __func__);
|
||||
n = scnprintf(buffer, 4, "%d\n", hdmi->isr_enabled);
|
||||
n++;
|
||||
buffer[n] = 0;
|
||||
return simple_read_from_buffer(buf, count, ppos, buffer, n);
|
||||
}
|
||||
|
||||
static ssize_t tpi_polling_read(struct file *filp, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
int n = 0;
|
||||
char buffer[4];
|
||||
struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
|
||||
|
||||
HDMI_DBG("%s\n", __func__);
|
||||
n = scnprintf(buffer, 4, "%d\n", hdmi->polling);
|
||||
n++;
|
||||
buffer[n] = 0;
|
||||
return simple_read_from_buffer(buf, count, ppos, buffer, n);
|
||||
}
|
||||
|
||||
static ssize_t tpi_int_status_read(struct file *filp, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
int n = 0;
|
||||
char buffer[8];
|
||||
struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
|
||||
|
||||
HDMI_DBG("%s\n", __func__);
|
||||
n = scnprintf(buffer, 8, "%02x\n", hdmi_read(hdmi->client, 0x3d));
|
||||
n++;
|
||||
buffer[n] = 0;
|
||||
return simple_read_from_buffer(buf, count, ppos, buffer, n);
|
||||
}
|
||||
|
||||
static ssize_t tpi_int_enable_read(struct file *filp, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
int n = 0;
|
||||
char buffer[8];
|
||||
struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
|
||||
|
||||
HDMI_DBG("%s\n", __func__);
|
||||
n = scnprintf(buffer, 8, "%02x\n", hdmi_read(hdmi->client, 0x3c));
|
||||
n++;
|
||||
buffer[n] = 0;
|
||||
return simple_read_from_buffer(buf, count, ppos, buffer, n);
|
||||
}
|
||||
|
||||
static ssize_t tpi_avc_read(struct file *filp, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
int n = 0;
|
||||
char buffer[8];
|
||||
struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data;
|
||||
|
||||
HDMI_DBG("%s\n", __func__);
|
||||
/*
|
||||
n = scnprintf(buffer, 8, "%02x\n", hdmi_read(hdmi->client, 0x3c));
|
||||
n++;
|
||||
buffer[n] = 0;
|
||||
return simple_read_from_buffer(buf, count, ppos, buffer, n);
|
||||
*/
|
||||
hdmi_active9022(hdmi->client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct file_operations tpi_debugfs_fops[] = {
|
||||
{
|
||||
.open = tpi_dbg_open,
|
||||
.read = tpi_ddc_request_read,
|
||||
.write = tpi_ddc_request_write,
|
||||
},
|
||||
{
|
||||
.open = tpi_dbg_open,
|
||||
.read = tpi_isr_read,
|
||||
},
|
||||
{
|
||||
.open = tpi_dbg_open,
|
||||
.read = tpi_polling_read,
|
||||
},
|
||||
{
|
||||
.open = tpi_dbg_open,
|
||||
.read = tpi_int_status_read,
|
||||
},
|
||||
{
|
||||
.open = tpi_dbg_open,
|
||||
.read = tpi_int_enable_read,
|
||||
},
|
||||
{
|
||||
.open = tpi_dbg_open,
|
||||
.read = tpi_avc_read,
|
||||
},
|
||||
};
|
||||
|
||||
int tpi_debugfs_init(struct hdmi_info *hdmi)
|
||||
{
|
||||
struct dentry *tpi_dent;
|
||||
|
||||
tpi_dent = debugfs_create_dir("tpi", hdmi->debug_dir);
|
||||
if (IS_ERR(tpi_dent))
|
||||
return PTR_ERR(tpi_dent);
|
||||
|
||||
//FIXME: error handling
|
||||
debugfs_create_file("ddc_request", 0644, tpi_dent, hdmi,
|
||||
&tpi_debugfs_fops[0]);
|
||||
debugfs_create_file("isr_enabled", 0444, tpi_dent, hdmi,
|
||||
&tpi_debugfs_fops[1]);
|
||||
debugfs_create_file("polling", 0444, tpi_dent, hdmi,
|
||||
&tpi_debugfs_fops[2]);
|
||||
debugfs_create_file("int_stat", 0444, tpi_dent, hdmi,
|
||||
&tpi_debugfs_fops[3]);
|
||||
debugfs_create_file("int_ena", 0444, tpi_dent, hdmi,
|
||||
&tpi_debugfs_fops[4]);
|
||||
debugfs_create_file("avc", 0444, tpi_dent, hdmi,
|
||||
&tpi_debugfs_fops[5]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
1019
drivers/video/msm/hdmi/transmitter.c
Normal file
1019
drivers/video/msm/hdmi/transmitter.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -44,7 +44,9 @@
|
||||
#define CMD_GET_CLIENT_STATUS 0x0602
|
||||
|
||||
static uint32_t mddi_debug_flags;
|
||||
|
||||
#ifdef CONFIG_MSM_MDP40
|
||||
static struct clk *mdp_clk;
|
||||
#endif
|
||||
union mddi_rev {
|
||||
unsigned char raw[MDDI_REV_BUFFER_SIZE];
|
||||
struct mddi_rev_packet hdr;
|
||||
@ -100,9 +102,11 @@ struct mddi_info {
|
||||
char client_name[20];
|
||||
|
||||
struct platform_device client_pdev;
|
||||
unsigned type;
|
||||
};
|
||||
|
||||
static void mddi_init_rev_encap(struct mddi_info *mddi);
|
||||
static void mddi_skew_calibration(struct mddi_info *mddi);
|
||||
|
||||
#define mddi_readl(r) readl(mddi->base + (MDDI_##r))
|
||||
#define mddi_writel(v, r) writel((v), mddi->base + (MDDI_##r))
|
||||
@ -160,7 +164,7 @@ static void mddi_handle_rev_data(struct mddi_info *mddi, union mddi_rev *rev)
|
||||
printk(KERN_INFO "rev: got reg %x = %x without "
|
||||
" pending read\n",
|
||||
rev->reg.register_address,
|
||||
rev->reg.register_data_list);
|
||||
rev->reg.u.reg_data);
|
||||
break;
|
||||
}
|
||||
if (ri->reg != rev->reg.register_address) {
|
||||
@ -168,12 +172,12 @@ static void mddi_handle_rev_data(struct mddi_info *mddi, union mddi_rev *rev)
|
||||
"wrong register, expected "
|
||||
"%x\n",
|
||||
rev->reg.register_address,
|
||||
rev->reg.register_data_list, ri->reg);
|
||||
rev->reg.u.reg_data, ri->reg);
|
||||
break;
|
||||
}
|
||||
mddi->reg_read = NULL;
|
||||
ri->status = 0;
|
||||
ri->result = rev->reg.register_data_list;
|
||||
ri->result = rev->reg.u.reg_data;
|
||||
complete(&ri->done);
|
||||
break;
|
||||
default:
|
||||
@ -397,9 +401,6 @@ static uint16_t mddi_init_registers(struct mddi_info *mddi)
|
||||
mddi_writel(0x0003, SPM); /* subframes per media */
|
||||
mddi_writel(0x0005, TA1_LEN);
|
||||
mddi_writel(MDDI_HOST_TA2_LEN, TA2_LEN);
|
||||
mddi_writel(0x0096, DRIVE_HI);
|
||||
/* 0x32 normal, 0x50 for Toshiba display */
|
||||
mddi_writel(0x0050, DRIVE_LO);
|
||||
mddi_writel(0x003C, DISP_WAKE); /* wakeup counter */
|
||||
mddi_writel(MDDI_HOST_REV_RATE_DIV, REV_RATE_DIV);
|
||||
|
||||
@ -418,8 +419,26 @@ static uint16_t mddi_init_registers(struct mddi_info *mddi)
|
||||
}
|
||||
|
||||
/* Recommendation from PAD hw team */
|
||||
mddi_writel(0xa850f, PAD_CTL);
|
||||
if (mddi->type == MSM_MDP_MDDI_TYPE_II)
|
||||
mddi_writel(0x402a850f, PAD_CTL);
|
||||
else
|
||||
mddi_writel(0xa850f, PAD_CTL);
|
||||
|
||||
#if defined (CONFIG_ARCH_QSD8X50) || defined (CONFIG_ARCH_MSM7X30)
|
||||
/* Only for novatek driver IC*/
|
||||
mddi_writel(0x00C8, DRIVE_HI);
|
||||
/* 0x32 normal, 0x50 for Toshiba display */
|
||||
mddi_writel(0x0050, DRIVE_LO);
|
||||
mddi_writel(0x00320000, PAD_IO_CTL);
|
||||
if (mddi->type == MSM_MDP_MDDI_TYPE_II)
|
||||
mddi_writel(0x40884020, PAD_CAL);
|
||||
else
|
||||
mddi_writel(0x00220020, PAD_CAL);
|
||||
#else
|
||||
mddi_writel(0x0096, DRIVE_HI);
|
||||
/* 0x32 normal, 0x50 for Toshiba display */
|
||||
mddi_writel(0x0050, DRIVE_LO);
|
||||
#endif
|
||||
|
||||
/* Need an even number for counts */
|
||||
mddi_writel(0x60006, DRIVER_START_CNT);
|
||||
@ -450,6 +469,9 @@ static void mddi_suspend(struct msm_mddi_client_data *cdata)
|
||||
mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
|
||||
/* turn off the clock */
|
||||
clk_disable(mddi->clk);
|
||||
#if CONFIG_MSM_MDP40
|
||||
clk_disable(mdp_clk);
|
||||
#endif
|
||||
wake_unlock(&mddi->idle_lock);
|
||||
}
|
||||
|
||||
@ -462,11 +484,18 @@ static void mddi_resume(struct msm_mddi_client_data *cdata)
|
||||
/* turn on the client */
|
||||
if (mddi->power_client)
|
||||
mddi->power_client(&mddi->client_data, 1);
|
||||
#if CONFIG_MSM_MDP40
|
||||
clk_enable(mdp_clk);
|
||||
#endif
|
||||
/* turn on the clock */
|
||||
clk_enable(mddi->clk);
|
||||
/* set up the local registers */
|
||||
mddi->rev_data_curr = 0;
|
||||
mddi_init_registers(mddi);
|
||||
/* FIXME: Workaround for Novatek
|
||||
if (mddi->type == MSM_MDP_MDDI_TYPE_II)
|
||||
mddi_skew_calibration(mddi);
|
||||
*/
|
||||
mddi_writel(mddi->int_enable, INTEN);
|
||||
mddi_writel(MDDI_CMD_LINK_ACTIVE, CMD);
|
||||
mddi_writel(MDDI_CMD_SEND_RTD, CMD);
|
||||
@ -493,37 +522,44 @@ static int __init mddi_get_client_caps(struct mddi_info *mddi)
|
||||
|
||||
mddi_writel(MDDI_CMD_LINK_ACTIVE, CMD);
|
||||
mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
|
||||
/*FIXME: mddi host can't get caps on MDDI type 2*/
|
||||
if (mddi->type == MSM_MDP_MDDI_TYPE_I) {
|
||||
for (j = 0; j < 3; j++) {
|
||||
/* the toshiba vga panel does not respond to get
|
||||
* caps unless you SEND_RTD, but the first SEND_RTD
|
||||
* will fail...
|
||||
*/
|
||||
for (i = 0; i < 4; i++) {
|
||||
uint32_t stat;
|
||||
|
||||
for (j = 0; j < 3; j++) {
|
||||
/* the toshiba vga panel does not respond to get
|
||||
* caps unless you SEND_RTD, but the first SEND_RTD
|
||||
* will fail...
|
||||
*/
|
||||
for (i = 0; i < 4; i++) {
|
||||
uint32_t stat;
|
||||
mddi_writel(MDDI_CMD_SEND_RTD, CMD);
|
||||
mdelay(1);
|
||||
mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
|
||||
stat = mddi_readl(STAT);
|
||||
printk(KERN_INFO "mddi cmd send rtd: int %x, stat %x, "
|
||||
"rtd val %x\n", mddi_readl(INT), stat,
|
||||
mddi_readl(RTD_VAL));
|
||||
if ((stat & MDDI_STAT_RTD_MEAS_FAIL) == 0) {
|
||||
mdelay(1);
|
||||
break;
|
||||
}
|
||||
msleep(1);
|
||||
}
|
||||
|
||||
mddi_writel(MDDI_CMD_SEND_RTD, CMD);
|
||||
mddi_writel(CMD_GET_CLIENT_CAP, CMD);
|
||||
mdelay(1);
|
||||
mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
|
||||
stat = mddi_readl(STAT);
|
||||
printk(KERN_INFO "mddi cmd send rtd: int %x, stat %x, "
|
||||
"rtd val %x\n", mddi_readl(INT), stat,
|
||||
mddi_readl(RTD_VAL));
|
||||
if ((stat & MDDI_STAT_RTD_MEAS_FAIL) == 0)
|
||||
break;
|
||||
msleep(1);
|
||||
}
|
||||
|
||||
mddi_writel(CMD_GET_CLIENT_CAP, CMD);
|
||||
mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
|
||||
wait_event_timeout(mddi->int_wait, mddi->flags & FLAG_HAVE_CAPS,
|
||||
wait_event_timeout(mddi->int_wait, mddi->flags & FLAG_HAVE_CAPS,
|
||||
HZ / 100);
|
||||
|
||||
if (mddi->flags & FLAG_HAVE_CAPS)
|
||||
break;
|
||||
printk(KERN_INFO KERN_ERR "mddi_init, timeout waiting for "
|
||||
if (mddi->flags & FLAG_HAVE_CAPS)
|
||||
break;
|
||||
printk(KERN_INFO KERN_ERR "mddi_init, timeout waiting for "
|
||||
"caps\n");
|
||||
}
|
||||
return mddi->flags & FLAG_HAVE_CAPS;
|
||||
}
|
||||
return (mddi->flags & FLAG_HAVE_CAPS);
|
||||
} else
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* link must be active when this is called */
|
||||
@ -564,53 +600,86 @@ int mddi_check_status(struct mddi_info *mddi)
|
||||
}
|
||||
|
||||
|
||||
void mddi_remote_write(struct msm_mddi_client_data *cdata, uint32_t val,
|
||||
uint32_t reg)
|
||||
/*
|
||||
* mddi_remote_write_vals - send the register access packet
|
||||
*
|
||||
* @cdata: mddi layer dedicated structure, holding info needed by mddi
|
||||
* @val : parameters
|
||||
* @reg : cmd
|
||||
* @nr_bytes: size of parameters in bytes
|
||||
*
|
||||
* jay, Nov 13, 08'
|
||||
* extend the single parameter to multiple.
|
||||
*/
|
||||
void mddi_remote_write_vals(struct msm_mddi_client_data *cdata, uint8_t * val,
|
||||
uint32_t reg, unsigned int nr_bytes)
|
||||
{
|
||||
struct mddi_info *mddi = container_of(cdata, struct mddi_info,
|
||||
client_data);
|
||||
struct mddi_llentry *ll;
|
||||
struct mddi_register_access *ra;
|
||||
/* unsigned s; */
|
||||
dma_addr_t bus_addr = 0;
|
||||
|
||||
mutex_lock(&mddi->reg_write_lock);
|
||||
|
||||
ll = mddi->reg_write_data;
|
||||
|
||||
ra = &(ll->u.r);
|
||||
ra->length = 14 + 4;
|
||||
ra->length = 14 + nr_bytes;
|
||||
ra->type = TYPE_REGISTER_ACCESS;
|
||||
ra->client_id = 0;
|
||||
ra->read_write_info = MDDI_WRITE | 1;
|
||||
ra->read_write_info = MDDI_WRITE | (nr_bytes / 4);
|
||||
ra->crc16 = 0;
|
||||
|
||||
ra->register_address = reg;
|
||||
ra->register_data_list = val;
|
||||
|
||||
ll->flags = 1;
|
||||
/* register access packet header occupies 14 bytes */
|
||||
ll->header_count = 14;
|
||||
ll->data_count = 4;
|
||||
ll->data = mddi->reg_write_addr + offsetof(struct mddi_llentry,
|
||||
u.r.register_data_list);
|
||||
ll->data_count = nr_bytes; /* num of bytes in the data field */
|
||||
|
||||
if (nr_bytes == 4) {
|
||||
uint32_t *prm = (uint32_t *)val;
|
||||
|
||||
ll->data = mddi->reg_write_addr +
|
||||
offsetof(struct mddi_llentry, u.r.u.reg_data);
|
||||
ra->u.reg_data = *prm;
|
||||
} else {
|
||||
int dma_retry = 5;
|
||||
|
||||
while (dma_retry--) {
|
||||
bus_addr = dma_map_single(NULL, (void *)val, nr_bytes,
|
||||
DMA_TO_DEVICE);
|
||||
if (dma_mapping_error(NULL, bus_addr) == 0)
|
||||
break;
|
||||
msleep(1);
|
||||
}
|
||||
if (dma_retry == 0) {
|
||||
printk(KERN_ERR "%s: dma map fail!\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
ll->data = bus_addr;
|
||||
ra->u.reg_data_list = (uint32_t *)bus_addr;
|
||||
}
|
||||
ll->next = 0;
|
||||
ll->reserved = 0;
|
||||
|
||||
/* s = mddi_readl(STAT); */
|
||||
/* printk(KERN_INFO "mddi_remote_write(%x, %x), stat = %x\n", val,
|
||||
* reg, s); */
|
||||
|
||||
/* inform mddi to start */
|
||||
mddi_writel(mddi->reg_write_addr, PRI_PTR);
|
||||
|
||||
/* s = mddi_readl(STAT); */
|
||||
/* printk(KERN_INFO "mddi_remote_write(%x, %x) sent, stat = %x\n",
|
||||
* val, reg, s); */
|
||||
|
||||
mddi_wait_interrupt(mddi, MDDI_INT_PRI_LINK_LIST_DONE);
|
||||
/* printk(KERN_INFO "mddi_remote_write(%x, %x) done, stat = %x\n",
|
||||
* val, reg, s); */
|
||||
if (bus_addr)
|
||||
dma_unmap_single(NULL, bus_addr, nr_bytes, DMA_TO_DEVICE);
|
||||
mutex_unlock(&mddi->reg_write_lock);
|
||||
}
|
||||
|
||||
void mddi_remote_write(struct msm_mddi_client_data *cdata, uint32_t val,
|
||||
uint32_t reg)
|
||||
{
|
||||
uint8_t * p = (uint8_t *)&val;
|
||||
mddi_remote_write_vals(cdata, p, reg, 4);
|
||||
}
|
||||
|
||||
uint32_t mddi_remote_read(struct msm_mddi_client_data *cdata, uint32_t reg)
|
||||
{
|
||||
struct mddi_info *mddi = container_of(cdata, struct mddi_info,
|
||||
@ -647,6 +716,7 @@ uint32_t mddi_remote_read(struct msm_mddi_client_data *cdata, uint32_t reg)
|
||||
|
||||
ri.reg = reg;
|
||||
ri.status = -1;
|
||||
ri.result = -1;
|
||||
|
||||
do {
|
||||
init_completion(&ri.done);
|
||||
@ -711,7 +781,16 @@ static int __init mddi_clk_setup(struct platform_device *pdev,
|
||||
unsigned long clk_rate)
|
||||
{
|
||||
int ret;
|
||||
|
||||
#ifdef CONFIG_MSM_MDP40
|
||||
mdp_clk = clk_get(&pdev->dev, "mdp_clk");
|
||||
if (IS_ERR(mdp_clk)) {
|
||||
printk(KERN_INFO "mddi: failed to get mdp clk");
|
||||
return PTR_ERR(mdp_clk);
|
||||
}
|
||||
ret = clk_enable(mdp_clk);
|
||||
if (ret)
|
||||
goto fail;
|
||||
#endif
|
||||
/* set up the clocks */
|
||||
mddi->clk = clk_get(&pdev->dev, "mddi_clk");
|
||||
if (IS_ERR(mddi->clk)) {
|
||||
@ -724,6 +803,7 @@ static int __init mddi_clk_setup(struct platform_device *pdev,
|
||||
ret = clk_set_rate(mddi->clk, clk_rate);
|
||||
if (ret)
|
||||
goto fail;
|
||||
printk(KERN_DEBUG "mddi runs at %ld\n", clk_get_rate(mddi->clk));
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
@ -751,6 +831,18 @@ static int __init mddi_rev_data_setup(struct mddi_info *mddi)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mddi_skew_calibration(struct mddi_info *mddi)
|
||||
{
|
||||
struct msm_mddi_platform_data *pdata = mddi->client_pdev.dev.platform_data;
|
||||
|
||||
clk_set_rate( mddi->clk, 50000000);
|
||||
mdelay(1);
|
||||
mddi_writel(MDDI_CMD_SKEW_CALIBRATION, CMD);
|
||||
mdelay(1);
|
||||
clk_set_rate( mddi->clk, pdata->clk_rate);
|
||||
mdelay(1);
|
||||
}
|
||||
|
||||
static int __init mddi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct msm_mddi_platform_data *pdata = pdev->dev.platform_data;
|
||||
@ -779,6 +871,8 @@ static int __init mddi_probe(struct platform_device *pdev)
|
||||
printk(KERN_INFO "mddi: init() base=0x%p irq=%d\n", mddi->base,
|
||||
mddi->irq);
|
||||
mddi->power_client = pdata->power_client;
|
||||
if (pdata->type != NULL)
|
||||
mddi->type = pdata->type;
|
||||
|
||||
mutex_init(&mddi->reg_write_lock);
|
||||
mutex_init(&mddi->reg_read_lock);
|
||||
@ -811,9 +905,10 @@ static int __init mddi_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
/* turn on the mddi client bridge chip */
|
||||
#if 0 /*advised by SKY*/
|
||||
if (mddi->power_client)
|
||||
mddi->power_client(&mddi->client_data, 1);
|
||||
|
||||
#endif
|
||||
/* initialize the mddi registers */
|
||||
mddi_set_auto_hibernate(&mddi->client_data, 0);
|
||||
mddi_writel(MDDI_CMD_RESET, CMD);
|
||||
@ -834,11 +929,19 @@ static int __init mddi_probe(struct platform_device *pdev)
|
||||
printk(KERN_INFO "mddi powerdown: stat %x\n", mddi_readl(STAT));
|
||||
msleep(100);
|
||||
printk(KERN_INFO "mddi powerdown: stat %x\n", mddi_readl(STAT));
|
||||
return 0;
|
||||
goto dummy_client;
|
||||
}
|
||||
|
||||
mddi_set_auto_hibernate(&mddi->client_data, 1);
|
||||
|
||||
if (mddi->caps.Mfr_Name == 0 && mddi->caps.Product_Code == 0)
|
||||
/*
|
||||
* FIXME: User kernel defconfig to link dedicated mddi client driver.
|
||||
*/
|
||||
#if 0
|
||||
if ( mddi->caps.Mfr_Name == 0 && mddi->caps.Product_Code == 0)
|
||||
#else
|
||||
if (mddi->caps.Mfr_Name == 0 )
|
||||
#endif
|
||||
pdata->fixup(&mddi->caps.Mfr_Name, &mddi->caps.Product_Code);
|
||||
|
||||
mddi->client_pdev.id = 0;
|
||||
@ -856,8 +959,17 @@ static int __init mddi_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
if (i >= pdata->num_clients)
|
||||
if (i >= pdata->num_clients) {
|
||||
dummy_client:
|
||||
mddi->client_data.private_client_data =
|
||||
pdata->client_platform_data[0].client_data;
|
||||
mddi->client_pdev.name =
|
||||
pdata->client_platform_data[0].name;
|
||||
mddi->client_pdev.id =
|
||||
pdata->client_platform_data[0].id;
|
||||
mddi->client_pdev.name = "mddi_c_dummy";
|
||||
clk_disable(mddi->clk);
|
||||
}
|
||||
printk(KERN_INFO "mddi: registering panel %s\n",
|
||||
mddi->client_pdev.name);
|
||||
|
||||
@ -865,6 +977,7 @@ static int __init mddi_probe(struct platform_device *pdev)
|
||||
mddi->client_data.resume = mddi_resume;
|
||||
mddi->client_data.activate_link = mddi_activate_link;
|
||||
mddi->client_data.remote_write = mddi_remote_write;
|
||||
mddi->client_data.remote_write_vals = mddi_remote_write_vals;
|
||||
mddi->client_data.remote_read = mddi_remote_read;
|
||||
mddi->client_data.auto_hibernate = mddi_set_auto_hibernate;
|
||||
mddi->client_data.fb_resource = pdata->fb_resource;
|
||||
|
267
drivers/video/msm/mddi_client_epson.c
Normal file
267
drivers/video/msm/mddi_client_epson.c
Normal file
@ -0,0 +1,267 @@
|
||||
/*
|
||||
* Copyright (C) 2008 HTC Corporation.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/wakelock.h>
|
||||
#include <mach/msm_fb.h>
|
||||
|
||||
static DECLARE_WAIT_QUEUE_HEAD(epson_vsync_wait);
|
||||
|
||||
struct panel_info {
|
||||
struct msm_mddi_client_data *client_data;
|
||||
struct platform_device pdev;
|
||||
struct msm_panel_data panel_data;
|
||||
struct msmfb_callback *epson_callback;
|
||||
struct wake_lock idle_lock;
|
||||
int epson_got_int;
|
||||
};
|
||||
|
||||
static struct platform_device mddi_eps_cabc = {
|
||||
.name = "eps_cabc",
|
||||
.id = 0,
|
||||
};
|
||||
|
||||
static void epson_request_vsync(struct msm_panel_data *panel_data,
|
||||
struct msmfb_callback *callback)
|
||||
{
|
||||
struct panel_info *panel = container_of(panel_data, struct panel_info,
|
||||
panel_data);
|
||||
struct msm_mddi_client_data *client_data = panel->client_data;
|
||||
|
||||
panel->epson_callback = callback;
|
||||
if (panel->epson_got_int) {
|
||||
panel->epson_got_int = 0;
|
||||
client_data->activate_link(client_data);
|
||||
}
|
||||
}
|
||||
|
||||
static void epson_clear_vsync(struct msm_panel_data *panel_data)
|
||||
{
|
||||
struct panel_info *panel = container_of(panel_data, struct panel_info,
|
||||
panel_data);
|
||||
struct msm_mddi_client_data *client_data = panel->client_data;
|
||||
|
||||
client_data->activate_link(client_data);
|
||||
}
|
||||
|
||||
static void epson_wait_vsync(struct msm_panel_data *panel_data)
|
||||
{
|
||||
struct panel_info *panel = container_of(panel_data, struct panel_info,
|
||||
panel_data);
|
||||
struct msm_mddi_client_data *client_data = panel->client_data;
|
||||
|
||||
if (panel->epson_got_int) {
|
||||
panel->epson_got_int = 0;
|
||||
client_data->activate_link(client_data); /* clears interrupt */
|
||||
}
|
||||
if (wait_event_timeout(epson_vsync_wait, panel->epson_got_int,
|
||||
HZ/2) == 0)
|
||||
printk(KERN_ERR "timeout waiting for VSYNC\n");
|
||||
panel->epson_got_int = 0;
|
||||
/* interrupt clears when screen dma starts */
|
||||
}
|
||||
|
||||
static int epson_suspend(struct msm_panel_data *panel_data)
|
||||
{
|
||||
struct panel_info *panel = container_of(panel_data, struct panel_info,
|
||||
panel_data);
|
||||
struct msm_mddi_client_data *client_data = panel->client_data;
|
||||
|
||||
struct msm_mddi_bridge_platform_data *bridge_data =
|
||||
client_data->private_client_data;
|
||||
int ret;
|
||||
|
||||
wake_lock(&panel->idle_lock);
|
||||
ret = bridge_data->uninit(bridge_data, client_data);
|
||||
wake_unlock(&panel->idle_lock);
|
||||
if (ret) {
|
||||
printk(KERN_INFO "mddi epson client: non zero return from "
|
||||
"uninit\n");
|
||||
return ret;
|
||||
}
|
||||
client_data->suspend(client_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int epson_resume(struct msm_panel_data *panel_data)
|
||||
{
|
||||
struct panel_info *panel = container_of(panel_data, struct panel_info,
|
||||
panel_data);
|
||||
struct msm_mddi_client_data *client_data = panel->client_data;
|
||||
|
||||
struct msm_mddi_bridge_platform_data *bridge_data =
|
||||
client_data->private_client_data;
|
||||
int ret;
|
||||
|
||||
wake_lock(&panel->idle_lock);
|
||||
client_data->resume(client_data);
|
||||
wake_unlock(&panel->idle_lock);
|
||||
ret = bridge_data->init(bridge_data, client_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int epson_blank(struct msm_panel_data *panel_data)
|
||||
{
|
||||
struct panel_info *panel = container_of(panel_data, struct panel_info,
|
||||
panel_data);
|
||||
struct msm_mddi_client_data *client_data = panel->client_data;
|
||||
struct msm_mddi_bridge_platform_data *bridge_data =
|
||||
client_data->private_client_data;
|
||||
|
||||
return bridge_data->blank(bridge_data, client_data);
|
||||
}
|
||||
|
||||
static int epson_unblank(struct msm_panel_data *panel_data)
|
||||
{
|
||||
struct panel_info *panel = container_of(panel_data, struct panel_info,
|
||||
panel_data);
|
||||
struct msm_mddi_client_data *client_data = panel->client_data;
|
||||
struct msm_mddi_bridge_platform_data *bridge_data =
|
||||
client_data->private_client_data;
|
||||
|
||||
return bridge_data->unblank(bridge_data, client_data);
|
||||
}
|
||||
|
||||
static irqreturn_t epson_vsync_interrupt(int irq, void *data)
|
||||
{
|
||||
struct panel_info *panel = data;
|
||||
|
||||
panel->epson_got_int = 1;
|
||||
if (panel->epson_callback) {
|
||||
panel->epson_callback->func(panel->epson_callback);
|
||||
panel->epson_callback = 0;
|
||||
}
|
||||
wake_up(&epson_vsync_wait);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int setup_vsync(struct panel_info *panel,
|
||||
int init)
|
||||
{
|
||||
int ret;
|
||||
int gpio = 98;
|
||||
unsigned int irq;
|
||||
|
||||
if (!init) {
|
||||
ret = 0;
|
||||
goto uninit;
|
||||
}
|
||||
ret = gpio_request(gpio, "vsync");
|
||||
if (ret)
|
||||
goto err_request_gpio_failed;
|
||||
|
||||
ret = gpio_direction_input(gpio);
|
||||
if (ret)
|
||||
goto err_gpio_direction_input_failed;
|
||||
|
||||
ret = irq = gpio_to_irq(gpio);
|
||||
if (ret < 0)
|
||||
goto err_get_irq_num_failed;
|
||||
|
||||
ret = request_irq(irq, epson_vsync_interrupt, IRQF_TRIGGER_FALLING,
|
||||
"vsync", panel);
|
||||
if (ret)
|
||||
goto err_request_irq_failed;
|
||||
printk(KERN_INFO "vsync on gpio %d now %d\n",
|
||||
gpio, gpio_get_value(gpio));
|
||||
return 0;
|
||||
|
||||
uninit:
|
||||
free_irq(gpio_to_irq(gpio), panel);
|
||||
err_request_irq_failed:
|
||||
err_get_irq_num_failed:
|
||||
err_gpio_direction_input_failed:
|
||||
gpio_free(gpio);
|
||||
err_request_gpio_failed:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mddi_epson_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct msm_mddi_client_data *client_data = pdev->dev.platform_data;
|
||||
struct msm_mddi_bridge_platform_data *bridge_data =
|
||||
client_data->private_client_data;
|
||||
struct panel_data *panel_data = &bridge_data->panel_conf;
|
||||
struct panel_info *panel =
|
||||
kzalloc(sizeof(struct panel_info), GFP_KERNEL);
|
||||
if (!panel)
|
||||
return -ENOMEM;
|
||||
platform_set_drvdata(pdev, panel);
|
||||
|
||||
printk(KERN_DEBUG "%s\n", __func__);
|
||||
|
||||
if (panel_data->caps & MSMFB_CAP_CABC) {
|
||||
printk(KERN_INFO "CABC enabled\n");
|
||||
mddi_eps_cabc.dev.platform_data = client_data;
|
||||
platform_device_register(&mddi_eps_cabc);
|
||||
}
|
||||
|
||||
ret = setup_vsync(panel, 1);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "mddi_bridge_setup_vsync failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
panel->client_data = client_data;
|
||||
panel->panel_data.suspend = epson_suspend;
|
||||
panel->panel_data.resume = epson_resume;
|
||||
panel->panel_data.wait_vsync = epson_wait_vsync;
|
||||
panel->panel_data.request_vsync = epson_request_vsync;
|
||||
panel->panel_data.clear_vsync = epson_clear_vsync;
|
||||
panel->panel_data.blank = epson_blank;
|
||||
panel->panel_data.unblank = epson_unblank;
|
||||
panel->panel_data.fb_data = &bridge_data->fb_data;
|
||||
panel->panel_data.caps = ~MSMFB_CAP_PARTIAL_UPDATES;
|
||||
|
||||
panel->pdev.name = "msm_panel";
|
||||
panel->pdev.id = pdev->id;
|
||||
panel->pdev.resource = client_data->fb_resource;
|
||||
panel->pdev.num_resources = 1;
|
||||
panel->pdev.dev.platform_data = &panel->panel_data;
|
||||
platform_device_register(&panel->pdev);
|
||||
wake_lock_init(&panel->idle_lock, WAKE_LOCK_IDLE, "eps_idle_lock");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mddi_epson_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_info *panel = platform_get_drvdata(pdev);
|
||||
|
||||
setup_vsync(panel, 0);
|
||||
kfree(panel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver mddi_client_d263_0000 = {
|
||||
.probe = mddi_epson_probe,
|
||||
.remove = mddi_epson_remove,
|
||||
.driver = { .name = "mddi_c_4ca3_0000" },
|
||||
};
|
||||
|
||||
static int __init mddi_client_epson_init(void)
|
||||
{
|
||||
platform_driver_register(&mddi_client_d263_0000);
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(mddi_client_epson_init);
|
||||
|
269
drivers/video/msm/mddi_client_novb9f6_5582.c
Normal file
269
drivers/video/msm/mddi_client_novb9f6_5582.c
Normal file
@ -0,0 +1,269 @@
|
||||
/*
|
||||
* Copyright (C) 2008 HTC Corporation.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/wakelock.h>
|
||||
#include <mach/msm_fb.h>
|
||||
|
||||
static DECLARE_WAIT_QUEUE_HEAD(novtec_vsync_wait);
|
||||
|
||||
struct panel_info {
|
||||
struct msm_mddi_client_data *client_data;
|
||||
struct platform_device pdev;
|
||||
struct msm_panel_data panel_data;
|
||||
struct msmfb_callback *novtec_callback;
|
||||
struct wake_lock idle_lock;
|
||||
int novtec_got_int;
|
||||
};
|
||||
|
||||
static struct platform_device mddi_nov_cabc = {
|
||||
.name = "nov_cabc",
|
||||
.id = 0,
|
||||
};
|
||||
|
||||
static void novtec_request_vsync(struct msm_panel_data *panel_data,
|
||||
struct msmfb_callback *callback)
|
||||
{
|
||||
struct panel_info *panel = container_of(panel_data, struct panel_info,
|
||||
panel_data);
|
||||
struct msm_mddi_client_data *client_data = panel->client_data;
|
||||
|
||||
panel->novtec_callback = callback;
|
||||
if (panel->novtec_got_int) {
|
||||
panel->novtec_got_int = 0;
|
||||
client_data->activate_link(client_data);
|
||||
}
|
||||
}
|
||||
|
||||
static void novtec_clear_vsync(struct msm_panel_data *panel_data)
|
||||
{
|
||||
struct panel_info *panel = container_of(panel_data, struct panel_info,
|
||||
panel_data);
|
||||
struct msm_mddi_client_data *client_data = panel->client_data;
|
||||
|
||||
client_data->activate_link(client_data);
|
||||
}
|
||||
|
||||
static void novtec_wait_vsync(struct msm_panel_data *panel_data)
|
||||
{
|
||||
struct panel_info *panel = container_of(panel_data, struct panel_info,
|
||||
panel_data);
|
||||
struct msm_mddi_client_data *client_data = panel->client_data;
|
||||
|
||||
if (panel->novtec_got_int) {
|
||||
panel->novtec_got_int = 0;
|
||||
client_data->activate_link(client_data); /* clears interrupt */
|
||||
}
|
||||
if (wait_event_timeout(novtec_vsync_wait, panel->novtec_got_int,
|
||||
HZ/2) == 0)
|
||||
printk(KERN_ERR "timeout waiting for VSYNC\n");
|
||||
panel->novtec_got_int = 0;
|
||||
/* interrupt clears when screen dma starts */
|
||||
}
|
||||
|
||||
static int novtec_suspend(struct msm_panel_data *panel_data)
|
||||
{
|
||||
struct panel_info *panel = container_of(panel_data, struct panel_info,
|
||||
panel_data);
|
||||
struct msm_mddi_client_data *client_data = panel->client_data;
|
||||
|
||||
struct msm_mddi_bridge_platform_data *bridge_data =
|
||||
client_data->private_client_data;
|
||||
int ret;
|
||||
|
||||
wake_lock(&panel->idle_lock);
|
||||
ret = bridge_data->uninit(bridge_data, client_data);
|
||||
wake_unlock(&panel->idle_lock);
|
||||
if (ret) {
|
||||
printk(KERN_INFO "mddi novtec client: non zero return from "
|
||||
"uninit\n");
|
||||
return ret;
|
||||
}
|
||||
client_data->suspend(client_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int novtec_resume(struct msm_panel_data *panel_data)
|
||||
{
|
||||
struct panel_info *panel = container_of(panel_data, struct panel_info,
|
||||
panel_data);
|
||||
struct msm_mddi_client_data *client_data = panel->client_data;
|
||||
|
||||
struct msm_mddi_bridge_platform_data *bridge_data =
|
||||
client_data->private_client_data;
|
||||
int ret;
|
||||
|
||||
wake_lock(&panel->idle_lock);
|
||||
client_data->resume(client_data);
|
||||
wake_unlock(&panel->idle_lock);
|
||||
ret = bridge_data->init(bridge_data, client_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int novtec_blank(struct msm_panel_data *panel_data)
|
||||
{
|
||||
struct panel_info *panel = container_of(panel_data, struct panel_info,
|
||||
panel_data);
|
||||
struct msm_mddi_client_data *client_data = panel->client_data;
|
||||
struct msm_mddi_bridge_platform_data *bridge_data =
|
||||
client_data->private_client_data;
|
||||
|
||||
return bridge_data->blank(bridge_data, client_data);
|
||||
}
|
||||
|
||||
static int novtec_unblank(struct msm_panel_data *panel_data)
|
||||
{
|
||||
struct panel_info *panel = container_of(panel_data, struct panel_info,
|
||||
panel_data);
|
||||
struct msm_mddi_client_data *client_data = panel->client_data;
|
||||
struct msm_mddi_bridge_platform_data *bridge_data =
|
||||
client_data->private_client_data;
|
||||
|
||||
return bridge_data->unblank(bridge_data, client_data);
|
||||
}
|
||||
|
||||
static irqreturn_t novtec_vsync_interrupt(int irq, void *data)
|
||||
{
|
||||
struct panel_info *panel = data;
|
||||
|
||||
panel->novtec_got_int = 1;
|
||||
if (panel->novtec_callback) {
|
||||
mdelay(3);
|
||||
panel->novtec_callback->func(panel->novtec_callback);
|
||||
panel->novtec_callback = 0;
|
||||
}
|
||||
wake_up(&novtec_vsync_wait);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int setup_vsync(struct panel_info *panel,
|
||||
int init)
|
||||
{
|
||||
int ret;
|
||||
int gpio = 98;
|
||||
unsigned int irq;
|
||||
|
||||
if (!init) {
|
||||
ret = 0;
|
||||
goto uninit;
|
||||
}
|
||||
ret = gpio_request(gpio, "vsync");
|
||||
if (ret)
|
||||
goto err_request_gpio_failed;
|
||||
|
||||
ret = gpio_direction_input(gpio);
|
||||
if (ret)
|
||||
goto err_gpio_direction_input_failed;
|
||||
|
||||
ret = irq = gpio_to_irq(gpio);
|
||||
if (ret < 0)
|
||||
goto err_get_irq_num_failed;
|
||||
|
||||
ret = request_irq(irq, novtec_vsync_interrupt, IRQF_TRIGGER_FALLING,
|
||||
"vsync", panel);
|
||||
if (ret)
|
||||
goto err_request_irq_failed;
|
||||
printk(KERN_INFO "vsync on gpio %d now %d\n",
|
||||
gpio, gpio_get_value(gpio));
|
||||
return 0;
|
||||
|
||||
uninit:
|
||||
free_irq(gpio_to_irq(gpio), panel);
|
||||
err_request_irq_failed:
|
||||
err_get_irq_num_failed:
|
||||
err_gpio_direction_input_failed:
|
||||
gpio_free(gpio);
|
||||
err_request_gpio_failed:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mddi_novtec_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct msm_mddi_client_data *client_data = pdev->dev.platform_data;
|
||||
struct msm_mddi_bridge_platform_data *bridge_data =
|
||||
client_data->private_client_data;
|
||||
struct panel_data *panel_data = &bridge_data->panel_conf;
|
||||
struct panel_info *panel =
|
||||
kzalloc(sizeof(struct panel_info), GFP_KERNEL);
|
||||
|
||||
if (!panel)
|
||||
return -ENOMEM;
|
||||
platform_set_drvdata(pdev, panel);
|
||||
|
||||
printk(KERN_DEBUG "%s\n", __func__);
|
||||
|
||||
if (panel_data->caps & MSMFB_CAP_CABC) {
|
||||
printk(KERN_INFO "CABC enabled\n");
|
||||
mddi_nov_cabc.dev.platform_data = client_data;
|
||||
platform_device_register(&mddi_nov_cabc);
|
||||
}
|
||||
|
||||
ret = setup_vsync(panel, 1);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "mddi_bridge_setup_vsync failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
panel->client_data = client_data;
|
||||
panel->panel_data.suspend = novtec_suspend;
|
||||
panel->panel_data.resume = novtec_resume;
|
||||
panel->panel_data.wait_vsync = novtec_wait_vsync;
|
||||
panel->panel_data.request_vsync = novtec_request_vsync;
|
||||
panel->panel_data.clear_vsync = novtec_clear_vsync;
|
||||
panel->panel_data.blank = novtec_blank;
|
||||
panel->panel_data.unblank = novtec_unblank;
|
||||
panel->panel_data.fb_data = &bridge_data->fb_data;
|
||||
panel->panel_data.caps = MSMFB_CAP_PARTIAL_UPDATES;
|
||||
|
||||
panel->pdev.name = "msm_panel";
|
||||
panel->pdev.id = pdev->id;
|
||||
panel->pdev.resource = client_data->fb_resource;
|
||||
panel->pdev.num_resources = 1;
|
||||
panel->pdev.dev.platform_data = &panel->panel_data;
|
||||
platform_device_register(&panel->pdev);
|
||||
wake_lock_init(&panel->idle_lock, WAKE_LOCK_IDLE, "nov_idle_lock");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mddi_novtec_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct panel_info *panel = platform_get_drvdata(pdev);
|
||||
|
||||
setup_vsync(panel, 0);
|
||||
kfree(panel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver mddi_client_d263_0000 = {
|
||||
.probe = mddi_novtec_probe,
|
||||
.remove = mddi_novtec_remove,
|
||||
.driver = { .name = "mddi_c_b9f6_5582" },
|
||||
};
|
||||
|
||||
static int __init mddi_client_novtec_init(void)
|
||||
{
|
||||
platform_driver_register(&mddi_client_d263_0000);
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(mddi_client_novtec_init);
|
||||
|
@ -53,6 +53,8 @@
|
||||
#define MDDI_MF_CNT 0x0084
|
||||
#define MDDI_CURR_REV_PTR 0x0088
|
||||
#define MDDI_CORE_VER 0x008c
|
||||
#define MDDI_PAD_IO_CTL 0x00a0
|
||||
#define MDDI_PAD_CAL 0x00a4
|
||||
|
||||
#define MDDI_INT_PRI_PTR_READ 0x0001
|
||||
#define MDDI_INT_SEC_PTR_READ 0x0002
|
||||
@ -112,9 +114,7 @@
|
||||
#define MDDI_CMD_LINK_ACTIVE 0x0900
|
||||
#define MDDI_CMD_PERIODIC_REV_ENCAP 0x0A00
|
||||
#define MDDI_CMD_FORCE_NEW_REV_PTR 0x0C00
|
||||
|
||||
|
||||
|
||||
#define MDDI_CMD_SKEW_CALIBRATION 0x0D00
|
||||
#define MDDI_VIDEO_REV_PKT_SIZE 0x40
|
||||
#define MDDI_CLIENT_CAPABILITY_REV_PKT_SIZE 0x60
|
||||
#define MDDI_MAX_REV_PKT_SIZE 0x60
|
||||
@ -125,9 +125,17 @@
|
||||
/* MDP sends 256 pixel packets, so lower value hibernates more without
|
||||
* significantly increasing latency of waiting for next subframe */
|
||||
#define MDDI_HOST_BYTES_PER_SUBFRAME 0x3C00
|
||||
#if defined (CONFIG_ARCH_QSD8X50) || defined (CONFIG_ARCH_MSM7X30)
|
||||
#define MDDI_HOST_TA2_LEN 0x001a
|
||||
#else
|
||||
#define MDDI_HOST_TA2_LEN 0x000c
|
||||
#define MDDI_HOST_REV_RATE_DIV 0x0002
|
||||
#endif
|
||||
|
||||
#if defined (CONFIG_ARCH_QSD8X50) || defined (CONFIG_ARCH_MSM7X30)
|
||||
#define MDDI_HOST_REV_RATE_DIV 0x0004
|
||||
#else
|
||||
#define MDDI_HOST_REV_RATE_DIV 0x0002
|
||||
#endif
|
||||
|
||||
struct __attribute__((packed)) mddi_rev_packet {
|
||||
uint16_t length;
|
||||
@ -284,8 +292,12 @@ struct __attribute__((packed)) mddi_register_access {
|
||||
|
||||
uint16_t crc16;
|
||||
|
||||
uint32_t register_data_list;
|
||||
/* list of 4-byte register data values for/from client registers */
|
||||
union {
|
||||
uint32_t reg_data;
|
||||
uint32_t *reg_data_list;
|
||||
} u;
|
||||
|
||||
uint16_t crc_data;
|
||||
};
|
||||
|
||||
struct __attribute__((packed)) mddi_llentry {
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/msm_mdp.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/wait.h>
|
||||
@ -29,11 +28,11 @@
|
||||
|
||||
#include <mach/msm_iomap.h>
|
||||
#include <mach/msm_fb.h>
|
||||
#include <asm/mach-types.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "mdp_hw.h"
|
||||
#include "mdp_ppp.h"
|
||||
#include <asm/mach-types.h>
|
||||
|
||||
struct class *mdp_class;
|
||||
|
||||
@ -42,6 +41,18 @@ struct class *mdp_class;
|
||||
static DECLARE_WAIT_QUEUE_HEAD(mdp_ppp_waitqueue);
|
||||
static unsigned int mdp_irq_mask;
|
||||
struct clk *mdp_clk_to_disable_later = 0;
|
||||
static struct mdp_blit_req *timeout_req;
|
||||
#ifdef CONFIG_FB_MSM_OVERLAY
|
||||
extern int mdp4_overlay_get(struct mdp_device *mdp_dev, struct fb_info *info, struct mdp_overlay *req);
|
||||
extern int mdp4_overlay_set(struct mdp_device *mdp_dev, struct fb_info *info, struct mdp_overlay *req);
|
||||
extern int mdp4_overlay_unset(struct mdp_device *mdp_dev, struct fb_info *info, int ndx);
|
||||
extern int mdp4_overlay_play(struct mdp_device *mdp_dev, struct fb_info *info, struct msmfb_overlay_data *req,
|
||||
struct file **pp_src_file);
|
||||
extern void mdp4_mddi_overlay(void *priv, uint32_t addr, uint32_t stride,
|
||||
uint32_t width, uint32_t height, uint32_t x,
|
||||
uint32_t y);
|
||||
#include "mdp4.h"
|
||||
#endif
|
||||
DEFINE_MUTEX(mdp_mutex);
|
||||
|
||||
static int locked_enable_mdp_irq(struct mdp_info *mdp, uint32_t mask)
|
||||
@ -51,16 +62,14 @@ static int locked_enable_mdp_irq(struct mdp_info *mdp, uint32_t mask)
|
||||
/* if the mask bits are already set return an error, this interrupt
|
||||
* is already enabled */
|
||||
if (mdp_irq_mask & mask) {
|
||||
// pr_err("mdp irq already on %x %x\n", mdp_irq_mask, mask);
|
||||
pr_err("mdp irq already on %x %x\n", mdp_irq_mask, mask);
|
||||
return -1;
|
||||
}
|
||||
/* if the mdp irq is not already enabled enable it */
|
||||
if (!mdp_irq_mask) {
|
||||
|
||||
clk_set_rate(mdp->ebi1_clk, 128000000);
|
||||
clk_enable(mdp->clk);
|
||||
|
||||
enable_irq(mdp->irq);
|
||||
clk_set_rate(mdp->ebi1_clk, 128000000);
|
||||
}
|
||||
|
||||
/* clear out any previous irqs for the requested mask*/
|
||||
@ -86,8 +95,6 @@ static int enable_mdp_irq(struct mdp_info *mdp, uint32_t mask)
|
||||
|
||||
static int locked_disable_mdp_irq(struct mdp_info *mdp, uint32_t mask)
|
||||
{
|
||||
if (!mask) return 1;
|
||||
|
||||
/* this interrupt is already disabled! */
|
||||
if (!(mdp_irq_mask & mask)) {
|
||||
printk(KERN_ERR "mdp irq already off %x %x\n",
|
||||
@ -102,11 +109,9 @@ static int locked_disable_mdp_irq(struct mdp_info *mdp, uint32_t mask)
|
||||
/* if no one is waiting on the interrupt, disable it */
|
||||
if (!mdp_irq_mask) {
|
||||
disable_irq_nosync(mdp->irq);
|
||||
|
||||
if (mdp->clk)
|
||||
clk_disable(mdp->clk);
|
||||
clk_set_rate(mdp->ebi1_clk, 0);
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -133,13 +138,21 @@ static irqreturn_t mdp_isr(int irq, void *data)
|
||||
|
||||
status = mdp_readl(mdp, MDP_INTR_STATUS);
|
||||
mdp_writel(mdp, status, MDP_INTR_CLEAR);
|
||||
|
||||
|
||||
|
||||
#if defined(CONFIG_MACH_HTCLEO)
|
||||
status &= ~0x10000; // Cotulla
|
||||
#endif
|
||||
|
||||
// pr_info("%s: status=%08x (irq_mask=%08x)\n", __func__, status,
|
||||
// mdp_irq_mask);
|
||||
status &= mdp_irq_mask;
|
||||
|
||||
#ifdef CONFIG_MSM_MDP40
|
||||
if (mdp->mdp_dev.overrides & MSM_MDP4_MDDI_DMA_SWITCH) {
|
||||
if(status && mdp->out_if[MSM_MDDI_PMDH_INTERFACE].dma_cb != NULL)
|
||||
status |= (INTR_OVERLAY0_DONE | MDP_DMA_S_DONE);
|
||||
}
|
||||
#endif
|
||||
for (i = 0; i < MSM_MDP_NUM_INTERFACES; ++i) {
|
||||
struct mdp_out_interface *out_if = &mdp->out_if[i];
|
||||
if (status & out_if->dma_mask) {
|
||||
@ -182,20 +195,16 @@ static int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq)
|
||||
unsigned long irq_flags;
|
||||
|
||||
// pr_info("%s: WAITING for 0x%x\n", __func__, mask);
|
||||
wait_event_timeout(*wq, !mdp_check_mask(mdp, mask), HZ); // Cotulla TEST
|
||||
wait_event_timeout(*wq, !mdp_check_mask(mdp, mask), HZ);
|
||||
|
||||
spin_lock_irqsave(&mdp->lock, irq_flags);
|
||||
if (mdp_irq_mask & mask) {
|
||||
locked_disable_mdp_irq(mdp, mask);
|
||||
pr_warning("%s: timeout waiting for mdp to complete 0x%x\n",
|
||||
__func__, mask);
|
||||
printk("GLBL_CLK_ENA: %08X\n", readl(MSM_CLK_CTL_BASE + 0x0000));
|
||||
printk("GLBL_CLK_STATE: %08X\n", readl(MSM_CLK_CTL_BASE + 0x0004));
|
||||
printk("GLBL_SLEEP_EN: %08X\n", readl(MSM_CLK_CTL_BASE + 0x001C));
|
||||
printk("GLBL_CLK_ENA_2: %08X\n", readl(MSM_CLK_CTL_BASE + 0x0220));
|
||||
printk("GLBL_CLK_STATE_2: %08X\n", readl(MSM_CLK_CTL_BASE + 0x0224));
|
||||
printk("GLBL_CLK_SLEEP_EN_2: %08X\n", readl(MSM_CLK_CTL_BASE + 0x023C));
|
||||
mdp_ppp_dump_debug(mdp);
|
||||
locked_disable_mdp_irq(mdp, mask);
|
||||
if(timeout_req)
|
||||
mdp_dump_blit(timeout_req);
|
||||
|
||||
ret = -ETIMEDOUT;
|
||||
} else {
|
||||
// pr_info("%s: SUCCESS waiting for 0x%x\n", __func__, mask);
|
||||
@ -217,6 +226,7 @@ void mdp_dma_wait(struct mdp_device *mdp_dev, int interface)
|
||||
case MSM_MDDI_PMDH_INTERFACE:
|
||||
case MSM_MDDI_EMDH_INTERFACE:
|
||||
case MSM_LCDC_INTERFACE:
|
||||
case MSM_TV_INTERFACE:
|
||||
BUG_ON(!mdp->out_if[interface].registered);
|
||||
mask = mdp->out_if[interface].dma_mask;
|
||||
wq = &mdp->out_if[interface].dma_waitqueue;
|
||||
@ -243,15 +253,15 @@ static int mdp_ppp_wait(struct mdp_info *mdp)
|
||||
return mdp_wait(mdp, DL0_ROI_DONE, &mdp_ppp_waitqueue);
|
||||
}
|
||||
|
||||
static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride,
|
||||
uint32_t width, uint32_t height, uint32_t x,
|
||||
uint32_t y)
|
||||
static void mdp_dmas_to_mddi(void *priv, uint32_t addr, uint32_t stride,
|
||||
uint32_t width, uint32_t height, uint32_t x, uint32_t y)
|
||||
{
|
||||
struct mdp_info *mdp = priv;
|
||||
uint32_t dma2_cfg;
|
||||
uint16_t ld_param = 0; /* 0=PRIM, 1=SECD, 2=EXT */
|
||||
uint32_t video_packet_parameter;
|
||||
uint16_t ld_param = 1;
|
||||
|
||||
|
||||
printk("MDDI start\n");
|
||||
if(machine_is_htcleo()) {
|
||||
dma2_cfg = DMA_PACK_ALIGN_MSB |
|
||||
DMA_PACK_PATTERN_RGB;
|
||||
@ -261,30 +271,120 @@ static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride,
|
||||
dma2_cfg |= DMA_OUT_SEL_LCDC;
|
||||
|
||||
dma2_cfg |= DMA_IBUF_FORMAT_RGB565;
|
||||
|
||||
/* 655 16BPP */
|
||||
dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS;
|
||||
}
|
||||
else {
|
||||
|
||||
} else {
|
||||
dma2_cfg = DMA_PACK_TIGHT |
|
||||
DMA_PACK_ALIGN_LSB |
|
||||
DMA_PACK_PATTERN_RGB |
|
||||
DMA_OUT_SEL_AHB |
|
||||
DMA_IBUF_NONCONTIGUOUS;
|
||||
|
||||
dma2_cfg |= mdp->format;
|
||||
|
||||
dma2_cfg |= DMA_OUT_SEL_MDDI;
|
||||
#if defined CONFIG_MSM_MDP22 || defined CONFIG_MSM_MDP30
|
||||
if (mdp->format == DMA_IBUF_FORMAT_RGB888_OR_ARGB8888)
|
||||
#else
|
||||
if (mdp->format == DMA_IBUF_FORMAT_XRGB8888)
|
||||
#endif
|
||||
dma2_cfg |= DMA_PACK_PATTERN_BGR;
|
||||
else
|
||||
dma2_cfg |= DMA_PACK_PATTERN_RGB;
|
||||
|
||||
dma2_cfg |= DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY;
|
||||
dma2_cfg |= DMA_OUT_SEL_MDDI;
|
||||
|
||||
dma2_cfg |= DMA_DITHER_EN;
|
||||
dma2_cfg |= DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY;
|
||||
|
||||
/* 666 18BPP */
|
||||
dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS;
|
||||
dma2_cfg |= DMA_DITHER_EN;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MSM_MDP22
|
||||
if (mdp->mdp_dev.color_format == MSM_MDP_OUT_IF_FMT_RGB565) {
|
||||
dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS;
|
||||
video_packet_parameter = MDDI_VDO_PACKET_DESC_RGB565;
|
||||
} else if (mdp->mdp_dev.color_format == MSM_MDP_OUT_IF_FMT_RGB666) {
|
||||
dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS;
|
||||
video_packet_parameter = MDDI_VDO_PACKET_DESC_RGB666;
|
||||
}
|
||||
|
||||
/* setup size, address, and stride */
|
||||
mdp_writel(mdp, (height << 16) | (width), MDP_DMA_S_SIZE);
|
||||
mdp_writel(mdp, addr, MDP_DMA_S_IBUF_ADDR);
|
||||
mdp_writel(mdp, stride, MDP_DMA_S_IBUF_Y_STRIDE);
|
||||
|
||||
/* set y & x offset and MDDI transaction parameters */
|
||||
mdp_writel(mdp, (y << 16) | (x), MDP_DMA_S_OUT_XY);
|
||||
mdp_writel(mdp, ld_param, MDP_MDDI_PARAM_WR_SEL);
|
||||
if (mdp->mdp_dev.overrides & MSM_MDP_PANEL_IGNORE_PIXEL_DATA) {
|
||||
mdp_writel(mdp, (video_packet_parameter << 16) | 0xE3,
|
||||
MDP_MDDI_PARAM);
|
||||
}
|
||||
else {
|
||||
mdp_writel(mdp, (video_packet_parameter << 16) | MDDI_VDO_PACKET_PRIM,
|
||||
MDP_MDDI_PARAM);
|
||||
}
|
||||
|
||||
mdp_writel(mdp, dma2_cfg, MDP_DMA_S_CONFIG);
|
||||
mdp_writel(mdp, 0, MDP_DMA_S_START);
|
||||
}
|
||||
|
||||
static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride,
|
||||
uint32_t width, uint32_t height, uint32_t x,
|
||||
uint32_t y)
|
||||
{
|
||||
struct mdp_info *mdp = priv;
|
||||
uint32_t dma2_cfg = 0;
|
||||
uint32_t video_packet_parameter = 0;
|
||||
uint16_t ld_param = 0; /* 0=PRIM, 1=SECD, 2=EXT */
|
||||
|
||||
#if !defined(CONFIG_MSM_MDP30)
|
||||
dma2_cfg = DMA_PACK_TIGHT |
|
||||
DMA_PACK_ALIGN_LSB |
|
||||
DMA_OUT_SEL_AHB |
|
||||
DMA_IBUF_NONCONTIGUOUS;
|
||||
|
||||
#endif
|
||||
dma2_cfg |= mdp->format;
|
||||
|
||||
#if defined CONFIG_MSM_MDP22 || defined CONFIG_MSM_MDP30
|
||||
if (mdp->format == DMA_IBUF_FORMAT_RGB888_OR_ARGB8888)
|
||||
#else
|
||||
if (mdp->format == DMA_IBUF_FORMAT_XRGB8888)
|
||||
#endif
|
||||
dma2_cfg |= DMA_PACK_PATTERN_BGR;
|
||||
else
|
||||
dma2_cfg |= DMA_PACK_PATTERN_RGB;
|
||||
|
||||
dma2_cfg |= DMA_OUT_SEL_MDDI;
|
||||
|
||||
dma2_cfg |= DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY;
|
||||
|
||||
#if !defined(CONFIG_MSM_MDP30)
|
||||
dma2_cfg |= DMA_DITHER_EN;
|
||||
#endif
|
||||
|
||||
if (mdp->mdp_dev.color_format == MSM_MDP_OUT_IF_FMT_RGB565) {
|
||||
dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS;
|
||||
video_packet_parameter = MDDI_VDO_PACKET_DESC_RGB565;
|
||||
} else if (mdp->mdp_dev.color_format == MSM_MDP_OUT_IF_FMT_RGB666) {
|
||||
dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS;
|
||||
video_packet_parameter = MDDI_VDO_PACKET_DESC_RGB666;
|
||||
}
|
||||
|
||||
|
||||
#if defined(CONFIG_MSM_MDP30) || defined(CONFIG_MSM_MDP302)
|
||||
writel(height << 16 | width, mdp->base + 0x90004);
|
||||
writel(addr, mdp->base + 0x90008);
|
||||
writel(stride, mdp->base + 0x9000c);
|
||||
|
||||
/* set y & x offset and MDDI transaction parameters */
|
||||
writel(y << 16 | x, mdp->base + 0x90010);
|
||||
writel(ld_param, mdp->base + 0x00090);
|
||||
writel((video_packet_parameter << 16) | MDDI_VDO_PACKET_PRIM,
|
||||
mdp->base + 0x00094);
|
||||
|
||||
writel(dma2_cfg, mdp->base + 0x90000);
|
||||
|
||||
/* start DMA2 */
|
||||
writel(0, mdp->base + 0x0044);
|
||||
#elif defined(CONFIG_MSM_MDP22)
|
||||
/* setup size, address, and stride */
|
||||
mdp_writel(mdp, (height << 16) | (width),
|
||||
MDP_CMD_DEBUG_ACCESS_BASE + 0x0184);
|
||||
@ -294,7 +394,7 @@ static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride,
|
||||
/* set y & x offset and MDDI transaction parameters */
|
||||
mdp_writel(mdp, (y << 16) | (x), MDP_CMD_DEBUG_ACCESS_BASE + 0x0194);
|
||||
mdp_writel(mdp, ld_param, MDP_CMD_DEBUG_ACCESS_BASE + 0x01a0);
|
||||
mdp_writel(mdp, (MDDI_VDO_PACKET_DESC << 16) | MDDI_VDO_PACKET_PRIM,
|
||||
mdp_writel(mdp, (video_packet_parameter << 16) | MDDI_VDO_PACKET_PRIM,
|
||||
MDP_CMD_DEBUG_ACCESS_BASE + 0x01a4);
|
||||
|
||||
mdp_writel(mdp, dma2_cfg, MDP_CMD_DEBUG_ACCESS_BASE + 0x0180);
|
||||
@ -310,7 +410,7 @@ static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride,
|
||||
/* set y & x offset and MDDI transaction parameters */
|
||||
mdp_writel(mdp, (y << 16) | (x), MDP_DMA_P_OUT_XY);
|
||||
mdp_writel(mdp, ld_param, MDP_MDDI_PARAM_WR_SEL);
|
||||
mdp_writel(mdp, (MDDI_VDO_PACKET_DESC << 16) | MDDI_VDO_PACKET_PRIM,
|
||||
mdp_writel(mdp, (video_packet_parameter << 16) | MDDI_VDO_PACKET_PRIM,
|
||||
MDP_MDDI_PARAM);
|
||||
|
||||
mdp_writel(mdp, dma2_cfg, MDP_DMA_P_CONFIG);
|
||||
@ -326,7 +426,7 @@ void mdp_dma(struct mdp_device *mdp_dev, uint32_t addr, uint32_t stride,
|
||||
struct mdp_out_interface *out_if;
|
||||
unsigned long flags;
|
||||
|
||||
if (interface < 0 || interface > MSM_MDP_NUM_INTERFACES ||
|
||||
if (interface < 0 || interface >= MSM_MDP_NUM_INTERFACES ||
|
||||
!mdp->out_if[interface].registered) {
|
||||
pr_err("%s: Unknown interface: %d\n", __func__, interface);
|
||||
BUG();
|
||||
@ -335,7 +435,7 @@ void mdp_dma(struct mdp_device *mdp_dev, uint32_t addr, uint32_t stride,
|
||||
|
||||
spin_lock_irqsave(&mdp->lock, flags);
|
||||
if (locked_enable_mdp_irq(mdp, out_if->dma_mask)) {
|
||||
// pr_err("%s: busy\n", __func__);
|
||||
pr_err("%s: busy\n", __func__);
|
||||
goto done;
|
||||
}
|
||||
|
||||
@ -417,14 +517,14 @@ int mdp_check_output_format(struct mdp_device *mdp_dev, int bpp)
|
||||
int mdp_set_output_format(struct mdp_device *mdp_dev, int bpp)
|
||||
{
|
||||
struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
|
||||
uint32_t format, pack_pattern;
|
||||
uint32_t format, pack_pattern = DMA_PACK_PATTERN_RGB;
|
||||
|
||||
switch (bpp) {
|
||||
case 16:
|
||||
format = DMA_IBUF_FORMAT_RGB565;
|
||||
pack_pattern = DMA_PACK_PATTERN_RGB;
|
||||
break;
|
||||
#ifdef CONFIG_MSM_MDP22
|
||||
#if defined CONFIG_MSM_MDP22 || defined CONFIG_MSM_MDP30
|
||||
case 24:
|
||||
case 32:
|
||||
format = DMA_IBUF_FORMAT_RGB888_OR_ARGB8888;
|
||||
@ -480,8 +580,8 @@ static void dump_req(struct mdp_blit_req *req,
|
||||
}
|
||||
|
||||
int mdp_blit_and_wait(struct mdp_info *mdp, struct mdp_blit_req *req,
|
||||
struct file *src_file, unsigned long src_start, unsigned long src_len,
|
||||
struct file *dst_file, unsigned long dst_start, unsigned long dst_len)
|
||||
struct file *src_file, unsigned long src_start, unsigned long src_len,
|
||||
struct file *dst_file, unsigned long dst_start, unsigned long dst_len)
|
||||
{
|
||||
int ret;
|
||||
enable_mdp_irq(mdp, DL0_ROI_DONE);
|
||||
@ -495,17 +595,13 @@ int mdp_blit_and_wait(struct mdp_info *mdp, struct mdp_blit_req *req,
|
||||
ret = mdp_ppp_wait(mdp);
|
||||
if (unlikely(ret)) {
|
||||
printk(KERN_ERR "%s: failed!\n", __func__);
|
||||
// pr_err("original request:\n");
|
||||
// dump_req(mdp->req, src_start, src_len, dst_start, dst_len);
|
||||
pr_err("original request:\n");
|
||||
dump_req(mdp->req, src_start, src_len, dst_start, dst_len);
|
||||
pr_err("dead request:\n");
|
||||
dump_req(req, src_start, src_len, dst_start, dst_len);
|
||||
//BUG();
|
||||
BUG();
|
||||
return ret;
|
||||
}
|
||||
// Cotulla: Disable each request dump to prevent spam in logs
|
||||
// pr_err("good request:\n");
|
||||
// dump_req(req, src_start, src_len, dst_start, dst_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -517,9 +613,7 @@ int mdp_blit(struct mdp_device *mdp_dev, struct fb_info *fb,
|
||||
struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
|
||||
struct file *src_file = 0, *dst_file = 0;
|
||||
|
||||
printk("+MDP_BLIT: %08X\n", req->flags);
|
||||
|
||||
#ifdef CONFIG_MSM_MDP31
|
||||
#if defined(CONFIG_MSM_MDP31) || defined(CONFIG_MSM_MDP302)
|
||||
if (req->flags & MDP_ROT_90) {
|
||||
if (unlikely(((req->dst_rect.h == 1) &&
|
||||
((req->src_rect.w != 1) ||
|
||||
@ -567,10 +661,11 @@ int mdp_blit(struct mdp_device *mdp_dev, struct fb_info *fb,
|
||||
}
|
||||
mutex_lock(&mdp_mutex);
|
||||
|
||||
timeout_req = req;
|
||||
/* transp_masking unimplemented */
|
||||
req->transp_mask = MDP_TRANSP_NOP;
|
||||
mdp->req = req;
|
||||
#ifndef CONFIG_MSM_MDP31
|
||||
#if !defined(CONFIG_MSM_MDP31) && !defined(CONFIG_MSM_MDP302)
|
||||
if (unlikely((req->transp_mask != MDP_TRANSP_NOP ||
|
||||
req->alpha != MDP_ALPHA_NOP ||
|
||||
HAS_ALPHA(req->src.format)) &&
|
||||
@ -622,7 +717,37 @@ end:
|
||||
put_img(src_file);
|
||||
put_img(dst_file);
|
||||
mutex_unlock(&mdp_mutex);
|
||||
printk("-MDP_BLIT\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mdp_fb_mirror(struct mdp_device *mdp_dev,
|
||||
struct fb_info *src_fb, struct fb_info *dst_fb,
|
||||
struct mdp_blit_req *req)
|
||||
{
|
||||
int ret;
|
||||
struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
|
||||
|
||||
if (!src_fb || !dst_fb)
|
||||
return -EINVAL;
|
||||
|
||||
enable_mdp_irq(mdp, DL0_ROI_DONE);
|
||||
ret = mdp_ppp_blit(mdp, req,
|
||||
-1, src_fb->fix.smem_start, src_fb->fix.smem_len,
|
||||
-1, dst_fb->fix.smem_start, dst_fb->fix.smem_len);
|
||||
if (ret)
|
||||
goto err_bad_blit;
|
||||
|
||||
ret = mdp_ppp_wait(mdp);
|
||||
if (ret) {
|
||||
pr_err("mdp_ppp_wait error\n");
|
||||
goto err_wait_failed;
|
||||
}
|
||||
return 0;
|
||||
|
||||
err_bad_blit:
|
||||
disable_mdp_irq(mdp, DL0_ROI_DONE);
|
||||
|
||||
err_wait_failed:
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -690,7 +815,7 @@ int mdp_out_if_req_irq(struct mdp_device *mdp_dev, int interface,
|
||||
if (mask) {
|
||||
ret = locked_enable_mdp_irq(mdp, mask);
|
||||
if (ret) {
|
||||
// pr_err("%s: busy\n", __func__);
|
||||
pr_err("%s: busy\n", __func__);
|
||||
goto done;
|
||||
}
|
||||
mdp->out_if[interface].irq_mask = mask;
|
||||
@ -716,11 +841,26 @@ int register_mdp_client(struct class_interface *cint)
|
||||
return class_interface_register(cint);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MSM_MDP40
|
||||
void mdp_hw_init(struct mdp_info *mdp)
|
||||
{
|
||||
mdp_irq_mask = 0;
|
||||
mdp_writel(mdp, 0, MDP_INTR_ENABLE);
|
||||
}
|
||||
#else
|
||||
#include "mdp_csc_table.h"
|
||||
|
||||
void mdp_check_tearing(struct mdp_info *mdp, struct msm_mdp_platform_data *pdata)
|
||||
{
|
||||
mdp_writel(mdp, pdata->sync_config, MDP_SYNC_CONFIG_0);
|
||||
mdp_writel(mdp, 1, MDP_TEAR_CHECK_EN);
|
||||
mdp_writel(mdp, pdata->sync_thresh, MDP_SYNC_THRESH_0);
|
||||
mdp_writel(mdp, pdata->sync_start_pos, MDP_PRIM_START_POS);
|
||||
}
|
||||
void mdp_hw_init(struct mdp_info *mdp)
|
||||
{
|
||||
int n;
|
||||
int lcdc_enabled;
|
||||
|
||||
mdp_irq_mask = 0;
|
||||
|
||||
@ -731,6 +871,7 @@ void mdp_hw_init(struct mdp_info *mdp)
|
||||
mdp_writel(mdp, 1, MDP_EBI2_PORTMAP_MODE);
|
||||
|
||||
#ifndef CONFIG_MSM_MDP22
|
||||
lcdc_enabled = mdp_readl(mdp, MDP_LCDC_EN);
|
||||
/* disable lcdc */
|
||||
mdp_writel(mdp, 0, MDP_LCDC_EN);
|
||||
/* enable auto clock gating for all blocks by default */
|
||||
@ -781,15 +922,19 @@ void mdp_hw_init(struct mdp_info *mdp)
|
||||
#ifndef CONFIG_MSM_MDP31
|
||||
mdp_writel(mdp, 0x04000400, MDP_COMMAND_CONFIG);
|
||||
#endif
|
||||
#ifndef CONFIG_MSM_MDP22
|
||||
if(lcdc_enabled)
|
||||
mdp_writel(mdp, 1, MDP_LCDC_EN);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif //CONFIG_MSM_MDP40
|
||||
|
||||
int mdp_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *resource;
|
||||
int ret;
|
||||
int ret = -EINVAL;
|
||||
struct mdp_info *mdp;
|
||||
|
||||
struct msm_mdp_platform_data *pdata = pdev->dev.platform_data;
|
||||
|
||||
resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!resource) {
|
||||
@ -821,13 +966,49 @@ int mdp_probe(struct platform_device *pdev)
|
||||
mdp->mdp_dev.dma = mdp_dma;
|
||||
mdp->mdp_dev.dma_wait = mdp_dma_wait;
|
||||
mdp->mdp_dev.blit = mdp_blit;
|
||||
#ifdef CONFIG_FB_MSM_OVERLAY
|
||||
mdp->mdp_dev.overlay_get = mdp4_overlay_get;
|
||||
mdp->mdp_dev.overlay_set = mdp4_overlay_set;
|
||||
mdp->mdp_dev.overlay_unset = mdp4_overlay_unset;
|
||||
mdp->mdp_dev.overlay_play = mdp4_overlay_play;
|
||||
#endif
|
||||
mdp->mdp_dev.set_grp_disp = mdp_set_grp_disp;
|
||||
mdp->mdp_dev.set_output_format = mdp_set_output_format;
|
||||
mdp->mdp_dev.check_output_format = mdp_check_output_format;
|
||||
mdp->mdp_dev.configure_dma = mdp_configure_dma;
|
||||
|
||||
ret = mdp_out_if_register(&mdp->mdp_dev, MSM_MDDI_PMDH_INTERFACE, mdp,
|
||||
MDP_DMA_P_DONE, mdp_dma_to_mddi);
|
||||
if (pdata == NULL || pdata->overrides == 0)
|
||||
mdp->mdp_dev.overrides = 0;
|
||||
else if(pdata->overrides)
|
||||
mdp->mdp_dev.overrides = pdata->overrides;
|
||||
|
||||
if (pdata == NULL || pdata->color_format == 0)
|
||||
mdp->mdp_dev.color_format = MSM_MDP_OUT_IF_FMT_RGB565;
|
||||
else if(pdata->color_format)
|
||||
mdp->mdp_dev.color_format = pdata->color_format;
|
||||
|
||||
if (pdata == NULL || pdata->dma_channel == MDP_DMA_P) {
|
||||
#ifdef CONFIG_MSM_MDP40
|
||||
if (mdp->mdp_dev.overrides & MSM_MDP4_MDDI_DMA_SWITCH) {
|
||||
ret = mdp_out_if_register(&mdp->mdp_dev,
|
||||
MSM_MDDI_PMDH_INTERFACE, mdp, INTR_OVERLAY0_DONE
|
||||
| MDP_DMA_S_DONE, mdp4_mddi_overlay);
|
||||
} else {
|
||||
ret = mdp_out_if_register(&mdp->mdp_dev,
|
||||
MSM_MDDI_PMDH_INTERFACE, mdp, INTR_OVERLAY0_DONE,
|
||||
mdp4_mddi_overlay);
|
||||
}
|
||||
#else
|
||||
ret = mdp_out_if_register(&mdp->mdp_dev,
|
||||
MSM_MDDI_PMDH_INTERFACE, mdp, MDP_DMA_P_DONE,
|
||||
mdp_dma_to_mddi);
|
||||
#endif
|
||||
} else if (pdata->dma_channel == MDP_DMA_S) {
|
||||
ret = mdp_out_if_register(&mdp->mdp_dev,
|
||||
MSM_MDDI_PMDH_INTERFACE, mdp, MDP_DMA_S_DONE,
|
||||
mdp_dmas_to_mddi);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
goto error_mddi_pmdh_register;
|
||||
|
||||
@ -853,8 +1034,28 @@ int mdp_probe(struct platform_device *pdev)
|
||||
|
||||
clk_enable(mdp->clk);
|
||||
mdp_clk_to_disable_later = mdp->clk;
|
||||
mdp_hw_init(mdp);
|
||||
#ifdef CONFIG_MSM_MDP40
|
||||
//MDP_DISP_INTF_SEL
|
||||
if (mdp_readl(mdp, 0xc0000))
|
||||
mdp_writel(mdp, 0x8, 0x0038);
|
||||
else
|
||||
mdp_writel(mdp, 0xa, 0x0038); //mddi
|
||||
//FIXME: should select mddi or lcdc interface
|
||||
//mdp_writel(mdp, 0x8, 0x0038); //lcdc
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MSM_MDP40
|
||||
extern void mdp4_hw_init(struct mdp_info *mdp);
|
||||
mdp4_hw_init(mdp);
|
||||
#else
|
||||
mdp_hw_init(mdp);
|
||||
#endif
|
||||
|
||||
#if defined CONFIG_MSM_MDP302
|
||||
/* enable the tearing check in MDP */
|
||||
if(pdata != NULL && pdata->tearing_check)
|
||||
mdp_check_tearing(mdp, pdata);
|
||||
#endif
|
||||
/* register mdp device */
|
||||
mdp->mdp_dev.dev.parent = &pdev->dev;
|
||||
mdp->mdp_dev.dev.class = mdp_class;
|
||||
|
@ -74,23 +74,52 @@ void mdp_ppp_dump_debug(const struct mdp_info *mdp);
|
||||
#define mdp_writel(mdp, value, offset) writel(value, mdp->base + offset)
|
||||
#define mdp_readl(mdp, offset) readl(mdp->base + offset)
|
||||
|
||||
#define MDP_SYNC_CONFIG_0 (0x00000)
|
||||
#define MDP_SYNC_CONFIG_1 (0x00004)
|
||||
#define MDP_SYNC_CONFIG_2 (0x00008)
|
||||
#define MDP_SYNC_STATUS_0 (0x0000c)
|
||||
#define MDP_SYNC_STATUS_1 (0x00010)
|
||||
#define MDP_SYNC_STATUS_2 (0x00014)
|
||||
#define MDP_SYNC_THRESH_0 (0x00018)
|
||||
#define MDP_SYNC_THRESH_1 (0x0001c)
|
||||
#define MDP_INTR_ENABLE (0x00020)
|
||||
#define MDP_INTR_STATUS (0x00024)
|
||||
#define MDP_INTR_CLEAR (0x00028)
|
||||
#define MDP_DISPLAY0_START (0x00030)
|
||||
#define MDP_DISPLAY1_START (0x00034)
|
||||
#define MDP_DISPLAY_STATUS (0x00038)
|
||||
#define MDP_EBI2_LCD0 (0x0003c)
|
||||
#define MDP_EBI2_LCD1 (0x00040)
|
||||
#define MDP_EBI2_PORTMAP_MODE (0x0005c)
|
||||
#ifdef CONFIG_MSM_MDP302
|
||||
#define MDP_SYNC_CONFIG_0 ( 0x00300)
|
||||
#define MDP_SYNC_CONFIG_1 ( 0x00304)
|
||||
#define MDP_SYNC_CONFIG_2 ( 0x00308)
|
||||
#else
|
||||
#define MDP_SYNC_CONFIG_0 ( 0x00000)
|
||||
#define MDP_SYNC_CONFIG_1 ( 0x00004)
|
||||
#define MDP_SYNC_CONFIG_2 ( 0x00008)
|
||||
#endif
|
||||
|
||||
#define MDP_SYNC_STATUS_0 ( 0x0000c)
|
||||
#define MDP_SYNC_STATUS_1 ( 0x00010)
|
||||
#define MDP_SYNC_STATUS_2 ( 0x00014)
|
||||
|
||||
#ifdef CONFIG_MSM_MDP302
|
||||
#define MDP_SYNC_THRESH_0 ( 0x00200)
|
||||
#define MDP_SYNC_THRESH_1 ( 0x00204)
|
||||
#else
|
||||
#define MDP_SYNC_THRESH_0 ( 0x00018)
|
||||
#define MDP_SYNC_THRESH_1 ( 0x0001c)
|
||||
#endif
|
||||
#ifdef CONFIG_MSM_MDP40
|
||||
#define MDP_INTR_ENABLE ( 0x0050)
|
||||
#define MDP_INTR_STATUS ( 0x0054)
|
||||
#define MDP_INTR_CLEAR ( 0x0058)
|
||||
#define MDP_EBI2_LCD0 ( 0x0060)
|
||||
#define MDP_EBI2_LCD1 ( 0x0064)
|
||||
#define MDP_EBI2_PORTMAP_MODE ( 0x0070)
|
||||
|
||||
#define MDP_DMA_P_HIST_INTR_STATUS ( 0x95014)
|
||||
#define MDP_DMA_P_HIST_INTR_CLEAR ( 0x95018)
|
||||
#define MDP_DMA_P_HIST_INTR_ENABLE ( 0x9501C)
|
||||
#else
|
||||
#define MDP_INTR_ENABLE ( 0x00020)
|
||||
#define MDP_INTR_STATUS ( 0x00024)
|
||||
#define MDP_INTR_CLEAR ( 0x00028)
|
||||
#define MDP_EBI2_LCD0 ( 0x0003c)
|
||||
#define MDP_EBI2_LCD1 ( 0x00040)
|
||||
#define MDP_EBI2_PORTMAP_MODE ( 0x0005c)
|
||||
#endif
|
||||
#define MDP_DISPLAY0_START ( 0x00030)
|
||||
#define MDP_DISPLAY1_START ( 0x00034)
|
||||
#define MDP_DISPLAY_STATUS ( 0x00038)
|
||||
/* CONFIG_MSM_MDP302 */
|
||||
#define MDP_TEAR_CHECK_EN ( 0x0020c)
|
||||
#define MDP_PRIM_START_POS ( 0x00210)
|
||||
|
||||
#ifndef CONFIG_MSM_MDP31
|
||||
#define MDP_DISPLAY0_ADDR (0x00054)
|
||||
@ -228,7 +257,11 @@ void mdp_ppp_dump_debug(const struct mdp_info *mdp);
|
||||
#define MDP_TEST_CAPTURED_DCLK (0xd0210)
|
||||
#define MDP_TEST_MISR_CAPT_VAL_DCLK (0xd0214)
|
||||
|
||||
#ifdef CONFIG_MSM_MDP40
|
||||
#define MDP_DMA_P_START (0x000c)
|
||||
#else
|
||||
#define MDP_DMA_P_START (0x00044)
|
||||
#endif
|
||||
#define MDP_DMA_P_CONFIG (0x90000)
|
||||
#define MDP_DMA_P_SIZE (0x90004)
|
||||
#define MDP_DMA_P_IBUF_ADDR (0x90008)
|
||||
@ -236,6 +269,30 @@ void mdp_ppp_dump_debug(const struct mdp_info *mdp);
|
||||
#define MDP_DMA_P_OUT_XY (0x90010)
|
||||
#define MDP_DMA_P_COLOR_CORRECT_CONFIG (0x90070)
|
||||
|
||||
#define MDP_DMA_S_START (0x00048)
|
||||
#define MDP_DMA_S_CONFIG (0xa0000)
|
||||
#define MDP_DMA_S_SIZE (0xa0004)
|
||||
#define MDP_DMA_S_IBUF_ADDR (0xa0008)
|
||||
#define MDP_DMA_S_IBUF_Y_STRIDE (0xa000c)
|
||||
#define MDP_DMA_S_OUT_XY (0xa0010)
|
||||
|
||||
#ifdef CONFIG_MSM_MDP40
|
||||
#define MDP_LCDC_EN (0xc0000)
|
||||
#define MDP_LCDC_HSYNC_CTL (0xc0004)
|
||||
#define MDP_LCDC_VSYNC_PERIOD (0xc0008)
|
||||
#define MDP_LCDC_VSYNC_PULSE_WIDTH (0xc000c)
|
||||
#define MDP_LCDC_DISPLAY_HCTL (0xc0010)
|
||||
#define MDP_LCDC_DISPLAY_V_START (0xc0014)
|
||||
#define MDP_LCDC_DISPLAY_V_END (0xc0018)
|
||||
#define MDP_LCDC_ACTIVE_HCTL (0xc001c)
|
||||
#define MDP_LCDC_ACTIVE_V_START (0xc0020)
|
||||
#define MDP_LCDC_ACTIVE_V_END (0xc0024)
|
||||
#define MDP_LCDC_BORDER_CLR (0xc0028)
|
||||
#define MDP_LCDC_UNDERFLOW_CTL (0xc002c)
|
||||
#define MDP_LCDC_HSYNC_SKEW (0xc0030)
|
||||
#define MDP_LCDC_TEST_CTL (0xc0034)
|
||||
#define MDP_LCDC_CTL_POLARITY (0xc0038)
|
||||
#else
|
||||
#define MDP_LCDC_EN (0xe0000)
|
||||
#define MDP_LCDC_HSYNC_CTL (0xe0004)
|
||||
#define MDP_LCDC_VSYNC_PERIOD (0xe0008)
|
||||
@ -251,6 +308,7 @@ void mdp_ppp_dump_debug(const struct mdp_info *mdp);
|
||||
#define MDP_LCDC_HSYNC_SKEW (0xe0030)
|
||||
#define MDP_LCDC_TEST_CTL (0xe0034)
|
||||
#define MDP_LCDC_CTL_POLARITY (0xe0038)
|
||||
#endif
|
||||
|
||||
#define MDP_PPP_SCALE_STATUS (0x50000)
|
||||
#define MDP_PPP_BLEND_STATUS (0x70000)
|
||||
@ -262,15 +320,30 @@ void mdp_ppp_dump_debug(const struct mdp_info *mdp);
|
||||
#define DL0_ROI_DONE (1<<0)
|
||||
#define TV_OUT_DMA3_DONE (1<<6)
|
||||
#define TV_ENC_UNDERRUN (1<<7)
|
||||
#define TV_OUT_FRAME_START (1<<13)
|
||||
|
||||
#ifdef CONFIG_MSM_MDP22
|
||||
#define MDP_DMA_P_DONE (1 << 2)
|
||||
#define MDP_DMA_S_DONE (1 << 3)
|
||||
#else /* CONFIG_MSM_MDP31 */
|
||||
|
||||
#ifdef CONFIG_MSM_MDP40
|
||||
#define MDP_DMA_P_DONE (1 << 4)
|
||||
#else
|
||||
#define MDP_DMA_P_DONE (1 << 14)
|
||||
#endif
|
||||
|
||||
#define MDP_DMA_S_DONE (1 << 2)
|
||||
#define MDP_LCDC_UNDERFLOW (1 << 16)
|
||||
|
||||
#ifdef CONFIG_MSM_MDP40
|
||||
#define MDP_LCDC_FRAME_START (1 << 7)
|
||||
#else
|
||||
#define MDP_LCDC_FRAME_START (1 << 15)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#define MDP_TOP_LUMA 16
|
||||
#define MDP_TOP_CHROMA 0
|
||||
#define MDP_BOTTOM_LUMA 19
|
||||
@ -294,9 +367,15 @@ void mdp_ppp_dump_debug(const struct mdp_info *mdp);
|
||||
(((a)<<(bit*3))|((x)<<(bit*2))|((y)<<bit)|(z))
|
||||
|
||||
/* MDP_SYNC_CONFIG_0/1/2 */
|
||||
#if defined(CONFIG_MSM_MDP30)
|
||||
#define MDP_SYNCFG_HGT_LOC 21
|
||||
#define MDP_SYNCFG_VSYNC_EXT_EN (1<<20)
|
||||
#define MDP_SYNCFG_VSYNC_INT_EN (1<<19)
|
||||
#else
|
||||
#define MDP_SYNCFG_HGT_LOC 22
|
||||
#define MDP_SYNCFG_VSYNC_EXT_EN (1<<21)
|
||||
#define MDP_SYNCFG_VSYNC_INT_EN (1<<20)
|
||||
#endif
|
||||
|
||||
/* MDP_SYNC_THRESH_0 */
|
||||
#define MDP_PRIM_BELOW_LOC 0
|
||||
@ -309,11 +388,11 @@ void mdp_ppp_dump_debug(const struct mdp_info *mdp);
|
||||
/* MDP_VSYNC_CTRL */
|
||||
#define DISP0_VSYNC_MAP_VSYNC0 0
|
||||
#define DISP0_VSYNC_MAP_VSYNC1 (1<<0)
|
||||
#define DISP0_VSYNC_MAP_VSYNC2 ((1<<0)|(1<<1))
|
||||
#define DISP0_VSYNC_MAP_VSYNC2 (1<<0)|(1<<1)
|
||||
|
||||
#define DISP1_VSYNC_MAP_VSYNC0 0
|
||||
#define DISP1_VSYNC_MAP_VSYNC1 (1<<2)
|
||||
#define DISP1_VSYNC_MAP_VSYNC2 ((1<<2)|(1<<3))
|
||||
#define DISP1_VSYNC_MAP_VSYNC2 (1<<2)|(1<<3)
|
||||
|
||||
#define PRIMARY_LCD_SYNC_EN (1<<4)
|
||||
#define PRIMARY_LCD_SYNC_DISABLE 0
|
||||
@ -744,7 +823,25 @@ void mdp_ppp_dump_debug(const struct mdp_info *mdp);
|
||||
#define DMA_IBUF_FORMAT_MASK (1 << 20)
|
||||
#define DMA_IBUF_NONCONTIGUOUS (1<<21)
|
||||
|
||||
#else /* CONFIG_MSM_MDP31 */
|
||||
#elif defined(CONFIG_MSM_MDP30)
|
||||
|
||||
#define DMA_OUT_SEL_AHB 0
|
||||
#define DMA_OUT_SEL_MDDI (1<<19)
|
||||
#define DMA_AHBM_LCD_SEL_PRIMARY 0
|
||||
#define DMA_AHBM_LCD_SEL_SECONDARY (0)
|
||||
#define DMA_IBUF_C3ALPHA_EN (0)
|
||||
#define DMA_DITHER_EN (1<<24)
|
||||
|
||||
#define DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY 0
|
||||
#define DMA_MDDI_DMAOUT_LCD_SEL_SECONDARY (0)
|
||||
#define DMA_MDDI_DMAOUT_LCD_SEL_EXTERNAL (0)
|
||||
|
||||
#define DMA_IBUF_FORMAT_MASK (1 << 20)
|
||||
#define DMA_IBUF_FORMAT_RGB565 (1<<25)
|
||||
#define DMA_IBUF_FORMAT_RGB888_OR_ARGB8888 (1<<26)
|
||||
#define DMA_IBUF_NONCONTIGUOUS (0)
|
||||
|
||||
#else /* CONFIG_MSM_MDP31 | CONFIG_MSM_MDP302 */
|
||||
|
||||
#define DMA_OUT_SEL_AHB (0 << 19)
|
||||
#define DMA_OUT_SEL_MDDI (1 << 19)
|
||||
@ -763,7 +860,8 @@ void mdp_ppp_dump_debug(const struct mdp_info *mdp);
|
||||
#endif
|
||||
|
||||
/* MDDI REGISTER ? */
|
||||
#define MDDI_VDO_PACKET_DESC 0x5666
|
||||
#define MDDI_VDO_PACKET_DESC_RGB565 0x5565
|
||||
#define MDDI_VDO_PACKET_DESC_RGB666 0x5666
|
||||
#define MDDI_VDO_PACKET_PRIM 0xC3
|
||||
#define MDDI_VDO_PACKET_SECD 0xC0
|
||||
|
||||
|
@ -26,11 +26,23 @@
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/mach-types.h>
|
||||
|
||||
#include <linux/msm_mdp.h>
|
||||
#include <mach/msm_fb.h>
|
||||
|
||||
#include "mdp_hw.h"
|
||||
#ifdef CONFIG_MSM_MDP40
|
||||
#include "mdp4.h"
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#define D(fmt, args...) printk(KERN_INFO "Dispaly: " fmt, ##args)
|
||||
#else
|
||||
#define D(fmt, args...) do {} while (0)
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_ARCH_MSM7227)
|
||||
#define LCDC_MUX_CTL (MSM_TGPIO1_BASE + 0x278)
|
||||
#endif
|
||||
struct mdp_lcdc_info {
|
||||
struct mdp_info *mdp;
|
||||
struct clk *mdp_clk;
|
||||
@ -44,7 +56,7 @@ struct mdp_lcdc_info {
|
||||
struct msmfb_callback frame_start_cb;
|
||||
wait_queue_head_t vsync_waitq;
|
||||
int got_vsync;
|
||||
|
||||
unsigned color_format;
|
||||
struct {
|
||||
uint32_t clk_rate;
|
||||
uint32_t hsync_ctl;
|
||||
@ -60,6 +72,10 @@ struct mdp_lcdc_info {
|
||||
|
||||
static struct mdp_device *mdp_dev;
|
||||
|
||||
#ifdef CONFIG_MSM_MDP40
|
||||
static struct mdp4_overlay_pipe *lcdc_pipe;
|
||||
#endif
|
||||
|
||||
#define panel_to_lcdc(p) container_of((p), struct mdp_lcdc_info, fb_panel_data)
|
||||
|
||||
static int lcdc_unblank(struct msm_panel_data *fb_panel)
|
||||
@ -68,6 +84,7 @@ static int lcdc_unblank(struct msm_panel_data *fb_panel)
|
||||
struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops;
|
||||
|
||||
pr_info("%s: ()\n", __func__);
|
||||
|
||||
panel_ops->unblank(panel_ops);
|
||||
|
||||
return 0;
|
||||
@ -86,27 +103,49 @@ static int lcdc_blank(struct msm_panel_data *fb_panel)
|
||||
|
||||
static int lcdc_suspend(struct msm_panel_data *fb_panel)
|
||||
{
|
||||
int status;
|
||||
struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
|
||||
struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops;
|
||||
|
||||
pr_info("%s: suspending\n", __func__);
|
||||
|
||||
#if defined(CONFIG_ARCH_MSM7227)
|
||||
writel(0x0, LCDC_MUX_CTL);
|
||||
status = readl(LCDC_MUX_CTL);
|
||||
D("suspend_lcdc_mux_ctl = %x\n", status);
|
||||
#endif
|
||||
mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN);
|
||||
clk_disable(lcdc->pad_pclk);
|
||||
clk_disable(lcdc->pclk);
|
||||
clk_disable(lcdc->mdp_clk);
|
||||
if (panel_ops->uninit)
|
||||
panel_ops->uninit(panel_ops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lcdc_resume(struct msm_panel_data *fb_panel)
|
||||
{
|
||||
unsigned int status;
|
||||
struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel);
|
||||
struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops;
|
||||
|
||||
pr_info("%s: resuming\n", __func__);
|
||||
|
||||
if (panel_ops->init) {
|
||||
if (panel_ops->init(panel_ops) < 0)
|
||||
printk(KERN_ERR "LCD init fail!\n");
|
||||
}
|
||||
|
||||
clk_enable(lcdc->mdp_clk);
|
||||
clk_enable(lcdc->pclk);
|
||||
clk_enable(lcdc->pad_pclk);
|
||||
#if defined(CONFIG_ARCH_MSM7227)
|
||||
writel(0x1, LCDC_MUX_CTL);
|
||||
status = readl(LCDC_MUX_CTL);
|
||||
D("resume_lcdc_mux_ctl = %x\n",status);
|
||||
#endif
|
||||
|
||||
mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN);
|
||||
|
||||
return 0;
|
||||
@ -124,8 +163,6 @@ static int lcdc_hw_init(struct mdp_lcdc_info *lcdc)
|
||||
clk_set_rate(lcdc->pclk, lcdc->parms.clk_rate);
|
||||
clk_set_rate(lcdc->pad_pclk, lcdc->parms.clk_rate);
|
||||
|
||||
// mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN); // Cotulla
|
||||
|
||||
/* write the lcdc params */
|
||||
mdp_writel(lcdc->mdp, lcdc->parms.hsync_ctl, MDP_LCDC_HSYNC_CTL);
|
||||
mdp_writel(lcdc->mdp, lcdc->parms.vsync_period, MDP_LCDC_VSYNC_PERIOD);
|
||||
@ -138,7 +175,7 @@ static int lcdc_hw_init(struct mdp_lcdc_info *lcdc)
|
||||
mdp_writel(lcdc->mdp, lcdc->parms.hsync_skew, MDP_LCDC_HSYNC_SKEW);
|
||||
|
||||
mdp_writel(lcdc->mdp, 0, MDP_LCDC_BORDER_CLR);
|
||||
mdp_writel(lcdc->mdp, 0xFFFFFF | 0x80000000, MDP_LCDC_UNDERFLOW_CTL); // Cotulla
|
||||
mdp_writel(lcdc->mdp, 0xff, MDP_LCDC_UNDERFLOW_CTL);
|
||||
mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_HCTL);
|
||||
mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_START);
|
||||
mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_END);
|
||||
@ -152,38 +189,28 @@ static int lcdc_hw_init(struct mdp_lcdc_info *lcdc)
|
||||
|
||||
mdp_writel(lcdc->mdp, 0, MDP_DMA_P_OUT_XY);
|
||||
|
||||
dma_cfg = mdp_readl(lcdc->mdp, MDP_DMA_P_CONFIG);
|
||||
|
||||
|
||||
dma_cfg |= (DMA_PACK_ALIGN_LSB |
|
||||
DMA_PACK_PATTERN_RGB |
|
||||
DMA_DITHER_EN);
|
||||
dma_cfg |= DMA_OUT_SEL_LCDC;
|
||||
dma_cfg &= ~DMA_DST_BITS_MASK;
|
||||
|
||||
if(machine_is_htcleo()) {
|
||||
dma_cfg = DMA_PACK_ALIGN_MSB |
|
||||
DMA_PACK_PATTERN_RGB;
|
||||
|
||||
dma_cfg |= DMA_OUT_SEL_LCDC;
|
||||
dma_cfg &= ~DMA_DST_BITS_MASK;
|
||||
}
|
||||
else {
|
||||
dma_cfg |= (DMA_PACK_ALIGN_LSB |
|
||||
DMA_PACK_PATTERN_RGB |
|
||||
} else {
|
||||
dma_cfg = mdp_readl(lcdc->mdp, MDP_DMA_P_CONFIG);
|
||||
if (lcdc->pdata->overrides & MSM_MDP_LCDC_DMA_PACK_ALIGN_LSB)
|
||||
dma_cfg &= ~DMA_PACK_ALIGN_MSB;
|
||||
else
|
||||
dma_cfg |= DMA_PACK_ALIGN_MSB;
|
||||
|
||||
dma_cfg |= (DMA_PACK_PATTERN_RGB |
|
||||
DMA_DITHER_EN);
|
||||
dma_cfg |= DMA_OUT_SEL_LCDC;
|
||||
dma_cfg &= ~DMA_DST_BITS_MASK;
|
||||
}
|
||||
|
||||
/* enable the lcdc timing generation */
|
||||
mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN);
|
||||
|
||||
|
||||
if (fb_panel->fb_data->output_format == MSM_MDP_OUT_IF_FMT_RGB666)
|
||||
dma_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS;
|
||||
else
|
||||
if(lcdc->color_format == MSM_MDP_OUT_IF_FMT_RGB565)
|
||||
dma_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS;
|
||||
else if (lcdc->color_format == MSM_MDP_OUT_IF_FMT_RGB666)
|
||||
dma_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS;
|
||||
|
||||
mdp_writel(lcdc->mdp, dma_cfg, MDP_DMA_P_CONFIG);
|
||||
|
||||
@ -193,14 +220,12 @@ static int lcdc_hw_init(struct mdp_lcdc_info *lcdc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void lcdc_wait_vsync(struct msm_panel_data *panel)
|
||||
{
|
||||
struct mdp_lcdc_info *lcdc = panel_to_lcdc(panel);
|
||||
int ret;
|
||||
|
||||
ret = wait_event_timeout(lcdc->vsync_waitq, lcdc->got_vsync, HZ / 2);
|
||||
|
||||
ret = wait_event_timeout(lcdc->vsync_waitq, lcdc->got_vsync, HZ / 2);
|
||||
if (!ret && !lcdc->got_vsync)
|
||||
pr_err("%s: timeout waiting for VSYNC\n", __func__);
|
||||
lcdc->got_vsync = 0;
|
||||
@ -233,13 +258,13 @@ static void lcdc_request_vsync(struct msm_panel_data *fb_panel,
|
||||
// vsync_cb->func(vsync_cb);
|
||||
}
|
||||
else
|
||||
{
|
||||
lcdc->got_vsync = 0;
|
||||
mdp_out_if_req_irq(mdp_dev, MSM_LCDC_INTERFACE, MDP_LCDC_FRAME_START, &lcdc->frame_start_cb);
|
||||
lcdc_wait_vsync(fb_panel);
|
||||
}
|
||||
{
|
||||
lcdc->got_vsync = 0;
|
||||
mdp_out_if_req_irq(mdp_dev, MSM_LCDC_INTERFACE, MDP_LCDC_FRAME_START,
|
||||
&lcdc->frame_start_cb);
|
||||
lcdc_wait_vsync(fb_panel);
|
||||
}
|
||||
// CotullaFIX end
|
||||
|
||||
}
|
||||
|
||||
static void lcdc_clear_vsync(struct msm_panel_data *fb_panel)
|
||||
@ -266,13 +291,11 @@ static void lcdc_dma_start(void *priv, uint32_t addr, uint32_t stride,
|
||||
uint32_t y)
|
||||
{
|
||||
struct mdp_lcdc_info *lcdc = priv;
|
||||
|
||||
// printk("DMA start\n");
|
||||
struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
|
||||
if (mdp->dma_config_dirty)
|
||||
{
|
||||
mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN);
|
||||
mdelay(20);
|
||||
mdelay(30);
|
||||
mdp_dev->configure_dma(mdp_dev);
|
||||
mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN);
|
||||
}
|
||||
@ -280,6 +303,35 @@ static void lcdc_dma_start(void *priv, uint32_t addr, uint32_t stride,
|
||||
mdp_writel(lcdc->mdp, addr, MDP_DMA_P_IBUF_ADDR);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MSM_MDP40
|
||||
static void lcdc_overlay_start(void *priv, uint32_t addr, uint32_t stride,
|
||||
uint32_t width, uint32_t height, uint32_t x,
|
||||
uint32_t y)
|
||||
{
|
||||
struct mdp_lcdc_info *lcdc = priv;
|
||||
struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
|
||||
|
||||
struct mdp4_overlay_pipe *pipe;
|
||||
pipe = lcdc_pipe;
|
||||
pipe->srcp0_addr = addr;
|
||||
|
||||
if (mdp->dma_config_dirty)
|
||||
{
|
||||
if(mdp->format == DMA_IBUF_FORMAT_RGB565) {
|
||||
pipe->src_format = MDP_RGB_565;
|
||||
pipe->srcp0_ystride = pipe->src_width * 2;
|
||||
} else if(mdp->format == DMA_IBUF_FORMAT_XRGB8888) {
|
||||
pipe->src_format = MDP_RGBA_8888;
|
||||
pipe->srcp0_ystride = pipe->src_width * 4;
|
||||
}
|
||||
mdp4_overlay_format2pipe(pipe);
|
||||
mdp->dma_config_dirty = false;
|
||||
}
|
||||
mdp4_overlay_rgb_setup(pipe);
|
||||
mdp4_overlay_reg_flush(pipe, 1); /* rgb1 and mixer0 */
|
||||
|
||||
}
|
||||
#endif
|
||||
static void precompute_timing_parms(struct mdp_lcdc_info *lcdc)
|
||||
{
|
||||
struct msm_lcdc_timing *timing = lcdc->pdata->timing;
|
||||
@ -291,21 +343,21 @@ static void precompute_timing_parms(struct mdp_lcdc_info *lcdc)
|
||||
unsigned int display_vstart;
|
||||
unsigned int display_vend;
|
||||
|
||||
hsync_period = (timing->hsync_back_porch +
|
||||
hsync_period = (timing->hsync_pulse_width + timing->hsync_back_porch +
|
||||
fb_data->xres + timing->hsync_front_porch);
|
||||
hsync_start_x = timing->hsync_back_porch;
|
||||
hsync_start_x = (timing->hsync_pulse_width + timing->hsync_back_porch);
|
||||
hsync_end_x = hsync_start_x + fb_data->xres - 1;
|
||||
|
||||
vsync_period = (timing->vsync_back_porch +
|
||||
vsync_period = (timing->vsync_pulse_width + timing->vsync_back_porch +
|
||||
fb_data->yres + timing->vsync_front_porch);
|
||||
vsync_period *= hsync_period;
|
||||
|
||||
display_vstart = timing->vsync_back_porch;
|
||||
display_vstart = timing->vsync_pulse_width + timing->vsync_back_porch;
|
||||
display_vstart *= hsync_period;
|
||||
display_vstart += timing->hsync_skew;
|
||||
|
||||
display_vend = (timing->vsync_back_porch + fb_data->yres) *
|
||||
hsync_period;
|
||||
display_vend = (timing->vsync_pulse_width + timing->vsync_back_porch +
|
||||
fb_data->yres) * hsync_period;
|
||||
display_vend += timing->hsync_skew - 1;
|
||||
|
||||
/* register values we pre-compute at init time from the timing
|
||||
@ -332,6 +384,10 @@ static int mdp_lcdc_probe(struct platform_device *pdev)
|
||||
struct msm_lcdc_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct mdp_lcdc_info *lcdc;
|
||||
int ret = 0;
|
||||
#ifdef CONFIG_MSM_MDP40
|
||||
struct mdp4_overlay_pipe *pipe;
|
||||
int ptype;
|
||||
#endif
|
||||
|
||||
if (!pdata) {
|
||||
pr_err("%s: no LCDC platform data found\n", __func__);
|
||||
@ -369,25 +425,61 @@ static int mdp_lcdc_probe(struct platform_device *pdev)
|
||||
lcdc->frame_start_cb.func = lcdc_frame_start;
|
||||
|
||||
platform_set_drvdata(pdev, lcdc);
|
||||
|
||||
#ifdef CONFIG_MSM_MDP40
|
||||
mdp_out_if_register(mdp_dev, MSM_LCDC_INTERFACE, lcdc, INTR_OVERLAY0_DONE,
|
||||
lcdc_overlay_start);
|
||||
#else
|
||||
mdp_out_if_register(mdp_dev, MSM_LCDC_INTERFACE, lcdc, MDP_DMA_P_DONE,
|
||||
lcdc_dma_start);
|
||||
|
||||
#endif
|
||||
precompute_timing_parms(lcdc);
|
||||
|
||||
lcdc->fb_start = pdata->fb_resource->start;
|
||||
lcdc->mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
|
||||
if(lcdc->mdp->mdp_dev.color_format)
|
||||
lcdc->color_format = lcdc->mdp->mdp_dev.color_format;
|
||||
else
|
||||
lcdc->color_format = MSM_MDP_OUT_IF_FMT_RGB565;
|
||||
|
||||
#ifdef CONFIG_MSM_MDP40
|
||||
if (lcdc_pipe == NULL) {
|
||||
ptype = mdp4_overlay_format2type(MDP_RGB_565);
|
||||
pipe = mdp4_overlay_pipe_alloc(ptype);
|
||||
pipe->mixer_stage = MDP4_MIXER_STAGE_BASE;
|
||||
pipe->mixer_num = MDP4_MIXER0;
|
||||
pipe->src_format = MDP_RGB_565;
|
||||
mdp4_overlay_format2pipe(pipe);
|
||||
pipe->mdp = lcdc->mdp;
|
||||
|
||||
lcdc_pipe = pipe; /* keep it */
|
||||
} else {
|
||||
pipe = lcdc_pipe;
|
||||
}
|
||||
|
||||
pipe->src_height = pdata->fb_data->yres;
|
||||
pipe->src_width = pdata->fb_data->xres;
|
||||
pipe->src_h = pdata->fb_data->yres;
|
||||
pipe->src_w = pdata->fb_data->xres;
|
||||
pipe->src_y = 0;
|
||||
pipe->src_x = 0;
|
||||
pipe->srcp0_addr = (uint32_t) lcdc->fb_start;
|
||||
pipe->srcp0_ystride = pdata->fb_data->xres * 2;
|
||||
|
||||
mdp4_overlay_dmap_xy(pipe);
|
||||
mdp4_overlay_dmap_cfg(pipe, 1);
|
||||
|
||||
mdp4_overlay_rgb_setup(pipe);
|
||||
|
||||
mdp4_mixer_stage_up(pipe);
|
||||
|
||||
mdp4_overlayproc_cfg(pipe);
|
||||
mdp4_overlay_reg_flush(pipe, 1);
|
||||
#endif
|
||||
|
||||
lcdc->fb_panel_data.suspend = lcdc_suspend;
|
||||
lcdc->fb_panel_data.resume = lcdc_resume;
|
||||
lcdc->fb_panel_data.wait_vsync = lcdc_wait_vsync;
|
||||
//#if defined(CONFIG_MACH_HTCLEO)
|
||||
// Temporarily disable vsync to prevent a scheduler bug, need
|
||||
// to be looked into further.
|
||||
// lcdc->fb_panel_data.request_vsync = 0;
|
||||
//#else
|
||||
lcdc->fb_panel_data.request_vsync = lcdc_request_vsync;
|
||||
//#endif
|
||||
lcdc->fb_panel_data.clear_vsync = lcdc_clear_vsync;
|
||||
lcdc->fb_panel_data.blank = lcdc_blank;
|
||||
lcdc->fb_panel_data.unblank = lcdc_unblank;
|
||||
@ -395,7 +487,6 @@ static int mdp_lcdc_probe(struct platform_device *pdev)
|
||||
lcdc->fb_panel_data.interface_type = MSM_LCDC_INTERFACE;
|
||||
|
||||
ret = lcdc_hw_init(lcdc);
|
||||
// ret = 0;
|
||||
if (ret) {
|
||||
pr_err("%s: Cannot initialize the mdp_lcdc\n", __func__);
|
||||
goto err_hw_init;
|
||||
@ -407,8 +498,6 @@ static int mdp_lcdc_probe(struct platform_device *pdev)
|
||||
lcdc->fb_pdev.num_resources = 1;
|
||||
lcdc->fb_pdev.dev.platform_data = &lcdc->fb_panel_data;
|
||||
|
||||
if (pdata->panel_ops->init)
|
||||
pdata->panel_ops->init(pdata->panel_ops);
|
||||
|
||||
ret = platform_device_register(&lcdc->fb_pdev);
|
||||
if (ret) {
|
||||
|
@ -52,19 +52,19 @@ static uint32_t dst_img_cfg[] = {
|
||||
PPP_ARRAY1(CFG, DST)
|
||||
};
|
||||
|
||||
static const uint32_t bytes_per_pixel[] = {
|
||||
static uint32_t bytes_per_pixel[] = {
|
||||
[MDP_RGB_565] = 2,
|
||||
[MDP_XRGB_8888] = 4,
|
||||
[MDP_Y_CBCR_H2V2] = 1,
|
||||
[MDP_ARGB_8888] = 4,
|
||||
[MDP_RGB_888] = 3,
|
||||
[MDP_Y_CRCB_H2V2] = 1,
|
||||
[MDP_YCRYCB_H2V1] = 2,
|
||||
[MDP_Y_CRCB_H2V1] = 1,
|
||||
[MDP_Y_CBCR_H2V1] = 1,
|
||||
[MDP_XRGB_8888] = 4,
|
||||
[MDP_ARGB_8888] = 4,
|
||||
[MDP_RGBA_8888] = 4,
|
||||
[MDP_BGRA_8888] = 4,
|
||||
[MDP_RGBX_8888] = 4,
|
||||
[MDP_Y_CBCR_H2V1] = 1,
|
||||
[MDP_Y_CBCR_H2V2] = 1,
|
||||
[MDP_Y_CRCB_H2V1] = 1,
|
||||
[MDP_Y_CRCB_H2V2] = 1,
|
||||
[MDP_YCRYCB_H2V1] = 2
|
||||
};
|
||||
|
||||
static uint32_t dst_op_chroma[] = {
|
||||
@ -268,7 +268,7 @@ static void blit_blend(struct mdp_blit_req *req, struct ppp_regs *regs)
|
||||
req->alpha &= 0xff;
|
||||
/* ALPHA BLEND */
|
||||
if (HAS_ALPHA(req->src.format)) {
|
||||
#if 0
|
||||
#if !defined(CONFIG_MACH_HTCLEO)
|
||||
regs->op |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON;
|
||||
if (req->flags & MDP_BLEND_FG_PREMULT) {
|
||||
#ifdef CONFIG_MSM_MDP31
|
||||
@ -282,7 +282,7 @@ static void blit_blend(struct mdp_blit_req *req, struct ppp_regs *regs)
|
||||
regs->op |= PPP_OP_BLEND_CONSTANT_ALPHA;
|
||||
req->alpha = 0xff;
|
||||
#endif
|
||||
} els {
|
||||
} else {
|
||||
regs->op |= PPP_OP_BLEND_SRCPIXEL_ALPHA;
|
||||
}
|
||||
#else
|
||||
@ -499,19 +499,21 @@ static int send_blit(const struct mdp_info *mdp, struct mdp_blit_req *req,
|
||||
#ifdef CONFIG_MSM_MDP31
|
||||
mdp_writel_dbg(mdp, regs->bg_xy, MDP_PPP_BG_XY);
|
||||
mdp_writel_dbg(mdp, regs->bg_img_sz, MDP_PPP_BG_IMAGE_SIZE);
|
||||
mdp_writel_dbg(mdp, regs->bg_alpha_sel, MDP_PPP_BLEND_BG_ALPHA_SEL);
|
||||
|
||||
mdp_writel_dbg(mdp, 0, MDP_TFETCH_TEST_MODE);
|
||||
|
||||
mdp_writel_dbg(mdp, regs->bg_alpha_sel,
|
||||
MDP_PPP_BLEND_BG_ALPHA_SEL);
|
||||
|
||||
#if defined(CONFIG_MACH_HTCLEO)
|
||||
mdp_writel_dbg(mdp, 0, MDP_TFETCH_TEST_MODE);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
flush_imgs(req, regs, src_file, dst_file);
|
||||
if( src_file != -1 && dst_file != -1 )
|
||||
flush_imgs(req, regs, src_file, dst_file);
|
||||
mdp_writel_dbg(mdp, 0x1000, MDP_DISPLAY0_START);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if PPP_DUMP_BLITS
|
||||
static void mdp_dump_blit(struct mdp_blit_req *req)
|
||||
void mdp_dump_blit(struct mdp_blit_req *req)
|
||||
{
|
||||
pr_info("%s: src: w=%d h=%d f=0x%x offs=0x%x mem_id=%d\n", __func__,
|
||||
req->src.width, req->src.height, req->src.format,
|
||||
@ -529,7 +531,6 @@ static void mdp_dump_blit(struct mdp_blit_req *req)
|
||||
pr_info("%s: transp_max=0x%08x\n", __func__, req->transp_mask);
|
||||
pr_info("%s: flags=%08x\n", __func__, req->flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req,
|
||||
struct file *src_file, unsigned long src_start, unsigned long src_len,
|
||||
@ -655,13 +656,6 @@ int mdp_get_bytes_per_pixel(int format)
|
||||
|
||||
void mdp_ppp_dump_debug(const struct mdp_info *mdp)
|
||||
{
|
||||
mdp_dump_register(mdp, MDP_CMD_STATUS);
|
||||
mdp_dump_register(mdp, MDP_DMA_P_CONFIG);
|
||||
mdp_dump_register(mdp, MDP_DISPLAY_STATUS);
|
||||
mdp_dump_register(mdp, MDP_DMA_P_IBUF_Y_STRIDE);
|
||||
mdp_dump_register(mdp, MDP_DMA_P_IBUF_ADDR);
|
||||
mdp_dump_register(mdp, MDP_LCDC_UNDERFLOW_CTL);
|
||||
mdp_dump_register(mdp, MDP_TFETCH_TEST_MODE);
|
||||
mdp_dump_register(mdp, MDP_TFETCH_STATUS);
|
||||
mdp_dump_register(mdp, MDP_TFETCH_TILE_COUNT);
|
||||
mdp_dump_register(mdp, MDP_TFETCH_FETCH_COUNT);
|
||||
@ -673,3 +667,235 @@ void mdp_ppp_dump_debug(const struct mdp_info *mdp)
|
||||
mdp_dump_register(mdp, MDP_INTR_STATUS);
|
||||
mdp_dump_register(mdp, MDP_INTR_ENABLE);
|
||||
}
|
||||
|
||||
|
||||
/* Splits a blit into two horizontal stripes. Used to work around MDP bugs */
|
||||
int mdp_ppp_blit_split_height(struct mdp_info *mdp, const struct mdp_blit_req *req,
|
||||
struct file *src_file, unsigned long src_start, unsigned long src_len,
|
||||
struct file *dst_file, unsigned long dst_start, unsigned long dst_len)
|
||||
{
|
||||
int ret;
|
||||
struct mdp_blit_req splitreq;
|
||||
int s_x_0, s_x_1, s_w_0, s_w_1, s_y_0, s_y_1, s_h_0, s_h_1;
|
||||
int d_x_0, d_x_1, d_w_0, d_w_1, d_y_0, d_y_1, d_h_0, d_h_1;
|
||||
|
||||
splitreq = *req;
|
||||
/* break dest roi at height*/
|
||||
d_x_0 = d_x_1 = req->dst_rect.x;
|
||||
d_w_0 = d_w_1 = req->dst_rect.w;
|
||||
d_y_0 = req->dst_rect.y;
|
||||
if (req->dst_rect.h % 32 == 3)
|
||||
d_h_1 = (req->dst_rect.h - 3) / 2 - 1;
|
||||
else
|
||||
d_h_1 = (req->dst_rect.h - 1) / 2 - 1;
|
||||
d_h_0 = req->dst_rect.h - d_h_1;
|
||||
d_y_1 = d_y_0 + d_h_0;
|
||||
if (req->dst_rect.h == 3) {
|
||||
d_h_1 = 2;
|
||||
d_h_0 = 2;
|
||||
d_y_1 = d_y_0 + 1;
|
||||
}
|
||||
/* break source roi */
|
||||
if (splitreq.flags & MDP_ROT_90) {
|
||||
s_y_0 = s_y_1 = req->src_rect.y;
|
||||
s_h_0 = s_h_1 = req->src_rect.h;
|
||||
s_x_0 = req->src_rect.x;
|
||||
s_w_1 = (req->src_rect.w * d_h_1) / req->dst_rect.h;
|
||||
s_w_0 = req->src_rect.w - s_w_1;
|
||||
s_x_1 = s_x_0 + s_w_0;
|
||||
if (d_h_1 >= 8 * s_w_1) {
|
||||
s_w_1++;
|
||||
s_x_1--;
|
||||
}
|
||||
} else {
|
||||
s_x_0 = s_x_1 = req->src_rect.x;
|
||||
s_w_0 = s_w_1 = req->src_rect.w;
|
||||
s_y_0 = req->src_rect.y;
|
||||
s_h_1 = (req->src_rect.h * d_h_1) / req->dst_rect.h;
|
||||
s_h_0 = req->src_rect.h - s_h_1;
|
||||
s_y_1 = s_y_0 + s_h_0;
|
||||
if (d_h_1 >= 8 * s_h_1) {
|
||||
s_h_1++;
|
||||
s_y_1--;
|
||||
}
|
||||
}
|
||||
|
||||
/* blit first region */
|
||||
if (((splitreq.flags & MDP_ROT_MASK) == MDP_ROT_90) ||
|
||||
((splitreq.flags & MDP_ROT_MASK) == 0x0)) {
|
||||
splitreq.src_rect.h = s_h_0;
|
||||
splitreq.src_rect.y = s_y_0;
|
||||
splitreq.dst_rect.h = d_h_0;
|
||||
splitreq.dst_rect.y = d_y_0;
|
||||
splitreq.src_rect.x = s_x_0;
|
||||
splitreq.src_rect.w = s_w_0;
|
||||
splitreq.dst_rect.x = d_x_0;
|
||||
splitreq.dst_rect.w = d_w_0;
|
||||
} else {
|
||||
splitreq.src_rect.h = s_h_0;
|
||||
splitreq.src_rect.y = s_y_0;
|
||||
splitreq.dst_rect.h = d_h_1;
|
||||
splitreq.dst_rect.y = d_y_1;
|
||||
splitreq.src_rect.x = s_x_0;
|
||||
splitreq.src_rect.w = s_w_0;
|
||||
splitreq.dst_rect.x = d_x_1;
|
||||
splitreq.dst_rect.w = d_w_1;
|
||||
}
|
||||
ret = mdp_blit_and_wait(mdp, &splitreq,
|
||||
src_file, src_start, src_len,
|
||||
dst_file, dst_start, dst_len);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* blit second region */
|
||||
if (((splitreq.flags & MDP_ROT_MASK) == MDP_ROT_90) ||
|
||||
((splitreq.flags & MDP_ROT_MASK) == 0x0)) {
|
||||
splitreq.src_rect.h = s_h_1;
|
||||
splitreq.src_rect.y = s_y_1;
|
||||
splitreq.dst_rect.h = d_h_1;
|
||||
splitreq.dst_rect.y = d_y_1;
|
||||
splitreq.src_rect.x = s_x_1;
|
||||
splitreq.src_rect.w = s_w_1;
|
||||
splitreq.dst_rect.x = d_x_1;
|
||||
splitreq.dst_rect.w = d_w_1;
|
||||
} else {
|
||||
splitreq.src_rect.h = s_h_1;
|
||||
splitreq.src_rect.y = s_y_1;
|
||||
splitreq.dst_rect.h = d_h_0;
|
||||
splitreq.dst_rect.y = d_y_0;
|
||||
splitreq.src_rect.x = s_x_1;
|
||||
splitreq.src_rect.w = s_w_1;
|
||||
splitreq.dst_rect.x = d_x_0;
|
||||
splitreq.dst_rect.w = d_w_0;
|
||||
}
|
||||
ret = mdp_blit_and_wait(mdp, &splitreq,
|
||||
src_file, src_start, src_len,
|
||||
dst_file, dst_start, dst_len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Splits a blit into two vertical stripes. Used to work around MDP bugs */
|
||||
int mdp_ppp_blit_split_width(struct mdp_info *mdp, const struct mdp_blit_req *req,
|
||||
struct file *src_file, unsigned long src_start, unsigned long src_len,
|
||||
struct file *dst_file, unsigned long dst_start, unsigned long dst_len)
|
||||
{
|
||||
int ret;
|
||||
struct mdp_blit_req splitreq;
|
||||
int s_x_0, s_x_1, s_w_0, s_w_1, s_y_0, s_y_1, s_h_0, s_h_1;
|
||||
int d_x_0, d_x_1, d_w_0, d_w_1, d_y_0, d_y_1, d_h_0, d_h_1;
|
||||
splitreq = *req;
|
||||
|
||||
/* break dest roi at width*/
|
||||
d_y_0 = d_y_1 = req->dst_rect.y;
|
||||
d_h_0 = d_h_1 = req->dst_rect.h;
|
||||
d_x_0 = req->dst_rect.x;
|
||||
if (req->dst_rect.w % 32 == 6)
|
||||
d_w_1 = req->dst_rect.w / 2 - 1;
|
||||
else if (req->dst_rect.w % 2 == 0)
|
||||
d_w_1 = req->dst_rect.w / 2;
|
||||
else if (req->dst_rect.w % 32 == 3)
|
||||
d_w_1 = (req->dst_rect.w - 3) / 2 - 1;
|
||||
else
|
||||
d_w_1 = (req->dst_rect.w - 1) / 2 - 1;
|
||||
d_w_0 = req->dst_rect.w - d_w_1;
|
||||
d_x_1 = d_x_0 + d_w_0;
|
||||
if (req->dst_rect.w == 3) {
|
||||
d_w_1 = 2;
|
||||
d_w_0 = 2;
|
||||
d_x_1 = d_x_0 + 1;
|
||||
}
|
||||
|
||||
/* break src roi at height or width*/
|
||||
if (splitreq.flags & MDP_ROT_90) {
|
||||
s_x_0 = s_x_1 = req->src_rect.x;
|
||||
s_w_0 = s_w_1 = req->src_rect.w;
|
||||
s_y_0 = req->src_rect.y;
|
||||
s_h_1 = (req->src_rect.h * d_w_1) / req->dst_rect.w;
|
||||
s_h_0 = req->src_rect.h - s_h_1;
|
||||
s_y_1 = s_y_0 + s_h_0;
|
||||
if (d_w_1 >= 8 * s_h_1) {
|
||||
s_h_1++;
|
||||
s_y_1--;
|
||||
}
|
||||
} else {
|
||||
s_y_0 = s_y_1 = req->src_rect.y;
|
||||
s_h_0 = s_h_1 = req->src_rect.h;
|
||||
s_x_0 = req->src_rect.x;
|
||||
s_w_1 = (req->src_rect.w * d_w_1) / req->dst_rect.w;
|
||||
s_w_0 = req->src_rect.w - s_w_1;
|
||||
s_x_1 = s_x_0 + s_w_0;
|
||||
if (d_w_1 >= 8 * s_w_1) {
|
||||
s_w_1++;
|
||||
s_x_1--;
|
||||
}
|
||||
}
|
||||
|
||||
/* blit first region */
|
||||
if (((splitreq.flags & MDP_ROT_MASK) == MDP_ROT_270) ||
|
||||
((splitreq.flags & MDP_ROT_MASK) == 0x0)) {
|
||||
splitreq.src_rect.h = s_h_0;
|
||||
splitreq.src_rect.y = s_y_0;
|
||||
splitreq.dst_rect.h = d_h_0;
|
||||
splitreq.dst_rect.y = d_y_0;
|
||||
splitreq.src_rect.x = s_x_0;
|
||||
splitreq.src_rect.w = s_w_0;
|
||||
splitreq.dst_rect.x = d_x_0;
|
||||
splitreq.dst_rect.w = d_w_0;
|
||||
} else {
|
||||
splitreq.src_rect.h = s_h_0;
|
||||
splitreq.src_rect.y = s_y_0;
|
||||
splitreq.dst_rect.h = d_h_1;
|
||||
splitreq.dst_rect.y = d_y_1;
|
||||
splitreq.src_rect.x = s_x_0;
|
||||
splitreq.src_rect.w = s_w_0;
|
||||
splitreq.dst_rect.x = d_x_1;
|
||||
splitreq.dst_rect.w = d_w_1;
|
||||
}
|
||||
|
||||
if (unlikely((splitreq.dst_rect.h != 1) &&
|
||||
((splitreq.dst_rect.h % 32 == 3) ||
|
||||
(splitreq.dst_rect.h % 32) == 1)))
|
||||
ret = mdp_ppp_blit_split_height(mdp, &splitreq,
|
||||
src_file, src_start, src_len,
|
||||
dst_file, dst_start, dst_len);
|
||||
else
|
||||
ret = mdp_blit_and_wait(mdp, &splitreq,
|
||||
src_file, src_start, src_len,
|
||||
dst_file, dst_start, dst_len);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* blit second region */
|
||||
if (((splitreq.flags & MDP_ROT_MASK) == MDP_ROT_270) ||
|
||||
((splitreq.flags & MDP_ROT_MASK) == 0x0)) {
|
||||
splitreq.src_rect.h = s_h_1;
|
||||
splitreq.src_rect.y = s_y_1;
|
||||
splitreq.dst_rect.h = d_h_1;
|
||||
splitreq.dst_rect.y = d_y_1;
|
||||
splitreq.src_rect.x = s_x_1;
|
||||
splitreq.src_rect.w = s_w_1;
|
||||
splitreq.dst_rect.x = d_x_1;
|
||||
splitreq.dst_rect.w = d_w_1;
|
||||
} else {
|
||||
splitreq.src_rect.h = s_h_1;
|
||||
splitreq.src_rect.y = s_y_1;
|
||||
splitreq.dst_rect.h = d_h_0;
|
||||
splitreq.dst_rect.y = d_y_0;
|
||||
splitreq.src_rect.x = s_x_1;
|
||||
splitreq.src_rect.w = s_w_1;
|
||||
splitreq.dst_rect.x = d_x_0;
|
||||
splitreq.dst_rect.w = d_w_0;
|
||||
}
|
||||
|
||||
if (unlikely((splitreq.dst_rect.h != 1) &&
|
||||
((splitreq.dst_rect.h % 32 == 3) ||
|
||||
(splitreq.dst_rect.h % 32) == 1)))
|
||||
ret = mdp_ppp_blit_split_height(mdp, &splitreq,
|
||||
src_file, src_start, src_len,
|
||||
dst_file, dst_start, dst_len);
|
||||
else
|
||||
ret = mdp_blit_and_wait(mdp, &splitreq,
|
||||
src_file, src_start, src_len,
|
||||
dst_file, dst_start, dst_len);
|
||||
return ret;
|
||||
}
|
@ -16,6 +16,7 @@
|
||||
#define _VIDEO_MSM_MDP_PPP_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
#define PPP_DUMP_BLITS 0
|
||||
|
||||
struct ppp_regs {
|
||||
uint32_t src0;
|
||||
@ -68,11 +69,10 @@ int mdp_ppp_cfg_scale(const struct mdp_info *mdp, struct ppp_regs *regs,
|
||||
struct mdp_rect *src_rect, struct mdp_rect *dst_rect,
|
||||
uint32_t src_format, uint32_t dst_format);
|
||||
int mdp_ppp_load_blur(const struct mdp_info *mdp);
|
||||
void mdp_dump_blit(struct mdp_blit_req *req);
|
||||
|
||||
#ifndef CONFIG_MSM_MDP31
|
||||
int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req, struct ppp_regs *regs);
|
||||
#else
|
||||
|
||||
#if defined(CONFIG_MSM_MDP31) || defined(CONFIG_MSM_MDP302)
|
||||
int mdp_ppp_blit_split_width(struct mdp_info *mdp, const struct mdp_blit_req *req,
|
||||
struct file *src_file, unsigned long src_start, unsigned long src_len,
|
||||
struct file *dst_file, unsigned long dst_start, unsigned long dst_len);
|
||||
@ -80,6 +80,9 @@ int mdp_ppp_blit_split_height(struct mdp_info *mdp, const struct mdp_blit_req *r
|
||||
struct file *src_file, unsigned long src_start, unsigned long src_len,
|
||||
struct file *dst_file, unsigned long dst_start, unsigned long dst_len);
|
||||
|
||||
#if defined(CONFIG_MSM_MDP302)
|
||||
int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req, struct ppp_regs *regs);
|
||||
#else
|
||||
static inline int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req,
|
||||
struct ppp_regs *regs)
|
||||
{
|
||||
@ -87,6 +90,10 @@ static inline int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req,
|
||||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req, struct ppp_regs *regs);
|
||||
#endif
|
||||
|
||||
int mdp_get_bytes_per_pixel(int format);
|
||||
int mdp_blit_and_wait(struct mdp_info *mdp, struct mdp_blit_req *req,
|
||||
struct file *src_file, unsigned long src_start, unsigned long src_len,
|
||||
|
@ -331,233 +331,4 @@ void mdp_ppp_init_scale(const struct mdp_info *mdp)
|
||||
load_table(mdp, scale, 0);
|
||||
}
|
||||
|
||||
/* Splits a blit into two horizontal stripes. Used to work around MDP bugs */
|
||||
int mdp_ppp_blit_split_height(struct mdp_info *mdp, const struct mdp_blit_req *req,
|
||||
struct file *src_file, unsigned long src_start, unsigned long src_len,
|
||||
struct file *dst_file, unsigned long dst_start, unsigned long dst_len)
|
||||
{
|
||||
int ret;
|
||||
struct mdp_blit_req splitreq;
|
||||
int s_x_0, s_x_1, s_w_0, s_w_1, s_y_0, s_y_1, s_h_0, s_h_1;
|
||||
int d_x_0, d_x_1, d_w_0, d_w_1, d_y_0, d_y_1, d_h_0, d_h_1;
|
||||
|
||||
splitreq = *req;
|
||||
/* break dest roi at height*/
|
||||
d_x_0 = d_x_1 = req->dst_rect.x;
|
||||
d_w_0 = d_w_1 = req->dst_rect.w;
|
||||
d_y_0 = req->dst_rect.y;
|
||||
if (req->dst_rect.h % 32 == 3)
|
||||
d_h_1 = (req->dst_rect.h - 3) / 2 - 1;
|
||||
else
|
||||
d_h_1 = (req->dst_rect.h - 1) / 2 - 1;
|
||||
d_h_0 = req->dst_rect.h - d_h_1;
|
||||
d_y_1 = d_y_0 + d_h_0;
|
||||
if (req->dst_rect.h == 3) {
|
||||
d_h_1 = 2;
|
||||
d_h_0 = 2;
|
||||
d_y_1 = d_y_0 + 1;
|
||||
}
|
||||
/* break source roi */
|
||||
if (splitreq.flags & MDP_ROT_90) {
|
||||
s_y_0 = s_y_1 = req->src_rect.y;
|
||||
s_h_0 = s_h_1 = req->src_rect.h;
|
||||
s_x_0 = req->src_rect.x;
|
||||
s_w_1 = (req->src_rect.w * d_h_1) / req->dst_rect.h;
|
||||
s_w_0 = req->src_rect.w - s_w_1;
|
||||
s_x_1 = s_x_0 + s_w_0;
|
||||
if (d_h_1 >= 8 * s_w_1) {
|
||||
s_w_1++;
|
||||
s_x_1--;
|
||||
}
|
||||
} else {
|
||||
s_x_0 = s_x_1 = req->src_rect.x;
|
||||
s_w_0 = s_w_1 = req->src_rect.w;
|
||||
s_y_0 = req->src_rect.y;
|
||||
s_h_1 = (req->src_rect.h * d_h_1) / req->dst_rect.h;
|
||||
s_h_0 = req->src_rect.h - s_h_1;
|
||||
s_y_1 = s_y_0 + s_h_0;
|
||||
if (d_h_1 >= 8 * s_h_1) {
|
||||
s_h_1++;
|
||||
s_y_1--;
|
||||
}
|
||||
}
|
||||
|
||||
/* blit first region */
|
||||
if (((splitreq.flags & MDP_ROT_MASK) == MDP_ROT_90) ||
|
||||
((splitreq.flags & MDP_ROT_MASK) == 0x0)) {
|
||||
splitreq.src_rect.h = s_h_0;
|
||||
splitreq.src_rect.y = s_y_0;
|
||||
splitreq.dst_rect.h = d_h_0;
|
||||
splitreq.dst_rect.y = d_y_0;
|
||||
splitreq.src_rect.x = s_x_0;
|
||||
splitreq.src_rect.w = s_w_0;
|
||||
splitreq.dst_rect.x = d_x_0;
|
||||
splitreq.dst_rect.w = d_w_0;
|
||||
} else {
|
||||
splitreq.src_rect.h = s_h_0;
|
||||
splitreq.src_rect.y = s_y_0;
|
||||
splitreq.dst_rect.h = d_h_1;
|
||||
splitreq.dst_rect.y = d_y_1;
|
||||
splitreq.src_rect.x = s_x_0;
|
||||
splitreq.src_rect.w = s_w_0;
|
||||
splitreq.dst_rect.x = d_x_1;
|
||||
splitreq.dst_rect.w = d_w_1;
|
||||
}
|
||||
ret = mdp_blit_and_wait(mdp, &splitreq,
|
||||
src_file, src_start, src_len,
|
||||
dst_file, dst_start, dst_len);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* blit second region */
|
||||
if (((splitreq.flags & MDP_ROT_MASK) == MDP_ROT_90) ||
|
||||
((splitreq.flags & MDP_ROT_MASK) == 0x0)) {
|
||||
splitreq.src_rect.h = s_h_1;
|
||||
splitreq.src_rect.y = s_y_1;
|
||||
splitreq.dst_rect.h = d_h_1;
|
||||
splitreq.dst_rect.y = d_y_1;
|
||||
splitreq.src_rect.x = s_x_1;
|
||||
splitreq.src_rect.w = s_w_1;
|
||||
splitreq.dst_rect.x = d_x_1;
|
||||
splitreq.dst_rect.w = d_w_1;
|
||||
} else {
|
||||
splitreq.src_rect.h = s_h_1;
|
||||
splitreq.src_rect.y = s_y_1;
|
||||
splitreq.dst_rect.h = d_h_0;
|
||||
splitreq.dst_rect.y = d_y_0;
|
||||
splitreq.src_rect.x = s_x_1;
|
||||
splitreq.src_rect.w = s_w_1;
|
||||
splitreq.dst_rect.x = d_x_0;
|
||||
splitreq.dst_rect.w = d_w_0;
|
||||
}
|
||||
ret = mdp_blit_and_wait(mdp, &splitreq,
|
||||
src_file, src_start, src_len,
|
||||
dst_file, dst_start, dst_len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Splits a blit into two vertical stripes. Used to work around MDP bugs */
|
||||
int mdp_ppp_blit_split_width(struct mdp_info *mdp, const struct mdp_blit_req *req,
|
||||
struct file *src_file, unsigned long src_start, unsigned long src_len,
|
||||
struct file *dst_file, unsigned long dst_start, unsigned long dst_len)
|
||||
{
|
||||
int ret;
|
||||
struct mdp_blit_req splitreq;
|
||||
int s_x_0, s_x_1, s_w_0, s_w_1, s_y_0, s_y_1, s_h_0, s_h_1;
|
||||
int d_x_0, d_x_1, d_w_0, d_w_1, d_y_0, d_y_1, d_h_0, d_h_1;
|
||||
splitreq = *req;
|
||||
|
||||
/* break dest roi at width*/
|
||||
d_y_0 = d_y_1 = req->dst_rect.y;
|
||||
d_h_0 = d_h_1 = req->dst_rect.h;
|
||||
d_x_0 = req->dst_rect.x;
|
||||
if (req->dst_rect.w % 32 == 6)
|
||||
d_w_1 = req->dst_rect.w / 2 - 1;
|
||||
else if (req->dst_rect.w % 2 == 0)
|
||||
d_w_1 = req->dst_rect.w / 2;
|
||||
else if (req->dst_rect.w % 32 == 3)
|
||||
d_w_1 = (req->dst_rect.w - 3) / 2 - 1;
|
||||
else
|
||||
d_w_1 = (req->dst_rect.w - 1) / 2 - 1;
|
||||
d_w_0 = req->dst_rect.w - d_w_1;
|
||||
d_x_1 = d_x_0 + d_w_0;
|
||||
if (req->dst_rect.w == 3) {
|
||||
d_w_1 = 2;
|
||||
d_w_0 = 2;
|
||||
d_x_1 = d_x_0 + 1;
|
||||
}
|
||||
|
||||
/* break src roi at height or width*/
|
||||
if (splitreq.flags & MDP_ROT_90) {
|
||||
s_x_0 = s_x_1 = req->src_rect.x;
|
||||
s_w_0 = s_w_1 = req->src_rect.w;
|
||||
s_y_0 = req->src_rect.y;
|
||||
s_h_1 = (req->src_rect.h * d_w_1) / req->dst_rect.w;
|
||||
s_h_0 = req->src_rect.h - s_h_1;
|
||||
s_y_1 = s_y_0 + s_h_0;
|
||||
if (d_w_1 >= 8 * s_h_1) {
|
||||
s_h_1++;
|
||||
s_y_1--;
|
||||
}
|
||||
} else {
|
||||
s_y_0 = s_y_1 = req->src_rect.y;
|
||||
s_h_0 = s_h_1 = req->src_rect.h;
|
||||
s_x_0 = req->src_rect.x;
|
||||
s_w_1 = (req->src_rect.w * d_w_1) / req->dst_rect.w;
|
||||
s_w_0 = req->src_rect.w - s_w_1;
|
||||
s_x_1 = s_x_0 + s_w_0;
|
||||
if (d_w_1 >= 8 * s_w_1) {
|
||||
s_w_1++;
|
||||
s_x_1--;
|
||||
}
|
||||
}
|
||||
|
||||
/* blit first region */
|
||||
if (((splitreq.flags & MDP_ROT_MASK) == MDP_ROT_270) ||
|
||||
((splitreq.flags & MDP_ROT_MASK) == 0x0)) {
|
||||
splitreq.src_rect.h = s_h_0;
|
||||
splitreq.src_rect.y = s_y_0;
|
||||
splitreq.dst_rect.h = d_h_0;
|
||||
splitreq.dst_rect.y = d_y_0;
|
||||
splitreq.src_rect.x = s_x_0;
|
||||
splitreq.src_rect.w = s_w_0;
|
||||
splitreq.dst_rect.x = d_x_0;
|
||||
splitreq.dst_rect.w = d_w_0;
|
||||
} else {
|
||||
splitreq.src_rect.h = s_h_0;
|
||||
splitreq.src_rect.y = s_y_0;
|
||||
splitreq.dst_rect.h = d_h_1;
|
||||
splitreq.dst_rect.y = d_y_1;
|
||||
splitreq.src_rect.x = s_x_0;
|
||||
splitreq.src_rect.w = s_w_0;
|
||||
splitreq.dst_rect.x = d_x_1;
|
||||
splitreq.dst_rect.w = d_w_1;
|
||||
}
|
||||
|
||||
if (unlikely((splitreq.dst_rect.h != 1) &&
|
||||
((splitreq.dst_rect.h % 32 == 3) ||
|
||||
(splitreq.dst_rect.h % 32) == 1)))
|
||||
ret = mdp_ppp_blit_split_height(mdp, &splitreq,
|
||||
src_file, src_start, src_len,
|
||||
dst_file, dst_start, dst_len);
|
||||
else
|
||||
ret = mdp_blit_and_wait(mdp, &splitreq,
|
||||
src_file, src_start, src_len,
|
||||
dst_file, dst_start, dst_len);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* blit second region */
|
||||
if (((splitreq.flags & MDP_ROT_MASK) == MDP_ROT_270) ||
|
||||
((splitreq.flags & MDP_ROT_MASK) == 0x0)) {
|
||||
splitreq.src_rect.h = s_h_1;
|
||||
splitreq.src_rect.y = s_y_1;
|
||||
splitreq.dst_rect.h = d_h_1;
|
||||
splitreq.dst_rect.y = d_y_1;
|
||||
splitreq.src_rect.x = s_x_1;
|
||||
splitreq.src_rect.w = s_w_1;
|
||||
splitreq.dst_rect.x = d_x_1;
|
||||
splitreq.dst_rect.w = d_w_1;
|
||||
} else {
|
||||
splitreq.src_rect.h = s_h_1;
|
||||
splitreq.src_rect.y = s_y_1;
|
||||
splitreq.dst_rect.h = d_h_0;
|
||||
splitreq.dst_rect.y = d_y_0;
|
||||
splitreq.src_rect.x = s_x_1;
|
||||
splitreq.src_rect.w = s_w_1;
|
||||
splitreq.dst_rect.x = d_x_0;
|
||||
splitreq.dst_rect.w = d_w_0;
|
||||
}
|
||||
|
||||
if (unlikely((splitreq.dst_rect.h != 1) &&
|
||||
((splitreq.dst_rect.h % 32 == 3) ||
|
||||
(splitreq.dst_rect.h % 32) == 1)))
|
||||
ret = mdp_ppp_blit_split_height(mdp, &splitreq,
|
||||
src_file, src_start, src_len,
|
||||
dst_file, dst_start, dst_len);
|
||||
else
|
||||
ret = mdp_blit_and_wait(mdp, &splitreq,
|
||||
src_file, src_start, src_len,
|
||||
dst_file, dst_start, dst_len);
|
||||
return ret;
|
||||
}
|
||||
|
@ -32,6 +32,11 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/android_pmem.h>
|
||||
|
||||
extern void start_drawing_late_resume(struct early_suspend *h);
|
||||
static void msmfb_resume_handler(struct early_suspend *h);
|
||||
static void msmfb_resume(struct work_struct *work);
|
||||
|
||||
#define MSMFB_DEBUG 1
|
||||
#ifdef CONFIG_FB_MSM_LOGO
|
||||
@ -56,13 +61,12 @@ extern int load_565rle_image(char *filename);
|
||||
|
||||
#define DLOG(mask, fmt, args...) \
|
||||
do { \
|
||||
if (msmfb_debug_mask & mask) \
|
||||
if ((msmfb_debug_mask | SUSPEND_RESUME) & mask) \
|
||||
printk(KERN_INFO "msmfb: "fmt, ##args); \
|
||||
} while (0)
|
||||
|
||||
#define BITS_PER_PIXEL(info) (info->fb->var.bits_per_pixel)
|
||||
#define BYTES_PER_PIXEL(info) (info->fb->var.bits_per_pixel >> 3)
|
||||
|
||||
static int msmfb_debug_mask;
|
||||
module_param_named(msmfb_debug_mask, msmfb_debug_mask, int,
|
||||
S_IRUGO | S_IWUSR | S_IWGRP);
|
||||
@ -96,12 +100,59 @@ struct msmfb_info {
|
||||
wait_queue_head_t frame_wq;
|
||||
struct workqueue_struct *resume_workqueue;
|
||||
struct work_struct resume_work;
|
||||
struct work_struct msmfb_resume_work;
|
||||
struct msmfb_callback dma_callback;
|
||||
struct msmfb_callback vsync_callback;
|
||||
struct hrtimer fake_vsync;
|
||||
ktime_t vsync_request_time;
|
||||
unsigned fb_resumed;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_FB_MSM_OVERLAY
|
||||
#define USE_OVERLAY 1
|
||||
struct overlay_waitevent{
|
||||
uint32_t waked_up;
|
||||
wait_queue_head_t event_wait;
|
||||
};
|
||||
static struct overlay_waitevent overlay_event;
|
||||
DEFINE_MUTEX(overlay_event_lock);
|
||||
#endif
|
||||
|
||||
#if (defined(CONFIG_USB_FUNCTION_PROJECTOR) || defined(CONFIG_USB_ANDROID_PROJECTOR))
|
||||
static spinlock_t fb_data_lock = SPIN_LOCK_UNLOCKED;
|
||||
static struct msm_fb_info msm_fb_data;
|
||||
int msmfb_get_var(struct msm_fb_info *tmp)
|
||||
{
|
||||
unsigned long flags;
|
||||
spin_lock_irqsave(&fb_data_lock, flags);
|
||||
memcpy(tmp, &msm_fb_data, sizeof(msm_fb_data));
|
||||
spin_unlock_irqrestore(&fb_data_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* projector need this, and very much */
|
||||
int msmfb_get_fb_area(void)
|
||||
{
|
||||
int area;
|
||||
unsigned long flags;
|
||||
spin_lock_irqsave(&fb_data_lock, flags);
|
||||
area = msm_fb_data.msmfb_area;
|
||||
spin_unlock_irqrestore(&fb_data_lock, flags);
|
||||
return area;
|
||||
}
|
||||
|
||||
static void msmfb_set_var(unsigned char *addr, int area)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&fb_data_lock, flags);
|
||||
msm_fb_data.fb_addr = addr;
|
||||
msm_fb_data.msmfb_area = area;
|
||||
spin_unlock_irqrestore(&fb_data_lock, flags);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
static int msmfb_open(struct fb_info *info, int user)
|
||||
{
|
||||
return 0;
|
||||
@ -248,6 +299,18 @@ static void msmfb_pan_update(struct fb_info *info, uint32_t left, uint32_t top,
|
||||
|
||||
DLOG(SHOW_UPDATES, "update %d %d %d %d %d %d\n",
|
||||
left, top, eright, ebottom, yoffset, pan_display);
|
||||
|
||||
#if !defined(CONFIG_MACH_HTCLEO)
|
||||
// For some reason we need to remove it here, state is 1, we have to look later to this problem
|
||||
if (msmfb->sleeping != AWAKE)
|
||||
DLOG(SUSPEND_RESUME, "pan_update in state(%d)\n", msmfb->sleeping);
|
||||
#endif
|
||||
|
||||
#if (defined(CONFIG_USB_FUNCTION_PROJECTOR) || defined(CONFIG_USB_ANDROID_PROJECTOR))
|
||||
/* Jay, 8/1/09' */
|
||||
msmfb_set_var(msmfb->fb->screen_base, yoffset);
|
||||
#endif
|
||||
|
||||
restart:
|
||||
spin_lock_irqsave(&msmfb->update_lock, irq_flags);
|
||||
|
||||
@ -264,8 +327,8 @@ restart:
|
||||
|
||||
sleeping = msmfb->sleeping;
|
||||
/* on a full update, if the last frame has not completed, wait for it */
|
||||
if ((pan_display && msmfb->frame_requested != msmfb->frame_done) ||
|
||||
sleeping == UPDATING) {
|
||||
if (pan_display && (msmfb->frame_requested != msmfb->frame_done ||
|
||||
sleeping == UPDATING)) {
|
||||
int ret;
|
||||
spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
|
||||
ret = wait_event_interruptible_timeout(msmfb->frame_wq,
|
||||
@ -365,7 +428,6 @@ static void power_on_panel(struct work_struct *work)
|
||||
container_of(work, struct msmfb_info, resume_work);
|
||||
struct msm_panel_data *panel = msmfb->panel;
|
||||
unsigned long irq_flags;
|
||||
|
||||
mutex_lock(&msmfb->panel_init_lock);
|
||||
DLOG(SUSPEND_RESUME, "turning on panel\n");
|
||||
if (msmfb->sleeping == UPDATING) {
|
||||
@ -385,6 +447,34 @@ error:
|
||||
mutex_unlock(&msmfb->panel_init_lock);
|
||||
}
|
||||
|
||||
static BLOCKING_NOTIFIER_HEAD(display_chain_head);
|
||||
int register_display_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return blocking_notifier_chain_register(&display_chain_head, nb);
|
||||
}
|
||||
static int display_notifier_callback(struct notifier_block *nfb,
|
||||
unsigned long action,
|
||||
void *ignored)
|
||||
{
|
||||
struct msmfb_info *msm_fb = (struct msmfb_info *)ignored;
|
||||
|
||||
switch (action) {
|
||||
case NOTIFY_MSM_FB:
|
||||
printk(KERN_DEBUG "NOTIFY_MSM_FB\n");
|
||||
msmfb_resume(&msm_fb->early_suspend);
|
||||
break;
|
||||
case NOTIFY_POWER:
|
||||
/* nothing to do */
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "%s: unknown action in 0x%lx\n",
|
||||
__func__, action);
|
||||
return NOTIFY_BAD;
|
||||
}
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
#ifdef CONFIG_HAS_EARLYSUSPEND
|
||||
/* turn off the panel */
|
||||
static void msmfb_earlier_suspend(struct early_suspend *h)
|
||||
@ -401,12 +491,12 @@ static void msmfb_earlier_suspend(struct early_suspend *h)
|
||||
spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
|
||||
wait_event_timeout(msmfb->frame_wq,
|
||||
msmfb->frame_requested == msmfb->frame_done, HZ/10);
|
||||
|
||||
#ifndef CONFIG_MSM_MDP40
|
||||
mdp->dma(mdp, virt_to_phys(msmfb->black), 0,
|
||||
msmfb->fb->var.xres, msmfb->fb->var.yres, 0, 0,
|
||||
NULL, panel->interface_type);
|
||||
mdp->dma_wait(mdp, panel->interface_type);
|
||||
|
||||
#endif
|
||||
/* turn off the panel */
|
||||
panel->blank(panel);
|
||||
}
|
||||
@ -417,14 +507,31 @@ static void msmfb_suspend(struct early_suspend *h)
|
||||
early_suspend);
|
||||
struct msm_panel_data *panel = msmfb->panel;
|
||||
/* suspend the panel */
|
||||
#ifdef CONFIG_FB_MSM_OVERLAY
|
||||
/*check whether overlay done*/
|
||||
wait_event_interruptible_timeout(
|
||||
overlay_event.event_wait,
|
||||
(overlay_event.waked_up == ~USE_OVERLAY),
|
||||
10*HZ);
|
||||
pr_info("wait event : %X\n", overlay_event.waked_up);
|
||||
#endif
|
||||
panel->suspend(panel);
|
||||
msmfb->fb_resumed = 0;
|
||||
mutex_unlock(&msmfb->panel_init_lock);
|
||||
}
|
||||
|
||||
static void msmfb_resume(struct early_suspend *h)
|
||||
static void msmfb_resume_handler(struct early_suspend *h)
|
||||
{
|
||||
struct msmfb_info *msmfb = container_of(h, struct msmfb_info,
|
||||
early_suspend);
|
||||
early_suspend);
|
||||
queue_work(msmfb->resume_workqueue, &msmfb->msmfb_resume_work);
|
||||
wait_event_interruptible_timeout(msmfb->frame_wq, msmfb->fb_resumed==1,HZ/2);
|
||||
}
|
||||
|
||||
static void msmfb_resume(struct work_struct *work)
|
||||
{
|
||||
struct msmfb_info *msmfb =
|
||||
container_of(work, struct msmfb_info, msmfb_resume_work);
|
||||
struct msm_panel_data *panel = msmfb->panel;
|
||||
unsigned long irq_flags;
|
||||
|
||||
@ -438,6 +545,9 @@ static void msmfb_resume(struct early_suspend *h)
|
||||
msmfb->sleeping = WAKING;
|
||||
DLOG(SUSPEND_RESUME, "ready, waiting for full update\n");
|
||||
spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
|
||||
start_drawing_late_resume(NULL);
|
||||
msmfb->fb_resumed = 1;
|
||||
wake_up(&msmfb->frame_wq);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -483,7 +593,6 @@ static int msmfb_set_par(struct fb_info *info)
|
||||
return -1;
|
||||
mdp->set_output_format(mdp, var->bits_per_pixel);
|
||||
fix->line_length = var->xres * var->bits_per_pixel / 8;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -502,16 +611,13 @@ int msmfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
|
||||
var->reserved[1] >> 16, var->reserved[2] & 0xffff,
|
||||
var->reserved[2] >> 16);
|
||||
#endif
|
||||
// printk("+PAN_DISP1\n");
|
||||
msmfb_pan_update(info, var->reserved[1] & 0xffff,
|
||||
var->reserved[1] >> 16,
|
||||
var->reserved[2] & 0xffff,
|
||||
var->reserved[2] >> 16, var->yoffset, 1);
|
||||
// printk("-PAN_DISP1\n");
|
||||
} else {
|
||||
// printk("+PAN_DISP2\n");
|
||||
msmfb_pan_update(info, 0, 0, info->var.xres, info->var.yres, var->yoffset, 1);
|
||||
// printk("-PAN_DISP2\n");
|
||||
msmfb_pan_update(info, 0, 0, info->var.xres, info->var.yres,
|
||||
var->yoffset, 1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -566,6 +672,91 @@ static int msmfb_blit(struct fb_info *info,
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#ifdef CONFIG_FB_MSM_OVERLAY
|
||||
static int msmfb_overlay_get(struct fb_info *info, void __user *p)
|
||||
{
|
||||
struct mdp_overlay req;
|
||||
int ret;
|
||||
|
||||
if (copy_from_user(&req, p, sizeof(req)))
|
||||
return -EFAULT;
|
||||
|
||||
ret = mdp->overlay_get(mdp, info, &req);
|
||||
|
||||
if (ret) {
|
||||
printk(KERN_ERR "%s: ioctl failed \n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
if (copy_to_user(p, &req, sizeof(req))) {
|
||||
printk(KERN_ERR "%s: copy2user failed \n",
|
||||
__func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msmfb_overlay_set(struct fb_info *info, void __user *p)
|
||||
{
|
||||
struct mdp_overlay req;
|
||||
int ret;
|
||||
|
||||
if (copy_from_user(&req, p, sizeof(req)))
|
||||
return -EFAULT;
|
||||
|
||||
ret = mdp->overlay_set(mdp, info, &req);
|
||||
if (ret) {
|
||||
printk(KERN_ERR "%s:ioctl failed \n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (copy_to_user(p, &req, sizeof(req))) {
|
||||
printk(KERN_ERR "%s: copy2user failed \n",
|
||||
__func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msmfb_overlay_unset(struct fb_info *info, unsigned long *argp)
|
||||
{
|
||||
int ret, ndx;
|
||||
|
||||
ret = copy_from_user(&ndx, argp, sizeof(ndx));
|
||||
if (ret) {
|
||||
printk(KERN_ERR "%s:msmfb_overlay_unset ioctl failed \n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return mdp->overlay_unset(mdp, info, ndx);
|
||||
}
|
||||
|
||||
static int msmfb_overlay_play(struct fb_info *info, unsigned long *argp)
|
||||
{
|
||||
int ret;
|
||||
struct msmfb_overlay_data req;
|
||||
struct file *p_src_file = 0;
|
||||
|
||||
ret = copy_from_user(&req, argp, sizeof(req));
|
||||
if (ret) {
|
||||
printk(KERN_ERR "%s:msmfb_overlay_play ioctl failed \n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mdp->overlay_play(mdp, info, &req, &p_src_file);
|
||||
|
||||
if (p_src_file)
|
||||
put_pmem_file(p_src_file);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
DEFINE_MUTEX(mdp_ppp_lock);
|
||||
@ -580,14 +771,13 @@ static int msmfb_ioctl(struct fb_info *p, unsigned int cmd, unsigned long arg)
|
||||
|
||||
switch (cmd) {
|
||||
case MSMFB_GRP_DISP:
|
||||
printk("GRP_DISP\n");
|
||||
mdp->set_grp_disp(mdp, arg);
|
||||
break;
|
||||
case MSMFB_BLIT:
|
||||
#if PRINT_BLIT_TIME
|
||||
t1 = ktime_get();
|
||||
#endif
|
||||
ret = msmfb_blit(p, argp);
|
||||
ret = msmfb_blit(p, argp);
|
||||
if (ret)
|
||||
return ret;
|
||||
#if PRINT_BLIT_TIME
|
||||
@ -596,6 +786,38 @@ static int msmfb_ioctl(struct fb_info *p, unsigned int cmd, unsigned long arg)
|
||||
ktime_to_ns(t2) - ktime_to_ns(t1));
|
||||
#endif
|
||||
break;
|
||||
#ifdef CONFIG_FB_MSM_OVERLAY
|
||||
case MSMFB_OVERLAY_GET:
|
||||
printk("CONFIG_FB_MSM_OVERLAY\n");
|
||||
//down(&mdp_ppp_lock);
|
||||
ret = msmfb_overlay_get(p, argp);
|
||||
//up(&mdp_ppp_lock);
|
||||
break;
|
||||
case MSMFB_OVERLAY_SET:
|
||||
printk("MSMFB_OVERLAY_SET\n");
|
||||
//down(&mdp_ppp_lock);
|
||||
ret = msmfb_overlay_set(p, argp);
|
||||
mutex_lock(&overlay_event_lock);
|
||||
overlay_event.waked_up = USE_OVERLAY;
|
||||
mutex_unlock(&overlay_event_lock);
|
||||
//up(&mdp_ppp_lock);
|
||||
break;
|
||||
case MSMFB_OVERLAY_UNSET:
|
||||
printk("MSMFB_OVERLAY_UNSET\n");
|
||||
//down(&mdp_ppp_lock);
|
||||
ret = msmfb_overlay_unset(p, argp);
|
||||
mutex_lock(&overlay_event_lock);
|
||||
overlay_event.waked_up = ~USE_OVERLAY;
|
||||
wake_up(&overlay_event.event_wait);
|
||||
mutex_unlock(&overlay_event_lock);
|
||||
//up(&mdp_ppp_lock);
|
||||
break;
|
||||
case MSMFB_OVERLAY_PLAY:
|
||||
//down(&mdp_ppp_lock);
|
||||
ret = msmfb_overlay_play(p, argp);
|
||||
//up(&mdp_ppp_lock);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
printk(KERN_INFO "msmfb unknown ioctl: %d\n", cmd);
|
||||
return -EINVAL;
|
||||
@ -658,6 +880,8 @@ static struct file_operations debug_fops = {
|
||||
};
|
||||
#endif
|
||||
|
||||
#define BITS_PER_PIXEL 16
|
||||
|
||||
static void setup_fb_info(struct msmfb_info *msmfb)
|
||||
{
|
||||
struct fb_info *fb_info = msmfb->fb;
|
||||
@ -673,13 +897,14 @@ static void setup_fb_info(struct msmfb_info *msmfb)
|
||||
fb_info->fix.type = FB_TYPE_PACKED_PIXELS;
|
||||
fb_info->fix.visual = FB_VISUAL_TRUECOLOR;
|
||||
fb_info->fix.line_length = msmfb->xres * 2;
|
||||
|
||||
fb_info->var.xres = msmfb->xres;
|
||||
fb_info->var.yres = msmfb->yres;
|
||||
fb_info->var.width = msmfb->panel->fb_data->width;
|
||||
fb_info->var.height = msmfb->panel->fb_data->height;
|
||||
fb_info->var.xres_virtual = msmfb->xres;
|
||||
fb_info->var.yres_virtual = msmfb->yres * 2;
|
||||
fb_info->var.bits_per_pixel = 16;
|
||||
fb_info->var.bits_per_pixel = BITS_PER_PIXEL;
|
||||
fb_info->var.accel_flags = 0;
|
||||
|
||||
fb_info->var.yoffset = 0;
|
||||
@ -688,8 +913,11 @@ static void setup_fb_info(struct msmfb_info *msmfb)
|
||||
/* set the param in the fixed screen, so userspace can't
|
||||
* change it. This will be used to check for the
|
||||
* capability. */
|
||||
|
||||
/* FIX ME: every panel support partial update?
|
||||
fb_info->fix.reserved[0] = 0x5444;
|
||||
fb_info->fix.reserved[1] = 0x5055;
|
||||
*/
|
||||
|
||||
/* This preloads the value so that if userspace doesn't
|
||||
* change it, it will be a full update */
|
||||
@ -717,6 +945,14 @@ static void setup_fb_info(struct msmfb_info *msmfb)
|
||||
PP[0] = 0;
|
||||
for (r = 1; r < 16; r++)
|
||||
PP[r] = 0xffffffff;
|
||||
|
||||
/* Jay add, 7/1/09' */
|
||||
#if (defined(CONFIG_USB_FUNCTION_PROJECTOR) || defined(CONFIG_USB_ANDROID_PROJECTOR))
|
||||
msm_fb_data.xres = msmfb->xres;
|
||||
msm_fb_data.yres = msmfb->yres;
|
||||
printk(KERN_INFO "setup_fb_info msmfb->xres %d, msmfb->yres %d\n",
|
||||
msmfb->xres,msmfb->yres);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int setup_fbmem(struct msmfb_info *msmfb, struct platform_device *pdev)
|
||||
@ -781,12 +1017,17 @@ static int msmfb_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
goto error_setup_fbmem;
|
||||
|
||||
#if (defined(CONFIG_USB_FUNCTION_PROJECTOR) || defined(CONFIG_USB_ANDROID_PROJECTOR))
|
||||
/* Jay, 8/1/09' */
|
||||
msmfb_set_var(msmfb->fb->screen_base, 0);
|
||||
#endif
|
||||
|
||||
setup_fb_info(msmfb);
|
||||
|
||||
spin_lock_init(&msmfb->update_lock);
|
||||
mutex_init(&msmfb->panel_init_lock);
|
||||
init_waitqueue_head(&msmfb->frame_wq);
|
||||
msmfb->resume_workqueue = create_workqueue("panel_on");
|
||||
msmfb->resume_workqueue = create_rt_workqueue("panel_on");
|
||||
if (msmfb->resume_workqueue == NULL) {
|
||||
printk(KERN_ERR "failed to create panel_on workqueue\n");
|
||||
ret = -ENOMEM;
|
||||
@ -799,8 +1040,9 @@ static int msmfb_probe(struct platform_device *pdev)
|
||||
wake_lock_init(&msmfb->idle_lock, WAKE_LOCK_IDLE, "msmfb_idle_lock");
|
||||
|
||||
#ifdef CONFIG_HAS_EARLYSUSPEND
|
||||
INIT_WORK(&msmfb->msmfb_resume_work, msmfb_resume);
|
||||
msmfb->early_suspend.suspend = msmfb_suspend;
|
||||
msmfb->early_suspend.resume = msmfb_resume;
|
||||
msmfb->early_suspend.resume = msmfb_resume_handler;
|
||||
msmfb->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
|
||||
register_early_suspend(&msmfb->early_suspend);
|
||||
|
||||
@ -822,7 +1064,6 @@ static int msmfb_probe(struct platform_device *pdev)
|
||||
hrtimer_init(&msmfb->fake_vsync, CLOCK_MONOTONIC,
|
||||
HRTIMER_MODE_REL);
|
||||
|
||||
|
||||
msmfb->fake_vsync.function = msmfb_fake_vsync;
|
||||
|
||||
ret = register_framebuffer(fb);
|
||||
@ -831,6 +1072,13 @@ static int msmfb_probe(struct platform_device *pdev)
|
||||
|
||||
msmfb->sleeping = WAKING;
|
||||
|
||||
#ifdef CONFIG_FB_MSM_OVERLAY
|
||||
/*init wait event*/
|
||||
init_waitqueue_head(&overlay_event.event_wait);
|
||||
/*init waked_up value*/
|
||||
overlay_event.waked_up = ~USE_OVERLAY;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_FB_MSM_LOGO
|
||||
if (!load_565rle_image(INIT_IMAGE_FILE)) {
|
||||
/* Flip buffer */
|
||||
@ -842,6 +1090,8 @@ static int msmfb_probe(struct platform_device *pdev)
|
||||
msmfb->yres, 0, 1);
|
||||
}
|
||||
#endif
|
||||
/* Jay, 29/12/08' */
|
||||
display_notifier(display_notifier_callback, NOTIFY_MSM_FB);
|
||||
return 0;
|
||||
|
||||
error_register_framebuffer:
|
||||
@ -854,9 +1104,35 @@ error_setup_fbmem:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void msmfb_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
struct msm_panel_data *panel = pdev->dev.platform_data;
|
||||
struct fb_info *fb;
|
||||
struct msmfb_info *msmfb;
|
||||
|
||||
printk(KERN_INFO "%s\n", __func__);
|
||||
fb = registered_fb[0];
|
||||
if (!fb) {
|
||||
printk(KERN_ERR "fb0 unavailable.\n");
|
||||
return;
|
||||
}
|
||||
msmfb = fb->par;
|
||||
|
||||
mdp->dma(mdp, virt_to_phys(msmfb->black), 0,
|
||||
msmfb->fb->var.xres, msmfb->fb->var.yres, 0, 0,
|
||||
NULL, panel->interface_type);
|
||||
|
||||
if (panel->blank)
|
||||
panel->blank(panel);
|
||||
|
||||
if (panel->shutdown)
|
||||
panel->shutdown(panel);
|
||||
}
|
||||
|
||||
static struct platform_driver msm_panel_driver = {
|
||||
/* need to write remove */
|
||||
.probe = msmfb_probe,
|
||||
.shutdown = msmfb_shutdown,
|
||||
.driver = {.name = "msm_panel"},
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user