From 3a35b5af72dcd168af3a63a992846ad2461dbae7 Mon Sep 17 00:00:00 2001 From: yzq Date: Fri, 20 Jul 2012 17:04:55 +0800 Subject: [PATCH] rk2928 hdmi support --- arch/arm/mach-rk2928/devices.c | 22 + drivers/video/Kconfig | 4 + drivers/video/rockchip/Makefile | 2 +- drivers/video/rockchip/hdmi/Kconfig | 30 +- drivers/video/rockchip/hdmi/Makefile | 6 +- drivers/video/rockchip/hdmi/chips/Kconfig | 23 + drivers/video/rockchip/hdmi/chips/Makefile | 8 + .../video/rockchip/hdmi/chips/rk2928/Kconfig | 1 + .../video/rockchip/hdmi/chips/rk2928/Makefile | 8 + .../rockchip/hdmi/chips/rk2928/hdcp/Kconfig | 14 + .../rockchip/hdmi/chips/rk2928/hdcp/Makefile | 8 + .../hdmi/chips/rk2928/hdcp/rk30_hdcp.c | 570 ++++++++++++++++++ .../hdmi/chips/rk2928/hdcp/rk30_hdmi_hdcp.c | 157 +++++ .../hdmi/chips/rk2928/hdcp/rk30_hdmi_hdcp.h | 99 +++ .../rockchip/hdmi/chips/rk2928/rk2928_hdmi.c | 299 +++++++++ .../rockchip/hdmi/chips/rk2928/rk2928_hdmi.h | 11 + .../hdmi/chips/rk2928/rk2928_hdmi_hw.c | 456 ++++++++++++++ .../hdmi/chips/rk2928/rk2928_hdmi_hw.h | 248 ++++++++ .../hdmi/chips/rk2928/rk2928_hdmi_hw.hbak | 497 +++++++++++++++ .../video/rockchip/hdmi/chips/rk30/Kconfig | 15 + .../video/rockchip/hdmi/chips/rk30/Makefile | 8 + .../rockchip/hdmi/chips/rk30/hdcp/Kconfig | 14 + .../rockchip/hdmi/chips/rk30/hdcp/Makefile | 8 + .../rockchip/hdmi/chips/rk30/hdcp/rk30_hdcp.c | 570 ++++++++++++++++++ .../hdmi/chips/rk30/hdcp/rk30_hdmi_hdcp.c | 157 +++++ .../hdmi/chips/rk30/hdcp/rk30_hdmi_hdcp.h | 99 +++ .../hdmi/{ => chips/rk30}/rk30_hdmi.c | 0 .../rockchip/hdmi/chips/rk30/rk30_hdmi.h | 11 + .../hdmi/{ => chips/rk30}/rk30_hdmi_hw.c | 15 +- .../hdmi/{ => chips/rk30}/rk30_hdmi_hw.h | 32 +- drivers/video/rockchip/hdmi/rk30_hdmi.h | 108 ---- drivers/video/rockchip/hdmi/rk_hdmi.h | 136 +++++ drivers/video/rockchip/hdmi/rk_hdmi_core.c | 254 ++++++++ .../hdmi/{rk30_hdmi_edid.c => rk_hdmi_edid.c} | 9 +- .../hdmi/{rk30_hdmi_lcdc.c => rk_hdmi_lcdc.c} | 5 +- .../{rk30_hdmi_sysfs.c => rk_hdmi_sysfs.c} | 4 +- .../hdmi/{rk30_hdmi_task.c => rk_hdmi_task.c} | 26 +- 37 files changed, 3746 insertions(+), 188 deletions(-) create mode 100755 drivers/video/rockchip/hdmi/chips/Kconfig create mode 100755 drivers/video/rockchip/hdmi/chips/Makefile create mode 100755 drivers/video/rockchip/hdmi/chips/rk2928/Kconfig create mode 100755 drivers/video/rockchip/hdmi/chips/rk2928/Makefile create mode 100755 drivers/video/rockchip/hdmi/chips/rk2928/hdcp/Kconfig create mode 100755 drivers/video/rockchip/hdmi/chips/rk2928/hdcp/Makefile create mode 100755 drivers/video/rockchip/hdmi/chips/rk2928/hdcp/rk30_hdcp.c create mode 100755 drivers/video/rockchip/hdmi/chips/rk2928/hdcp/rk30_hdmi_hdcp.c create mode 100755 drivers/video/rockchip/hdmi/chips/rk2928/hdcp/rk30_hdmi_hdcp.h create mode 100755 drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi.c create mode 100755 drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi.h create mode 100755 drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi_hw.c create mode 100644 drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi_hw.h create mode 100755 drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi_hw.hbak create mode 100755 drivers/video/rockchip/hdmi/chips/rk30/Kconfig create mode 100755 drivers/video/rockchip/hdmi/chips/rk30/Makefile create mode 100755 drivers/video/rockchip/hdmi/chips/rk30/hdcp/Kconfig create mode 100755 drivers/video/rockchip/hdmi/chips/rk30/hdcp/Makefile create mode 100755 drivers/video/rockchip/hdmi/chips/rk30/hdcp/rk30_hdcp.c create mode 100755 drivers/video/rockchip/hdmi/chips/rk30/hdcp/rk30_hdmi_hdcp.c create mode 100755 drivers/video/rockchip/hdmi/chips/rk30/hdcp/rk30_hdmi_hdcp.h rename drivers/video/rockchip/hdmi/{ => chips/rk30}/rk30_hdmi.c (100%) create mode 100755 drivers/video/rockchip/hdmi/chips/rk30/rk30_hdmi.h rename drivers/video/rockchip/hdmi/{ => chips/rk30}/rk30_hdmi_hw.c (97%) rename drivers/video/rockchip/hdmi/{ => chips/rk30}/rk30_hdmi_hw.h (94%) delete mode 100755 drivers/video/rockchip/hdmi/rk30_hdmi.h create mode 100755 drivers/video/rockchip/hdmi/rk_hdmi_core.c rename drivers/video/rockchip/hdmi/{rk30_hdmi_edid.c => rk_hdmi_edid.c} (95%) rename drivers/video/rockchip/hdmi/{rk30_hdmi_lcdc.c => rk_hdmi_lcdc.c} (99%) rename drivers/video/rockchip/hdmi/{rk30_hdmi_sysfs.c => rk_hdmi_sysfs.c} (99%) rename drivers/video/rockchip/hdmi/{rk30_hdmi_task.c => rk_hdmi_task.c} (94%) diff --git a/arch/arm/mach-rk2928/devices.c b/arch/arm/mach-rk2928/devices.c index d90a8055eec8..21e24335a532 100755 --- a/arch/arm/mach-rk2928/devices.c +++ b/arch/arm/mach-rk2928/devices.c @@ -522,6 +522,28 @@ static void __init rk2928_init_spim(void) platform_device_register(&rk29xx_device_spi0m); #endif } + +#ifdef CONFIG_HDMI_RK2928 +static struct resource resource_hdmi[] = { + [0] = { + .start = RK2928_HDMI_PHYS, + .end = RK2928_HDMI_PHYS + RK2928_HDMI_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_HDMI, + .end = IRQ_HDMI, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device device_hdmi = { + .name = "rk2928-hdmi", + .id = -1, + .num_resources = ARRAY_SIZE(resource_hdmi), + .resource = resource_hdmi, +}; +#endif #ifdef CONFIG_RGA_RK30 static struct resource resource_rga[] = { [0] = { diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index b4117f636264..47d5735793ba 100755 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -2438,7 +2438,11 @@ source "drivers/video/omap2/Kconfig" source "drivers/video/backlight/Kconfig" source "drivers/video/display/Kconfig" + +if !LCDC_RK30 && !LCDC_RK2928 source "drivers/video/hdmi/Kconfig" +endif + source "drivers/video/rockchip/Kconfig" if VT diff --git a/drivers/video/rockchip/Makefile b/drivers/video/rockchip/Makefile index ed778bb77b99..68967a49fc80 100755 --- a/drivers/video/rockchip/Makefile +++ b/drivers/video/rockchip/Makefile @@ -4,4 +4,4 @@ obj-$(CONFIG_LCDC_RK30) += chips/rk30_lcdc.o obj-$(CONFIG_LCDC_RK2928) += chips/rk2928_lcdc.o obj-$(CONFIG_LCDC_RK31) += chips/rk31_lcdc.o obj-$(CONFIG_RGA_RK30) += rga/ -obj-$(CONFIG_HDMI_RK30) += hdmi/ +obj-$(CONFIG_RK_HDMI) += hdmi/ diff --git a/drivers/video/rockchip/hdmi/Kconfig b/drivers/video/rockchip/hdmi/Kconfig index 654bfa0e67db..e3700bbd817d 100755 --- a/drivers/video/rockchip/hdmi/Kconfig +++ b/drivers/video/rockchip/hdmi/Kconfig @@ -1,23 +1,9 @@ -menuconfig HDMI_RK30 - bool "RK30 HDMI support" - depends on LCDC_RK30 - select FB_MODE_HELPERS - help - Support rk30 hdmi if you say y here +menu "RK_HDMI" +config RK_HDMI + bool "RK_HDMI support" + +if RK_HDMI +source "drivers/video/rockchip/hdmi/chips/Kconfig" +endif -config HDMI_RK30_CTL_CODEC - bool "Mute Codec When HDMI Actived" - depends on HDMI_RK30 - default n - help - If you say y heres, Codec will be mute when hdmi inserted, - and unmute when removed. - -config HDMI_RK30_DEBUG - bool "RK30 HDMI Debugging" - depends on HDMI_RK30 && LCDC_RK30 - default n - help - Enableds verbose debugging the the HDMI drivers - -source "drivers/video/rockchip/hdmi/hdcp/Kconfig" \ No newline at end of file +endmenu diff --git a/drivers/video/rockchip/hdmi/Makefile b/drivers/video/rockchip/hdmi/Makefile index 4692311dbd9a..ed00a162460d 100755 --- a/drivers/video/rockchip/hdmi/Makefile +++ b/drivers/video/rockchip/hdmi/Makefile @@ -2,7 +2,7 @@ # Makefile for HDMI linux kernel module. # -ccflags-$(CONFIG_HDMI_RK30_DEBUG) = -DDEBUG -DHDMI_DEBUG +ccflags-$(CONFIG_RK_HDMI_DEBUG) = -DDEBUG -DHDMI_DEBUG -obj-$(CONFIG_HDMI_RK30) += rk30_hdmi_hw.o rk30_hdmi_edid.o rk30_hdmi_lcdc.o rk30_hdmi_task.o rk30_hdmi_sysfs.o rk30_hdmi.o -obj-$(CONFIG_HDCP_RK30) += hdcp/ +obj-$(CONFIG_RK_HDMI) += rk_hdmi_edid.o rk_hdmi_lcdc.o rk_hdmi_task.o rk_hdmi_sysfs.o +obj-$(CONFIG_RK_HDMI) += chips/ diff --git a/drivers/video/rockchip/hdmi/chips/Kconfig b/drivers/video/rockchip/hdmi/chips/Kconfig new file mode 100755 index 000000000000..e0c1c271bdc1 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/Kconfig @@ -0,0 +1,23 @@ +choice + prompt "HDMI chips select" +config HDMI_RK30 + bool "RK30 HDMI support" + depends on LCDC_RK30 + select FB_MODE_HELPERS + help + Support rk30 hdmi if you say y here +config HDMI_RK2928 + bool "RK2928 HDMI support" + depends on LCDC_RK2928 + select FB_MODE_HELPERS + help + Support rk2928 hdmi if you say y here +endchoice + +if HDMI_RK30 +source "drivers/video/rockchip/hdmi/chips/rk30/Kconfig" +endif + +if HDMI_RK2928 +source "drivers/video/rockchip/hdmi/chips/rk2928/Kconfig" +endif diff --git a/drivers/video/rockchip/hdmi/chips/Makefile b/drivers/video/rockchip/hdmi/chips/Makefile new file mode 100755 index 000000000000..a43ac0633d6a --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for HDMI linux kernel module. +# + +ccflags-$(CONFIG_HDMI_RK30_DEBUG) = -DDEBUG -DHDMI_DEBUG + +obj-$(CONFIG_HDMI_RK30) += rk30/ +obj-$(CONFIG_HDMI_RK2928) += rk2928/ diff --git a/drivers/video/rockchip/hdmi/chips/rk2928/Kconfig b/drivers/video/rockchip/hdmi/chips/rk2928/Kconfig new file mode 100755 index 000000000000..8b137891791f --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk2928/Kconfig @@ -0,0 +1 @@ + diff --git a/drivers/video/rockchip/hdmi/chips/rk2928/Makefile b/drivers/video/rockchip/hdmi/chips/rk2928/Makefile new file mode 100755 index 000000000000..d3227c7f27d9 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk2928/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for HDMI linux kernel module. +# + +ccflags-$(CONFIG_HDMI_RK30_DEBUG) = -DDEBUG -DHDMI_DEBUG + +obj-y += rk2928_hdmi_hw.o rk2928_hdmi.o +obj-$(CONFIG_HDCP_RK2928) += hdcp/ diff --git a/drivers/video/rockchip/hdmi/chips/rk2928/hdcp/Kconfig b/drivers/video/rockchip/hdmi/chips/rk2928/hdcp/Kconfig new file mode 100755 index 000000000000..1bb3c3a24f56 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk2928/hdcp/Kconfig @@ -0,0 +1,14 @@ +config HDCP_RK30 + bool "RK30 HDCP support" + depends on LCDC_RK30 && HDMI_RK30 + default n + help + HDCP Interface. This adds the High Definition Content Protection Interface. + See http://www.digital-cp.com/ for HDCP specification. + +config HDCP_RK30_DEBUG + bool "RK30 HDCP Debugging" + depends on HDCP_RK30 + default n + help + Enableds verbose debugging the the HDCP drivers diff --git a/drivers/video/rockchip/hdmi/chips/rk2928/hdcp/Makefile b/drivers/video/rockchip/hdmi/chips/rk2928/hdcp/Makefile new file mode 100755 index 000000000000..108b67ca134e --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk2928/hdcp/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for HDCP linux kernel module. +# + +ccflags-$(CONFIG_HDCP_RK30_DEBUG) = -DDEBUG -DHDCP_DEBUG + +obj-$(CONFIG_HDCP_RK30) += hdcp.o +hdcp-y := rk30_hdcp.o rk30_hdmi_hdcp.o diff --git a/drivers/video/rockchip/hdmi/chips/rk2928/hdcp/rk30_hdcp.c b/drivers/video/rockchip/hdmi/chips/rk2928/hdcp/rk30_hdcp.c new file mode 100755 index 000000000000..68d0ac841a67 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk2928/hdcp/rk30_hdcp.c @@ -0,0 +1,570 @@ +#include +#include +#include +#include +#include +#include +#include +#include "../rk30_hdmi.h" +#include "../rk30_hdmi_hw.h" +#include "rk30_hdmi_hdcp.h" + +struct hdcp *hdcp = NULL; + +static void hdcp_work_queue(struct work_struct *work); + +/*----------------------------------------------------------------------------- + * Function: hdcp_submit_work + *----------------------------------------------------------------------------- + */ +static struct delayed_work *hdcp_submit_work(int event, int delay) +{ + struct hdcp_delayed_work *work; + + DBG("%s event %04x delay %d", __FUNCTION__, event, delay); + + work = kmalloc(sizeof(struct hdcp_delayed_work), GFP_ATOMIC); + + if (work) { + INIT_DELAYED_WORK(&work->work, hdcp_work_queue); + work->event = event; + queue_delayed_work(hdcp->workqueue, + &work->work, + msecs_to_jiffies(delay)); + } else { + printk(KERN_WARNING "HDCP: Cannot allocate memory to " + "create work\n"); + return 0; + } + + return &work->work; +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_cancel_work + *----------------------------------------------------------------------------- + */ +static void hdcp_cancel_work(struct delayed_work **work) +{ + int ret = 0; + + if (*work) { + ret = cancel_delayed_work(*work); + if (ret != 1) { + ret = cancel_work_sync(&((*work)->work)); + printk(KERN_INFO "Canceling work failed - " + "cancel_work_sync done %d\n", ret); + } + kfree(*work); + *work = 0; + } +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_wq_authentication_failure + *----------------------------------------------------------------------------- + */ +static void hdcp_wq_authentication_failure(void) +{ + if (hdcp->hdmi_state == HDMI_STOPPED) { + return; + } + + rk30_hdcp_disable(); + rk30_hdmi_control_output(false); + + hdcp_cancel_work(&hdcp->pending_wq_event); + + if (hdcp->retry_cnt && (hdcp->hdmi_state != HDMI_STOPPED)) { + if (hdcp->retry_cnt < HDCP_INFINITE_REAUTH) { + hdcp->retry_cnt--; + printk(KERN_INFO "HDCP: authentication failed - " + "retrying, attempts=%d\n", + hdcp->retry_cnt); + } else + printk(KERN_INFO "HDCP: authentication failed - " + "retrying\n"); + + hdcp->hdcp_state = HDCP_AUTHENTICATION_START; + + hdcp->pending_wq_event = hdcp_submit_work(HDCP_AUTH_REATT_EVENT, + HDCP_REAUTH_DELAY); + } else { + printk(KERN_INFO "HDCP: authentication failed - " + "HDCP disabled\n"); + hdcp->hdcp_state = HDCP_ENABLE_PENDING; + } + +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_wq_start_authentication + *----------------------------------------------------------------------------- + */ +static void hdcp_wq_start_authentication(void) +{ + int status = HDCP_OK; + + hdcp->hdcp_state = HDCP_AUTHENTICATION_START; + + DBG("HDCP: authentication start"); + + status = rk30_hdcp_start_authentication(); + + if (status != HDCP_OK) { + DBG("HDCP: authentication failed"); + hdcp_wq_authentication_failure(); + } else { + hdcp->hdcp_state = HDCP_WAIT_KSV_LIST; + } +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_wq_check_bksv + *----------------------------------------------------------------------------- + */ +static void hdcp_wq_check_bksv(void) +{ + int status = HDCP_OK; + + DBG("Check BKSV start"); + + status = rk30_hdcp_check_bksv(); + + if (status != HDCP_OK) { + printk(KERN_INFO "HDCP: Check BKSV failed"); + hdcp->retry_cnt = 0; + hdcp_wq_authentication_failure(); + } + else { + DBG("HDCP: Check BKSV successful"); + + hdcp->hdcp_state = HDCP_LINK_INTEGRITY_CHECK; + + /* Restore retry counter */ + if(hdcp->retry_times == 0) + hdcp->retry_cnt = HDCP_INFINITE_REAUTH; + else + hdcp->retry_cnt = hdcp->retry_times; + } +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_wq_authentication_sucess + *----------------------------------------------------------------------------- + */ +static void hdcp_wq_authentication_sucess(void) +{ + printk(KERN_INFO "HDCP: authentication pass"); + rk30_hdmi_control_output(true); +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_wq_disable + *----------------------------------------------------------------------------- + */ +static void hdcp_wq_disable(int event) +{ + printk(KERN_INFO "HDCP: disabled"); + + hdcp_cancel_work(&hdcp->pending_wq_event); + rk30_hdcp_disable(); + if(event == HDCP_DISABLE_CTL) { + hdcp->hdcp_state = HDCP_DISABLED; + if(hdcp->hdmi_state == HDMI_STARTED) + rk30_hdmi_control_output(true); + } + else if(event == HDCP_STOP_FRAME_EVENT) + hdcp->hdcp_state = HDCP_ENABLE_PENDING; +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_work_queue + *----------------------------------------------------------------------------- + */ +static void hdcp_work_queue(struct work_struct *work) +{ + struct hdcp_delayed_work *hdcp_w = + container_of(work, struct hdcp_delayed_work, work.work); + int event = hdcp_w->event; + + mutex_lock(&hdcp->lock); + + DBG("hdcp_work_queue() - START - %u hdmi=%d hdcp=%d evt= %x %d", + jiffies_to_msecs(jiffies), + hdcp->hdmi_state, + hdcp->hdcp_state, + (event & 0xFF00) >> 8, + event & 0xFF); + + if(event == HDCP_STOP_FRAME_EVENT) { + hdcp->hdmi_state = HDMI_STOPPED; + } + + if (event == HDCP_DISABLE_CTL || event == HDCP_STOP_FRAME_EVENT) { + hdcp_wq_disable(event); + } + + if (event & HDCP_WORKQUEUE_SRC) + hdcp->pending_wq_event = 0; + + /* First handle HDMI state */ + if (event == HDCP_START_FRAME_EVENT) { + hdcp->pending_start = 0; + hdcp->hdmi_state = HDMI_STARTED; + } + + /**********************/ + /* HDCP state machine */ + /**********************/ + switch (hdcp->hdcp_state) { + case HDCP_DISABLED: + /* HDCP enable control or re-authentication event */ + if (event == HDCP_ENABLE_CTL) { + if(hdcp->retry_times == 0) + hdcp->retry_cnt = HDCP_INFINITE_REAUTH; + else + hdcp->retry_cnt = hdcp->retry_times; + if (hdcp->hdmi_state == HDMI_STARTED) + hdcp_wq_start_authentication(); + else + hdcp->hdcp_state = HDCP_ENABLE_PENDING; + } + break; + + case HDCP_ENABLE_PENDING: + /* HDMI start frame event */ + if (event == HDCP_START_FRAME_EVENT) + hdcp_wq_start_authentication(); + + break; + + case HDCP_AUTHENTICATION_START: + /* Re-authentication */ + if (event == HDCP_AUTH_REATT_EVENT) + hdcp_wq_start_authentication(); + + break; + + case HDCP_WAIT_KSV_LIST: + /* KSV failure */ + if (event == HDCP_FAIL_EVENT) { + printk(KERN_INFO "HDCP: KSV switch failure\n"); + + hdcp_wq_authentication_failure(); + } + /* KSV list ready event */ + else if (event == HDCP_KSV_LIST_RDY_EVENT) + hdcp_wq_check_bksv(); + break; + + case HDCP_LINK_INTEGRITY_CHECK: + /* Ri failure */ + if (event == HDCP_FAIL_EVENT) { + printk(KERN_INFO "HDCP: Ri check failure\n"); + hdcp_wq_authentication_failure(); + } + else if(event == HDCP_AUTH_PASS_EVENT) + hdcp_wq_authentication_sucess(); + break; + + default: + printk(KERN_WARNING "HDCP: error - unknow HDCP state\n"); + break; + } + + kfree(hdcp_w); + if(event == HDCP_STOP_FRAME_EVENT) + complete(&hdcp->complete); + + mutex_unlock(&hdcp->lock); +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_start_frame_cb + *----------------------------------------------------------------------------- + */ +static void hdcp_start_frame_cb(void) +{ + DBG("hdcp_start_frame_cb()"); + + /* Cancel any pending work */ + if (hdcp->pending_start) + hdcp_cancel_work(&hdcp->pending_start); + if (hdcp->pending_wq_event) + hdcp_cancel_work(&hdcp->pending_wq_event); + + hdcp->pending_start = hdcp_submit_work(HDCP_START_FRAME_EVENT, + HDCP_ENABLE_DELAY); +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_irq_cb + *----------------------------------------------------------------------------- + */ +static void hdcp_irq_cb(int interrupt) +{ + int value; + DBG("%s 0x%x", __FUNCTION__, interrupt); + if(interrupt & m_INT_HDCP_ERR) + { + value = HDMIRdReg(HDCP_ERROR); + HDMIWrReg(HDCP_ERROR, value); + printk(KERN_INFO "HDCP: Error 0x%02x\n", value); + + if( (hdcp->hdcp_state != HDCP_DISABLED) && + (hdcp->hdcp_state != HDCP_ENABLE_PENDING) ) + { + hdcp_submit_work(HDCP_FAIL_EVENT, 0); + } + } + else if(interrupt & (m_INT_BKSV_RPRDY | m_INT_BKSV_RCRDY)) + hdcp_submit_work(HDCP_KSV_LIST_RDY_EVENT, 0); + else if(interrupt & m_INT_AUTH_DONE) + hdcp_submit_work(HDCP_AUTH_PASS_EVENT, 0); +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_power_on_cb + *----------------------------------------------------------------------------- + */ +static int hdcp_power_on_cb(void) +{ + DBG("%s", __FUNCTION__); + return rk30_hdcp_load_key2mem(hdcp->keys); +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_power_off_cb + *----------------------------------------------------------------------------- + */ +static void hdcp_power_off_cb(void) +{ + DBG("%s", __FUNCTION__); + if(!hdcp->enable) + return; + + hdcp_cancel_work(&hdcp->pending_start); + hdcp_cancel_work(&hdcp->pending_wq_event); + init_completion(&hdcp->complete); + /* Post event to workqueue */ + if (hdcp_submit_work(HDCP_STOP_FRAME_EVENT, 0)) + wait_for_completion_interruptible_timeout(&hdcp->complete, + msecs_to_jiffies(2000)); +} + +// Load HDCP key to external HDCP memory +static void hdcp_load_keys_cb(const struct firmware *fw, void *context) +{ + if (!fw) { + pr_err("HDCP: failed to load keys\n"); + return; + } + + if(fw->size < HDCP_KEY_SIZE) { + pr_err("HDCP: firmware wrong size %d\n", fw->size); + return; + } + + hdcp->keys = kmalloc(HDCP_KEY_SIZE, GFP_KERNEL); + if(hdcp->keys == NULL) { + pr_err("HDCP: can't allocated space for keys\n"); + return; + } + + memcpy(hdcp->keys, fw->data, HDCP_KEY_SIZE); + + rk30_hdcp_load_key2mem(hdcp->keys); + printk(KERN_INFO "HDCP: loaded hdcp key success\n"); + + if(fw->size > HDCP_KEY_SIZE) { + DBG("%s invalid key size %d", __FUNCTION__, fw->size - HDCP_KEY_SIZE); + if((fw->size - HDCP_KEY_SIZE) % 5) { + pr_err("HDCP: failed to load invalid keys\n"); + return; + } + hdcp->invalidkeys = kmalloc(fw->size - HDCP_KEY_SIZE, GFP_KERNEL); + if(hdcp->invalidkeys == NULL) { + pr_err("HDCP: can't allocated space for invalid keys\n"); + return; + } + memcpy(hdcp->invalidkeys, fw->data + HDCP_KEY_SIZE, fw->size - HDCP_KEY_SIZE); + hdcp->invalidkey = (fw->size - HDCP_KEY_SIZE)/5; + printk(KERN_INFO "HDCP: loaded hdcp invalid key success\n"); + } +} + +static ssize_t hdcp_enable_read(struct device *device, + struct device_attribute *attr, char *buf) +{ + int enable = 0; + + if(hdcp) + enable = hdcp->enable; + + return snprintf(buf, PAGE_SIZE, "%d\n", enable); +} + +static ssize_t hdcp_enable_write(struct device *device, + struct device_attribute *attr, const char *buf, size_t count) +{ + int enable; + + if(hdcp == NULL) + return -EINVAL; + + sscanf(buf, "%d", &enable); + if(hdcp->enable != enable) + { + /* Post event to workqueue */ + if(enable) { + if (hdcp_submit_work(HDCP_ENABLE_CTL, 0) == 0) + return -EFAULT; + } + else { + hdcp_cancel_work(&hdcp->pending_start); + hdcp_cancel_work(&hdcp->pending_wq_event); + + /* Post event to workqueue */ + if (hdcp_submit_work(HDCP_DISABLE_CTL, 0) == 0) + return -EFAULT; + } + hdcp->enable = enable; + } + return count; +} + +static DEVICE_ATTR(enable, S_IRUGO|S_IWUSR, hdcp_enable_read, hdcp_enable_write); + +static ssize_t hdcp_trytimes_read(struct device *device, + struct device_attribute *attr, char *buf) +{ + int trytimes = 0; + + if(hdcp) + trytimes = hdcp->retry_times; + + return snprintf(buf, PAGE_SIZE, "%d\n", trytimes); +} + +static ssize_t hdcp_trytimes_wrtie(struct device *device, + struct device_attribute *attr, const char *buf, size_t count) +{ + int trytimes; + + if(hdcp == NULL) + return -EINVAL; + + sscanf(buf, "%d", &trytimes); + if(hdcp->retry_times != trytimes) + hdcp->retry_times = trytimes; + + return count; +} + + +static DEVICE_ATTR(trytimes, S_IRUGO|S_IWUSR, hdcp_trytimes_read, hdcp_trytimes_wrtie); + + +static struct miscdevice mdev; + +static int __init rk30_hdcp_init(void) +{ + int ret; + + DBG("[%s] %u", __FUNCTION__, jiffies_to_msecs(jiffies)); + + hdcp = kmalloc(sizeof(struct hdcp), GFP_KERNEL); + if(!hdcp) + { + printk(KERN_ERR ">>HDCP: kmalloc fail!"); + ret = -ENOMEM; + goto error0; + } + memset(hdcp, 0, sizeof(struct hdcp)); + mutex_init(&hdcp->lock); + + mdev.minor = MISC_DYNAMIC_MINOR; + mdev.name = "hdcp"; + mdev.mode = 0666; + if (misc_register(&mdev)) { + printk(KERN_ERR "HDCP: Could not add character driver\n"); + ret = HDMI_ERROR_FALSE; + goto error1; + } + ret = device_create_file(mdev.this_device, &dev_attr_enable); + if(ret) + { + printk(KERN_ERR "HDCP: Could not add sys file enable\n"); + ret = -EINVAL; + goto error2; + } + + ret = device_create_file(mdev.this_device, &dev_attr_trytimes); + if(ret) + { + printk(KERN_ERR "HDCP: Could not add sys file trytimes\n"); + ret = -EINVAL; + goto error3; + } + + hdcp->workqueue = create_singlethread_workqueue("hdcp"); + if (hdcp->workqueue == NULL) { + printk(KERN_ERR "HDCP,: create workqueue failed.\n"); + goto error4; + } + + + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG, + "hdcp.keys", mdev.this_device, GFP_KERNEL, + hdcp, hdcp_load_keys_cb); + if (ret < 0) { + printk(KERN_ERR "HDCP: request_firmware_nowait failed: %d\n", ret); + goto error5; + } + + rk30_hdmi_register_hdcp_callbacks( hdcp_start_frame_cb, + hdcp_irq_cb, + hdcp_power_on_cb, + hdcp_power_off_cb); + + DBG("%s success %u", __FUNCTION__, jiffies_to_msecs(jiffies)); + return 0; + +error5: + destroy_workqueue(hdcp->workqueue); +error4: + device_remove_file(mdev.this_device, &dev_attr_trytimes); +error3: + device_remove_file(mdev.this_device, &dev_attr_enable); +error2: + misc_deregister(&mdev); +error1: + if(hdcp->keys) + kfree(hdcp->keys); + if(hdcp->invalidkeys) + kfree(hdcp->invalidkeys); + kfree(hdcp); +error0: + return ret; +} + +static void __exit rk30_hdcp_exit(void) +{ + if(hdcp) { + mutex_lock(&hdcp->lock); + rk30_hdmi_register_hdcp_callbacks(0, 0, 0, 0); + device_remove_file(mdev.this_device, &dev_attr_enable); + misc_deregister(&mdev); + destroy_workqueue(hdcp->workqueue); + if(hdcp->keys) + kfree(hdcp->keys); + if(hdcp->invalidkeys) + kfree(hdcp->invalidkeys); + mutex_unlock(&hdcp->lock); + kfree(hdcp); + } +} + +module_init(rk30_hdcp_init); +module_exit(rk30_hdcp_exit); \ No newline at end of file diff --git a/drivers/video/rockchip/hdmi/chips/rk2928/hdcp/rk30_hdmi_hdcp.c b/drivers/video/rockchip/hdmi/chips/rk2928/hdcp/rk30_hdmi_hdcp.c new file mode 100755 index 000000000000..1184989a57b2 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk2928/hdcp/rk30_hdmi_hdcp.c @@ -0,0 +1,157 @@ +#include +#include +#include +#include "../rk30_hdmi.h" +#include "../rk30_hdmi_hw.h" +#include "rk30_hdmi_hdcp.h" + +static void rk30_hdcp_write_mem(int addr_8, char value) +{ + int temp; + int addr_32 = addr_8 - addr_8%4; + int shift = (addr_8%4) * 8; + + temp = HDMIRdReg(addr_32); + temp &= ~(0xff << shift); + temp |= value << shift; +// printk("temp is %08x\n", temp); + HDMIWrReg(addr_32, temp); +} + +int rk30_hdcp_load_key2mem(struct hdcp_keys *key) +{ + int i; + + if(key == NULL) return HDMI_ERROR_FALSE; + + HDMIWrReg(0x800, HDMI_INTERANL_CLK_DIV); + + for(i = 0; i < 7; i++) + rk30_hdcp_write_mem(HDCP_RAM_KEY_KSV1 + i, key->KSV[i]); + for(i = 0; i < 7; i++) + rk30_hdcp_write_mem(HDCP_RAM_KEY_KSV2 + i, key->KSV[i]); + for(i = 0; i < HDCP_PRIVATE_KEY_SIZE; i++) + rk30_hdcp_write_mem(HDCP_RAM_KEY_PRIVATE + i, key->DeviceKey[i]); + for(i = 0; i < HDCP_KEY_SHA_SIZE; i++) + rk30_hdcp_write_mem(HDCP_RAM_KEY_PRIVATE + HDCP_PRIVATE_KEY_SIZE + i, key->sha1[i]); + + HDMIWrReg(0x800, HDMI_INTERANL_CLK_DIV | 0x20); + return HDCP_OK; +} + +void rk30_hdcp_disable(void) +{ + int temp; + // Diable HDCP Interrupt + HDMIWrReg(INTR_MASK2, 0x00); + // Stop and Reset HDCP + HDMIMskReg(temp, HDCP_CTRL, m_HDCP_FRAMED_ENCRYPED | m_HDCP_AUTH_STOP | m_HDCP_RESET, + v_HDCP_FRAMED_ENCRYPED(0) | v_HDCP_AUTH_STOP(1) | v_HDCP_RESET(1) ); +} + +static int rk30_hdcp_load_key(void) +{ + int value, temp = 0; + + if(hdcp->keys == NULL) { + pr_err("[%s] HDCP key not loaded.\n", __FUNCTION__); + return HDCP_KEY_ERR; + } + + value = HDMIRdReg(HDCP_KEY_MEM_CTRL); + //Check HDCP key loaded from external HDCP memory + while((value & (m_KSV_VALID | m_KEY_VALID | m_KEY_READY)) != (m_KSV_VALID | m_KEY_VALID | m_KEY_READY)) { + if(temp > 10) { + pr_err("[%s] loaded hdcp key is incorrectable %02x\n", __FUNCTION__, value & 0xFF); + return HDCP_KEY_ERR; + } + //Load HDCP Key from external HDCP memory + HDMIWrReg(HDCP_KEY_ACCESS_CTRL2, m_LOAD_HDCP_KEY); + msleep(1); + value = HDMIRdReg(HDCP_KEY_MEM_CTRL); + temp++; + } + + return HDCP_OK; +} + + +int rk30_hdcp_start_authentication(void) +{ + int rc, temp; + + rc = rk30_hdcp_load_key(); + if(rc != HDCP_OK) + return rc; + + // Set 100ms & 5 sec timer + switch(hdmi->vic) + { + case HDMI_720x576p_50Hz_4_3: + case HDMI_720x576p_50Hz_16_9: + case HDMI_1280x720p_50Hz: + case HDMI_1920x1080i_50Hz: + case HDMI_720x576i_50Hz_4_3: + case HDMI_720x576i_50Hz_16_9: + case HDMI_1920x1080p_50Hz: + HDMIWrReg(HDCP_TIMER_100MS, 5); + HDMIWrReg(HDCP_TIMER_5S, 250); + break; + + default: + HDMIWrReg(HDCP_TIMER_100MS, 0x26); + HDMIWrReg(HDCP_TIMER_5S, 0x2c); + break; + } + // Config DDC Clock + temp = (hdmi->tmdsclk/HDCP_DDC_CLK)/4; + HDMIWrReg(DDC_BUS_FREQ_L, temp & 0xFF); + HDMIWrReg(DDC_BUS_FREQ_H, (temp >> 8) & 0xFF); + // Enable HDCP Interrupt + HDMIWrReg(INTR_MASK2, m_INT_HDCP_ERR | m_INT_BKSV_RPRDY | m_INT_BKSV_RCRDY | m_INT_AUTH_DONE | m_INT_AUTH_READY); + // Start HDCP + HDMIMskReg(temp, HDCP_CTRL, m_HDCP_AUTH_START | m_HDCP_FRAMED_ENCRYPED, v_HDCP_AUTH_START(1) | v_HDCP_FRAMED_ENCRYPED(0)); + + return HDCP_OK; +} + +int rk30_hdcp_check_bksv(void) +{ + int i, temp; + char bksv[5]; + char *invalidkey; + + temp = HDMIRdReg(HDCP_BCAPS); + DBG("Receiver capacity is 0x%02x", temp); + +#ifdef DEBUG + if(temp & m_HDMI_RECEIVED) + DBG("Receiver support HDMI"); + if(temp & m_REPEATER) + DBG("Receiver is a repeater"); + if(temp & m_DDC_FAST) + DBG("Receiver support 400K DDC"); + if(temp & m_1_1_FEATURE) + DBG("Receiver support 1.1 features, such as advanced cipher, EESS."); + if(temp & m_FAST_REAUTHENTICATION) + DBG("Receiver support fast reauthentication."); +#endif + + for(i = 0; i < 5; i++) { + bksv[i] = HDMIRdReg(HDCP_KSV_BYTE0 + (4 - i)*4) & 0xFF; + } + + DBG("bksv is 0x%02x%02x%02x%02x%02x", bksv[0], bksv[1], bksv[2], bksv[3], bksv[4]); + + for(i = 0; i < hdcp->invalidkey; i++) + { + invalidkey = hdcp->invalidkeys + i *5; + if(memcmp(bksv, invalidkey, 5) == 0) { + printk(KERN_ERR "HDCP: BKSV was revocated!!!\n"); + HDMIMskReg(temp, HDCP_CTRL, m_HDCP_BKSV_FAILED | m_HDCP_FRAMED_ENCRYPED, v_HDCP_BKSV_FAILED(1) | v_HDCP_FRAMED_ENCRYPED(0)); + return HDCP_KSV_ERR; + } + } + HDMIMskReg(temp, HDCP_CTRL, m_HDCP_BKSV_PASS | m_HDCP_FRAMED_ENCRYPED, v_HDCP_BKSV_PASS(1) | v_HDCP_FRAMED_ENCRYPED(1)); + return HDCP_OK; +} diff --git a/drivers/video/rockchip/hdmi/chips/rk2928/hdcp/rk30_hdmi_hdcp.h b/drivers/video/rockchip/hdmi/chips/rk2928/hdcp/rk30_hdmi_hdcp.h new file mode 100755 index 000000000000..0224d88ab134 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk2928/hdcp/rk30_hdmi_hdcp.h @@ -0,0 +1,99 @@ +#ifndef __RK30_HDMI_HDCP_H__ +#define __RK30_HDMI_HDCP_H__ + +/***************************/ +/* Definitions */ +/***************************/ + +/* Status / error codes */ +#define HDCP_OK 0 +#define HDCP_KEY_ERR 1 +#define HDCP_KSV_ERR 2 + +/* Delays */ +#define HDCP_ENABLE_DELAY 300 +#define HDCP_REAUTH_DELAY 100 + +/* Event source */ +#define HDCP_SRC_SHIFT 8 +#define HDCP_IOCTL_SRC (0x1 << HDCP_SRC_SHIFT) +#define HDCP_HDMI_SRC (0x2 << HDCP_SRC_SHIFT) +#define HDCP_IRQ_SRC (0x4 << HDCP_SRC_SHIFT) +#define HDCP_WORKQUEUE_SRC (0x8 << HDCP_SRC_SHIFT) + +/* Event */ +#define HDCP_ENABLE_CTL (HDCP_IOCTL_SRC | 0) +#define HDCP_DISABLE_CTL (HDCP_IOCTL_SRC | 1) +#define HDCP_START_FRAME_EVENT (HDCP_HDMI_SRC | 2) +#define HDCP_STOP_FRAME_EVENT (HDCP_HDMI_SRC | 3) +#define HDCP_KSV_LIST_RDY_EVENT (HDCP_IRQ_SRC | 4) +#define HDCP_FAIL_EVENT (HDCP_IRQ_SRC | 5) +#define HDCP_AUTH_PASS_EVENT (HDCP_IRQ_SRC | 6) +#define HDCP_AUTH_REATT_EVENT (HDCP_WORKQUEUE_SRC | 7) + +/* Key size */ +#define HDCP_KEY_SIZE 308 + +/* Authentication retry times */ +#define HDCP_INFINITE_REAUTH 0x100 + +enum hdcp_states { + HDCP_DISABLED, + HDCP_ENABLE_PENDING, + HDCP_AUTHENTICATION_START, + HDCP_WAIT_KSV_LIST, + HDCP_LINK_INTEGRITY_CHECK, +}; + +enum hdmi_states { + HDMI_STOPPED, + HDMI_STARTED +}; + +#define HDCP_PRIVATE_KEY_SIZE 280 +#define HDCP_KEY_SHA_SIZE 20 +#define HDCP_DDC_CLK 100000 + +struct hdcp_keys{ + u8 KSV[8]; + u8 DeviceKey[HDCP_PRIVATE_KEY_SIZE]; + u8 sha1[HDCP_KEY_SHA_SIZE]; +}; + +struct hdcp_delayed_work { + struct delayed_work work; + int event; +}; + +struct hdcp { + int enable; + int retry_times; + struct hdcp_keys *keys; + int invalidkey; + char *invalidkeys; + struct mutex lock; + struct completion complete; + struct workqueue_struct *workqueue; + + enum hdmi_states hdmi_state; + enum hdcp_states hdcp_state; + + struct delayed_work *pending_start; + struct delayed_work *pending_wq_event; + int retry_cnt; +}; + +extern struct hdcp *hdcp; + +#ifdef HDCP_DEBUG +#define DBG(format, ...) \ + printk(KERN_INFO "HDCP: " format "\n", ## __VA_ARGS__) +#else +#define DBG(format, ...) +#endif + +extern void rk30_hdcp_disable(void); +extern int rk30_hdcp_start_authentication(void); +extern int rk30_hdcp_check_bksv(void); +extern int rk30_hdcp_load_key2mem(struct hdcp_keys *key); +#endif /* __RK30_HDMI_HDCP_H__ */ \ No newline at end of file diff --git a/drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi.c b/drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi.c new file mode 100755 index 000000000000..1ef14180bd6f --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi.c @@ -0,0 +1,299 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "rk2928_hdmi.h" +#include "rk2928_hdmi_hw.h" + +struct hdmi *hdmi = NULL; + +extern irqreturn_t hdmi_irq(int irq, void *priv); +extern void hdmi_work(struct work_struct *work); +extern struct rk_lcdc_device_driver * rk_get_lcdc_drv(char *name); +extern void hdmi_register_display_sysfs(struct hdmi *hdmi, struct device *parent); +extern void hdmi_unregister_display_sysfs(struct hdmi *hdmi); + +int rk2928_hdmi_register_hdcp_callbacks(void (*hdcp_cb)(void), + void (*hdcp_irq_cb)(int status), + int (*hdcp_power_on_cb)(void), + void (*hdcp_power_off_cb)(void)) +{ + if(hdmi == NULL) + return HDMI_ERROR_FALSE; + + hdmi->hdcp_cb = hdcp_cb; + hdmi->hdcp_irq_cb = hdcp_irq_cb; + hdmi->hdcp_power_on_cb = hdcp_power_on_cb; + hdmi->hdcp_power_off_cb = hdcp_power_off_cb; + + return HDMI_ERROR_SUCESS; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void hdmi_early_suspend(struct early_suspend *h) +{ + hdmi_dbg(hdmi->dev, "hdmi enter early suspend pwr %d state %d\n", hdmi->pwr_mode, hdmi->state); + flush_delayed_work(&hdmi->delay_work); + mutex_lock(&hdmi->enable_mutex); + hdmi->suspend = 1; + if(!hdmi->enable) { + mutex_unlock(&hdmi->enable_mutex); + return; + } + disable_irq(hdmi->irq); + mutex_unlock(&hdmi->enable_mutex); + hdmi->command = HDMI_CONFIG_ENABLE; + init_completion(&hdmi->complete); + hdmi->wait = 1; + queue_delayed_work(hdmi->workqueue, &hdmi->delay_work, 0); + wait_for_completion_interruptible_timeout(&hdmi->complete, + msecs_to_jiffies(5000)); + flush_delayed_work(&hdmi->delay_work); + // When HDMI 1.1V and 2.5V power off, DDC channel will be pull down, current is produced + // from VCC_IO which is pull up outside soc. We need to switch DDC IO to GPIO. +// rk2928_mux_api_set(GPIO0A7_I2C3_SDA_HDMI_DDCSDA_NAME, GPIO0A_GPIO0A7); +// rk2928_mux_api_set(GPIO0A6_I2C3_SCL_HDMI_DDCSCL_NAME, GPIO0A_GPIO0A6); + return; +} + +static void hdmi_early_resume(struct early_suspend *h) +{ + hdmi_dbg(hdmi->dev, "hdmi exit early resume\n"); + mutex_lock(&hdmi->enable_mutex); + +// rk2928_mux_api_set(GPIO0A7_I2C3_SDA_HDMI_DDCSDA_NAME, GPIO0A_HDMI_DDCSDA); +// rk2928_mux_api_set(GPIO0A6_I2C3_SCL_HDMI_DDCSCL_NAME, GPIO0A_HDMI_DDCSCL); + + hdmi->suspend = 0; + rk2928_hdmi_initial(); + if(hdmi->enable) { + enable_irq(hdmi->irq); + } + mutex_unlock(&hdmi->enable_mutex); + return; +} +#endif + +static inline void hdmi_io_remap(void) +{ + unsigned int value; + + // Remap HDMI IO Pin +// rk2928_mux_api_set(GPIO0A7_I2C3_SDA_HDMI_DDCSDA_NAME, GPIO0A_HDMI_DDCSDA); +// rk2928_mux_api_set(GPIO0A6_I2C3_SCL_HDMI_DDCSCL_NAME, GPIO0A_HDMI_DDCSCL); +// rk2928_mux_api_set(GPIO0B7_HDMI_HOTPLUGIN_NAME, GPIO0B_HDMI_HOTPLUGIN); + + // Select LCDC0 as video source and enabled. +// value = (HDMI_SOURCE_DEFAULT << 14) | (1 << 30); +// writel(value, GRF_SOC_CON0 + rk2928_GRF_BASE); +} + +static int __devinit rk2928_hdmi_probe (struct platform_device *pdev) +{ + int ret; + struct resource *res; + struct resource *mem; + + hdmi = kmalloc(sizeof(struct hdmi), GFP_KERNEL); + if(!hdmi) + { + dev_err(&pdev->dev, ">>rk30 hdmi kmalloc fail!"); + return -ENOMEM; + } + memset(hdmi, 0, sizeof(struct hdmi)); + hdmi->dev = &pdev->dev; + platform_set_drvdata(pdev, hdmi); + + if(HDMI_SOURCE_DEFAULT == HDMI_SOURCE_LCDC0) + hdmi->lcdc = rk_get_lcdc_drv("lcdc0"); + else + hdmi->lcdc = rk_get_lcdc_drv("lcdc1"); + if(hdmi->lcdc == NULL) + { + dev_err(hdmi->dev, "can not connect to video source lcdc\n"); + ret = -ENXIO; + goto err0; + } + hdmi->xscale = 95; + hdmi->yscale = 95; + + hdmi->hclk = clk_get(NULL,"hclk_hdmi"); + if(IS_ERR(hdmi->hclk)) + { + dev_err(hdmi->dev, "Unable to get hdmi hclk\n"); + ret = -ENXIO; + goto err0; + } + clk_enable(hdmi->hclk); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(hdmi->dev, "Unable to get register resource\n"); + ret = -ENXIO; + goto err0; + } + hdmi->regbase_phy = res->start; + hdmi->regsize_phy = (res->end - res->start) + 1; + mem = request_mem_region(res->start, (res->end - res->start) + 1, pdev->name); + if (!mem) + { + dev_err(hdmi->dev, "failed to request mem region for hdmi\n"); + ret = -ENOENT; + goto err0; + } + + + hdmi->regbase = (int)ioremap(res->start, (res->end - res->start) + 1); + if (!hdmi->regbase) { + dev_err(hdmi->dev, "cannot ioremap registers\n"); + ret = -ENXIO; + goto err1; + } + + ret = rk2928_hdmi_initial(); + if(ret != HDMI_ERROR_SUCESS) + goto err1; + + hdmi_io_remap(); + hdmi_sys_init(); + + hdmi->workqueue = create_singlethread_workqueue("hdmi"); + INIT_DELAYED_WORK(&(hdmi->delay_work), hdmi_work); + + #ifdef CONFIG_HAS_EARLYSUSPEND + hdmi->early_suspend.suspend = hdmi_early_suspend; + hdmi->early_suspend.resume = hdmi_early_resume; + hdmi->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB - 10; + register_early_suspend(&hdmi->early_suspend); + #endif + + hdmi_register_display_sysfs(hdmi, NULL); + #ifdef CONFIG_SWITCH + hdmi->switch_hdmi.name="hdmi"; + switch_dev_register(&(hdmi->switch_hdmi)); + #endif + + spin_lock_init(&hdmi->irq_lock); + mutex_init(&hdmi->enable_mutex); + + /* get the IRQ */ + hdmi->irq = platform_get_irq(pdev, 0); + if(hdmi->irq <= 0) { + dev_err(hdmi->dev, "failed to get hdmi irq resource (%d).\n", hdmi->irq); + ret = -ENXIO; + goto err2; + } + + /* request the IRQ */ + ret = request_irq(hdmi->irq, hdmi_irq, 0, dev_name(&pdev->dev), hdmi); + if (ret) + { + dev_err(hdmi->dev, "hdmi request_irq failed (%d).\n", ret); + goto err2; + } + + hdmi_dbg(hdmi->dev, "rk30 hdmi probe sucess.\n"); + return 0; +err2: + #ifdef CONFIG_SWITCH + switch_dev_unregister(&(hdmi->switch_hdmi)); + #endif + hdmi_unregister_display_sysfs(hdmi); + #ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&hdmi->early_suspend); + #endif + iounmap((void*)hdmi->regbase); +err1: + release_mem_region(res->start,(res->end - res->start) + 1); + clk_disable(hdmi->hclk); +err0: + hdmi_dbg(hdmi->dev, "rk30 hdmi probe error.\n"); + kfree(hdmi); + hdmi = NULL; + return ret; +} + +static int __devexit rk2928_hdmi_remove(struct platform_device *pdev) +{ + if(hdmi) { + mutex_lock(&hdmi->enable_mutex); + if(!hdmi->suspend && hdmi->enable) + disable_irq(hdmi->irq); + mutex_unlock(&hdmi->enable_mutex); + free_irq(hdmi->irq, NULL); + flush_workqueue(hdmi->workqueue); + destroy_workqueue(hdmi->workqueue); + #ifdef CONFIG_SWITCH + switch_dev_unregister(&(hdmi->switch_hdmi)); + #endif + hdmi_unregister_display_sysfs(hdmi); + #ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&hdmi->early_suspend); + #endif + iounmap((void*)hdmi->regbase); + release_mem_region(hdmi->regbase_phy, hdmi->regsize_phy); + clk_disable(hdmi->hclk); + fb_destroy_modelist(&hdmi->edid.modelist); + if(hdmi->edid.audio) + kfree(hdmi->edid.audio); + if(hdmi->edid.specs) + { + if(hdmi->edid.specs->modedb) + kfree(hdmi->edid.specs->modedb); + kfree(hdmi->edid.specs); + } + kfree(hdmi); + hdmi = NULL; + } + printk(KERN_INFO "rk30 hdmi removed.\n"); + return 0; +} + +static void rk2928_hdmi_shutdown(struct platform_device *pdev) +{ + if(hdmi) { + #ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&hdmi->early_suspend); + #endif + } + printk(KERN_INFO "rk30 hdmi shut down.\n"); +} + +static struct platform_driver rk2928_hdmi_driver = { + .probe = rk2928_hdmi_probe, + .remove = __devexit_p(rk2928_hdmi_remove), + .driver = { + .name = "rk30-hdmi", + .owner = THIS_MODULE, + }, + .shutdown = rk2928_hdmi_shutdown, +}; + +static int __init rk2928_hdmi_init(void) +{ + return platform_driver_register(&rk2928_hdmi_driver); +} + +static void __exit rk2928_hdmi_exit(void) +{ + platform_driver_unregister(&rk2928_hdmi_driver); +} + + +//fs_initcall(rk2928_hdmi_init); +module_init(rk2928_hdmi_init); +module_exit(rk2928_hdmi_exit); diff --git a/drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi.h b/drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi.h new file mode 100755 index 000000000000..4695086bf329 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi.h @@ -0,0 +1,11 @@ +#ifndef __RK30_HDMI_H__ +#define __RK30_HDMI_H__ + +#include "../../rk_hdmi.h" + + +extern int rk30_hdmi_register_hdcp_callbacks(void (*hdcp_cb)(void), + void (*hdcp_irq_cb)(int status), + int (*hdcp_power_on_cb)(void), + void (*hdcp_power_off_cb)(void)); +#endif /* __RK30_HDMI_H__ */ diff --git a/drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi_hw.c b/drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi_hw.c new file mode 100755 index 000000000000..4f6e636cc956 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi_hw.c @@ -0,0 +1,456 @@ +#include +#include +#include +#include "rk2928_hdmi.h" +#include "rk2928_hdmi_hw.h" + +static char edid_result = 0; +static bool analog_sync = 0; + +static inline void delay100us(void) +{ + msleep(1); +} + + +static void rk2928_hdmi_av_mute(bool enable) +{ + HDMIWrReg(AV_MUTE, v_AUDIO_MUTE(enable) | v_VIDEO_MUTE(enable)); +} + +static void rk2928_hdmi_sys_power_up(void) +{ + hdmi_dbg(hdmi->dev,"%s \n",__FUNCTION__); + HDMIWrReg(SYS_CTRL, v_REG_CLK_SOURCE_IIS | v_PWR_ON | v_INT_POL_HIGH); +} +static void rk2928_hdmi_sys_power_down(void) +{ + hdmi_dbg(hdmi->dev,"%s \n",__FUNCTION__); + HDMIWrReg(SYS_CTRL, v_REG_CLK_SOURCE_IIS | v_PWR_OFF | v_INT_POL_HIGH); +} + + + +static void rk2928_hdmi_set_pwr_mode(int mode) +{ + hdmi_dbg(hdmi->dev,"%s \n",__FUNCTION__); + if(hdmi->pwr_mode == mode) + return; + switch(mode){ + case NORMAL: + rk2928_hdmi_sys_power_down(); + HDMIWrReg(0xe3, 0x82); + HDMIWrReg(0xe5, 0x00); + HDMIWrReg(0xe4, 0x00); + HDMIWrReg(0xe7, 0x00); + HDMIWrReg(0xe1, 0x8e); + HDMIWrReg(0xce, 0x00); + HDMIWrReg(0xce, 0x01); + rk2928_hdmi_av_mute(1); + rk2928_hdmi_sys_power_up(); + analog_sync = 1; + break; + case LOWER_PWR: + rk2928_hdmi_av_mute(0); + rk2928_hdmi_sys_power_down(); + HDMIWrReg(0xe3, 0x02); + HDMIWrReg(0xe5, 0x1c); + HDMIWrReg(0xe1, 0x8c); + HDMIWrReg(0xe7, 0x04); + HDMIWrReg(0xe4, 0x03); + break; + default: + hdmi_dbg(hdmi->dev,"unkown rk2928 hdmi pwr mode %d\n",mode); + } + hdmi->pwr_mode = mode; +} + + +int rk2928_hdmi_detect_hotplug(void) +{ + int value = HDMIRdReg(HDMI_STATUS); + + hdmi_dbg(hdmi->dev, "[%s] value %02x\n", __FUNCTION__, value); + value &= m_HOTPLUG; + if(value == m_HOTPLUG) + return HDMI_HPD_ACTIVED; + else if(value) + return HDMI_HPD_INSERT; + else + return HDMI_HPD_REMOVED; +} + +#define HDMI_SYS_FREG_CLK 11289600 +#define HDMI_SCL_RATE (100*1000) +#define HDMI_DDC_CONFIG (HDMI_SYS_FREG_CLK>>2)/HDMI_SCL_RATE +#define DDC_BUS_FREQ_L 0x4b +#define DDC_BUS_FREQ_H 0x4c + +int rk2928_hdmi_read_edid(int block, unsigned char *buff) +{ + int value, ret = -1, ddc_bus_freq = 0; + char interrupt = 0, trytime = 2; + unsigned long flags; + + hdmi_dbg(hdmi->dev, "[%s] block %d\n", __FUNCTION__, block); + spin_lock_irqsave(&hdmi->irq_lock, flags); + edid_result = 0; + spin_unlock_irqrestore(&hdmi->irq_lock, flags); + //Before Phy parameter was set, DDC_CLK is equal to PLLA freq which is 30MHz. + //Set DDC I2C CLK which devided from DDC_CLK to 100KHz. + + ddc_bus_freq = HDMI_DDC_CONFIG; + HDMIWrReg(DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF); + HDMIWrReg(DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF); + + // Enable edid interrupt + HDMIWrReg(INTERRUPT_MASK1, m_INT_HOTPLUG | m_INT_EDID_READY); + + while(trytime--) { + // Config EDID block and segment addr + HDMIWrReg(EDID_WORD_ADDR, (block%2) * 0x80); + HDMIWrReg(EDID_SEGMENT_POINTER, block/2); + + value = 10; + while(value--) + { + spin_lock_irqsave(&hdmi->irq_lock, flags); + interrupt = edid_result; + edid_result = 0; + spin_unlock_irqrestore(&hdmi->irq_lock, flags); + if(interrupt & (m_INT_EDID_READY)) + break; + msleep(10); + } + hdmi_dbg(hdmi->dev, "[%s] edid read value %d\n", __FUNCTION__, value); + if(interrupt & m_INT_EDID_READY) + { + for(value = 0; value < HDMI_EDID_BLOCK_SIZE; value++) + buff[value] = HDMIRdReg(EDID_FIFO_ADDR); + ret = 0; + + hdmi_dbg(hdmi->dev, "[%s] edid read sucess\n", __FUNCTION__); +#ifdef HDMI_DEBUG + for(value = 0; value < 128; value++) { + printk("%02x ,", buff[value]); + if( (value + 1) % 16 == 0) + printk("\n"); + } +#endif + break; + }else + hdmi_err(hdmi->dev, "[%s] edid read error\n", __FUNCTION__); + + } + // Disable edid interrupt + HDMIWrReg(INTERRUPT_MASK1, m_INT_HOTPLUG); +// msleep(100); + return ret; +} + +static void rk2928_hdmi_config_avi(unsigned char vic, unsigned char output_color) +{ + int i; + char info[SIZE_AVI_INFOFRAME]; + + memset(info, 0, SIZE_AVI_INFOFRAME); + HDMIWrReg(CONTROL_PACKET_BUF_INDEX, INFOFRAME_AVI); + info[0] = 0x82; + info[1] = 0x02; + info[2] = 0x0D; + info[3] = info[0] + info[1] + info[2]; + info[4] = (AVI_COLOR_MODE_RGB << 5); + info[5] = (AVI_COLORIMETRY_NO_DATA << 6) | (AVI_CODED_FRAME_ASPECT_NO_DATA << 4) | ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME; + info[6] = 0; + info[7] = vic; + info[8] = 0; + + // Calculate AVI InfoFrame ChecKsum + for (i = 4; i < SIZE_AVI_INFOFRAME; i++) + { + info[3] += info[i]; + } + info[3] = 0x100 - info[3]; + + for(i = 0; i < SIZE_AVI_INFOFRAME; i++) + HDMIWrReg(CONTROL_PACKET_ADDR + i, info[i]); +} + +static int rk2928_hdmi_config_video(struct hdmi_video_para *vpara) +{ + int value; + struct fb_videomode *mode; + + hdmi_dbg(hdmi->dev, "[%s]\n", __FUNCTION__); + if(vpara == NULL) { + hdmi_err(hdmi->dev, "[%s] input parameter error\n", __FUNCTION__); + return -1; + } + + if(hdmi->hdcp_power_off_cb) + hdmi->hdcp_power_off_cb(); + // Diable video and audio output + HDMIWrReg(AV_MUTE, v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1)); + + // Input video mode is SDR RGB24bit, Data enable signal from external + HDMIWrReg(VIDEO_CONTRL1, v_VIDEO_INPUT_FORMAT(VIDEO_INPUT_SDR_RGB444) | v_DE_EXTERNAL); + HDMIWrReg(VIDEO_CONTRL2, v_VIDEO_INPUT_BITS(VIDEO_INPUT_8BITS) | (vpara->output_color & 0xFF)); + + // Set HDMI Mode + HDMIWrReg(HDCP_CTRL, v_HDMI_DVI(vpara->output_mode)); + + // Enable or disalbe color space convert + if(vpara->input_color != vpara->output_color) { + value = v_SOF_DISABLE | v_CSC_ENABLE; + } + else + value = v_SOF_DISABLE; + HDMIWrReg(VIDEO_CONTRL3, value); + + // Set ext video +#if 1 + HDMIWrReg(VIDEO_TIMING_CTL, 0); + mode = (struct fb_videomode *)hdmi_vic_to_videomode(vpara->vic); + if(mode == NULL) + { + hdmi_err(hdmi->dev, "[%s] not found vic %d\n", __FUNCTION__, vpara->vic); + return -ENOENT; + } + hdmi->tmdsclk = mode->pixclock; +#else + value = v_EXTERANL_VIDEO(1) | v_INETLACE(mode->vmode); + if(mode->sync & FB_SYNC_HOR_HIGH_ACT) + value |= v_HSYNC_POLARITY(1); + if(mode->sync & FB_SYNC_VERT_HIGH_ACT) + value |= v_VSYNC_POLARITY(1); + HDMIWrReg(VIDEO_TIMING_CTL, value); + + value = mode->left_margin + mode->xres + mode->right_margin + mode->hsync_len; + HDMIWrReg(VIDEO_EXT_HTOTAL_L, value & 0xFF); + HDMIWrReg(VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF); + + value = mode->left_margin + mode->right_margin + mode->hsync_len; + HDMIWrReg(VIDEO_EXT_HBLANK_L, value & 0xFF); + HDMIWrReg(VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF); + + value = mode->left_margin + mode->hsync_len; + HDMIWrReg(VIDEO_EXT_HDELAY_L, value & 0xFF); + HDMIWrReg(VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF); + + value = mode->hsync_len; + HDMIWrReg(VIDEO_EXT_HDURATION_L, value & 0xFF); + HDMIWrReg(VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF); + + value = mode->upper_margin + mode->yres + mode->lower_margin + mode->vsync_len; + HDMIWrReg(VIDEO_EXT_VTOTAL_L, value & 0xFF); + HDMIWrReg(VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF); + + value = mode->upper_margin + mode->vsync_len + mode->lower_margin; + HDMIWrReg(VIDEO_EXT_VBLANK, value & 0xFF); + + if(vpara->vic == HDMI_720x480p_60Hz_4_3 || vpara->vic == HDMI_720x480p_60Hz_16_9) + value = 42; + else + value = mode->upper_margin + mode->vsync_len; + + HDMIWrReg(VIDEO_EXT_VDELAY, value & 0xFF); + + value = mode->vsync_len; + HDMIWrReg(VIDEO_EXT_VDURATION, value & 0xFF); +#endif + + if(vpara->output_mode == OUTPUT_HDMI) { + rk2928_hdmi_config_avi(vpara->vic, vpara->output_color); + hdmi_dbg(hdmi->dev, "[%s] sucess output HDMI.\n", __FUNCTION__); + } + else { + hdmi_dbg(hdmi->dev, "[%s] sucess output DVI.\n", __FUNCTION__); + } + // Power on TMDS + HDMIWrReg(PHY_PRE_EMPHASIS, v_PRE_EMPHASIS(0) | v_TMDS_PWRDOWN(0)); // TMDS power on + + // Enable TMDS + value = HDMIRdReg(PHY_DRIVER); + value |= v_TX_ENABLE(1); + HDMIWrReg(PHY_DRIVER, value); + + return 0; +} + +static void rk2928_hdmi_config_aai(void) +{ + int i; + char info[SIZE_AUDIO_INFOFRAME]; + + memset(info, 0, SIZE_AUDIO_INFOFRAME); + + info[0] = 0x84; + info[1] = 0x01; + info[2] = 0x0A; + + info[3] = info[0] + info[1] + info[2]; + for (i = 4; i < SIZE_AUDIO_INFOFRAME; i++) + info[3] += info[i]; + + info[3] = 0x100 - info[3]; + + HDMIWrReg(CONTROL_PACKET_BUF_INDEX, INFOFRAME_AAI); + for(i = 0; i < SIZE_AUDIO_INFOFRAME; i++) + HDMIWrReg(CONTROL_PACKET_ADDR + i, info[i]); +} + +static int rk2928_hdmi_config_audio(struct hdmi_audio *audio) +{ + int rate, N, channel, mclk_fs; + + if(audio->channel < 3) + channel = I2S_CHANNEL_1_2; + else if(audio->channel < 5) + channel = I2S_CHANNEL_3_4; + else if(audio->channel < 7) + channel = I2S_CHANNEL_5_6; + else + channel = I2S_CHANNEL_7_8; + + switch(audio->rate) + { + case HDMI_AUDIO_FS_32000: + rate = AUDIO_32K; + N = N_32K; + mclk_fs = MCLK_384FS; + break; + case HDMI_AUDIO_FS_44100: + rate = AUDIO_441K; + N = N_441K; + mclk_fs = MCLK_256FS; + break; + case HDMI_AUDIO_FS_48000: + rate = AUDIO_48K; + N = N_48K; + mclk_fs = MCLK_256FS; + break; + case HDMI_AUDIO_FS_88200: + rate = AUDIO_882K; + N = N_882K; + mclk_fs = MCLK_128FS; + break; + case HDMI_AUDIO_FS_96000: + rate = AUDIO_96K; + N = N_96K; + mclk_fs = MCLK_128FS; + break; + case HDMI_AUDIO_FS_176400: + rate = AUDIO_1764K; + N = N_1764K; + mclk_fs = MCLK_128FS; + break; + case HDMI_AUDIO_FS_192000: + rate = AUDIO_192K; + N = N_192K; + mclk_fs = MCLK_128FS; + break; + default: + dev_err(hdmi->dev, "[%s] not support such sample rate %d\n", __FUNCTION__, audio->rate); + return -ENOENT; + } + + //set_audio source I2S + HDMIWrReg(AUDIO_CTRL1, 0x00); //internal CTS, disable down sample, i2s input, disable MCLK + HDMIWrReg(AUDIO_SAMPLE_RATE, rate); + HDMIWrReg(AUDIO_I2S_MODE, v_I2S_MODE(I2S_STANDARD) | v_I2S_CHANNEL(channel) ); + HDMIWrReg(AUDIO_I2S_MAP, 0x00); + HDMIWrReg(AUDIO_I2S_SWAPS_SPDIF, 0); // no swap + + //Set N value + HDMIWrReg(AUDIO_N_H, (N >> 16) & 0x0F); + HDMIWrReg(AUDIO_N_M, (N >> 8) & 0xFF); + HDMIWrReg(AUDIO_N_L, N & 0xFF); + rk2928_hdmi_config_aai(); + + return 0; +} + +static void rk2928_hdmi_control_output(int enable) +{ + char mutestatus = 0; + + if(enable) { + mutestatus = HDMIRdReg(AV_MUTE); + if(mutestatus && (m_AUDIO_MUTE | m_VIDEO_BLACK)) { + HDMIWrReg(AV_MUTE, v_AUDIO_MUTE(0) | v_VIDEO_MUTE(0)); + rk2928_hdmi_sys_power_up(); + rk2928_hdmi_sys_power_down(); + rk2928_hdmi_sys_power_up(); + if(analog_sync){ + HDMIWrReg(0xce, 0x00); + delay100us(); + HDMIWrReg(0xce, 0x01); + analog_sync = 0; + } + } + } + else { + HDMIWrReg(AV_MUTE, v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1)); + } +} + +int rk2928_hdmi_removed(void) +{ + + dev_printk(KERN_INFO , hdmi->dev , "Removed.\n"); + rk2928_hdmi_set_pwr_mode(LOWER_PWR); + + return HDMI_ERROR_SUCESS; +} + + +irqreturn_t hdmi_irq(int irq, void *priv) +{ + char interrupt1 = 0; + unsigned long flags; + spin_lock_irqsave(&hdmi->irq_lock,flags); + interrupt1 = HDMIRdReg(INTERRUPT_STATUS1); + HDMIWrReg(INTERRUPT_STATUS1, interrupt1); +#if 1 + hdmi_dbg(hdmi->dev, "[%s] interrupt1 %02x interrupt2 %02x \n",\ + __FUNCTION__, interrupt1, interrupt2); +#endif + if(interrupt1 & m_INT_HOTPLUG ){ + if(hdmi->state == HDMI_SLEEP) + hdmi->state = WAIT_HOTPLUG; + if(hdmi->pwr_mode == LOWER_PWR) + rk2928_hdmi_set_pwr_mode(NORMAL); + queue_delayed_work(hdmi->workqueue, &hdmi->delay_work, msecs_to_jiffies(10)); + }else if(interrupt1 & m_INT_EDID_READY) { + edid_result = interrupt1; + }else if(hdmi->state == HDMI_SLEEP) { + hdmi_dbg(hdmi->dev, "hdmi return to sleep mode\n"); + rk2928_hdmi_set_pwr_mode(LOWER_PWR); + } +#if 0 + if(hdmi->hdcp_irq_cb) + hdmi->hdcp_irq_cb(interrupt2); +#endif + spin_unlock_irqrestore(&hdmi->irq_lock,flags); + return IRQ_HANDLED; +} + +int rk2928_hdmi_initial(void) +{ + int rc = HDMI_ERROR_SUCESS; + + hdmi->pwr_mode = NORMAL; + hdmi->hdmi_removed = rk2928_hdmi_removed ; + hdmi->control_output = rk2928_hdmi_control_output; + hdmi->config_video = rk2928_hdmi_config_video; + hdmi->config_audio = rk2928_hdmi_config_audio; + hdmi->detect_hotplug = rk2928_hdmi_detect_hotplug; + hdmi->read_edid = rk2928_hdmi_read_edid; + // internal hclk = hdmi_hclk/20 + //HDMIWrReg(0x800, HDMI_INTERANL_CLK_DIV); + + if(hdmi->hdcp_power_on_cb) + rc = hdmi->hdcp_power_on_cb(); + + return rc; +} diff --git a/drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi_hw.h b/drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi_hw.h new file mode 100644 index 000000000000..6fd0f957a1ad --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi_hw.h @@ -0,0 +1,248 @@ +#ifndef _RK2928_HDMI_HW_H +#define _RK2928_HDMI_HW_H + +enum PWR_MODE{ + NORMAL, + LOWER_PWR, +}; +enum { + OUTPUT_DVI = 0, + OUTPUT_HDMI + }; +#define SYS_CTRL 0x00 + #define m_INT_POL (1 << 0) + #define m_POWER (1 << 1) + #define m_REG_CLK_SOURCE (1 << 2) + #define v_INT_POL_HIGH 1 + #define v_INT_POL_LOW 0 + #define v_PWR_ON (0 << 1) + #define v_PWR_OFF (1 << 1) + #define v_REG_CLK_SOURCE_TMDS (0 << 2) + #define v_REG_CLK_SOURCE_IIS (1 << 2) +#define VIDEO_CONTRL1 0x01 + #define m_VIDEO_INPUT_FORMAT (7 << 1) + #define m_DE_SOURCE (1 << 0) + enum { + VIDEO_INPUT_SDR_RGB444 = 0, + VIDEO_INPUT_DDR_RGB444 = 5, + VIDEO_INPUT_DDR_YCBCR422 = 6 + }; + #define v_VIDEO_INPUT_FORMAT(n) (n << 1) + #define v_DE_EXTERNAL 1 + #define v_DE_INTERANL 0 + +#define VIDEO_CONTRL2 0x02 + #define m_VIDEO_OUTPUT_FORMAT (3 << 6) + #define m_VIDEO_INPUT_BITS (3 << 4) + #define v_VIDEO_OUTPUT_FORMAT(n)(n << 6) + #define v_VIDEO_INPUT_BITS(n) (n << 4) + enum{ + VIDEO_INPUT_12BITS = 0, + VIDEO_INPUT_10BITS, + VIDEO_INPUT_8BITS + }; +#define VIDEO_CONTRL3 0x04 + #define m_SOF (1 << 3) + #define m_CSC (1 << 0) + #define v_SOF_ENABLE (0 << 3) + #define v_SOF_DISABLE (1 << 3) + #define v_CSC_ENABLE 1 + #define v_CSC_DISABLE 0 + +#define AV_MUTE 0x05 + #define m_AVMUTE_CLEAR (1 << 7) + #define m_AVMUTE_ENABLE (1 << 6) + #define m_AUDIO_MUTE (1 << 1) + #define m_VIDEO_BLACK (1 << 0) + #define v_AUDIO_MUTE(n) (n << 1) + #define v_VIDEO_MUTE(n) (n << 0) + +#define VIDEO_TIMING_CTL 0x08 + #define v_HSYNC_POLARITY(n) (n << 3) + #define v_VSYNC_POLARITY(n) (n << 2) + #define v_INETLACE(n) (n << 1) + #define v_EXTERANL_VIDEO(n) (n << 0) + +#define VIDEO_EXT_HTOTAL_L 0x09 +#define VIDEO_EXT_HTOTAL_H 0x0a +#define VIDEO_EXT_HBLANK_L 0x0b +#define VIDEO_EXT_HBLANK_H 0x0c +#define VIDEO_EXT_HDELAY_L 0x0d +#define VIDEO_EXT_HDELAY_H 0x0e +#define VIDEO_EXT_HDURATION_L 0x0f +#define VIDEO_EXT_HDURATION_H 0x10 +#define VIDEO_EXT_VTOTAL_L 0x11 +#define VIDEO_EXT_VTOTAL_H 0x12 +#define VIDEO_EXT_VBLANK 0x13 +#define VIDEO_EXT_VDELAY 0x14 +#define VIDEO_EXT_VDURATION 0x15 + +#define AUDIO_CTRL1 0x35 + enum { + CTS_SOURCE_INTERNAL = 0, + CTS_SOURCE_EXTERNAL + }; + #define v_CTS_SOURCE(n) (n << 7) + enum { + DOWNSAMPLE_DISABLE = 0, + DOWNSAMPLE_1_2, + DOWNSAMPLE_1_4 + }; + #define v_DOWN_SAMPLE(n) (n << 5) + enum { + AUDIO_SOURCE_IIS = 0, + AUDIO_SOURCE_SPDIF + }; + #define v_AUDIO_SOURCE(n) (n << 3) + #define v_MCLK_ENABLE(n) (n << 2) + enum { + MCLK_128FS = 0, + MCLK_256FS, + MCLK_384FS, + MCLK_512FS + }; + #define v_MCLK_RATIO(n) (n) + +#define AUDIO_SAMPLE_RATE 0x37 + enum { + AUDIO_32K = 0x3, + AUDIO_441K = 0x0, + AUDIO_48K = 0x2, + AUDIO_882K = 0x8, + AUDIO_96K = 0xa, + AUDIO_1764K = 0xc, + AUDIO_192K = 0xe, + }; + +#define AUDIO_I2S_MODE 0x38 + enum { + I2S_CHANNEL_1_2 = 1, + I2S_CHANNEL_3_4 = 3, + I2S_CHANNEL_5_6 = 7, + I2S_CHANNEL_7_8 = 0xf + }; + #define v_I2S_CHANNEL(n) ((n) << 2) + enum { + I2S_STANDARD = 0, + I2S_LEFT_JUSTIFIED, + I2S_RIGHT_JUSTIFIED + }; + #define v_I2S_MODE(n) (n) + +#define AUDIO_I2S_MAP 0x39 +#define AUDIO_I2S_SWAPS_SPDIF 0x3a + #define v_SPIDF_FREQ(n) (n) + +#define N_32K 0x1000 +#define N_441K 0x1880 +#define N_882K 0x3100 +#define N_1764K 0x6200 +#define N_48K 0x1800 +#define N_96K 0x3000 +#define N_192K 0x6000 + +#define AUDIO_N_H 0x3f +#define AUDIO_N_M 0x40 +#define AUDIO_N_L 0x41 + +#define AUDIO_CTS_H 0x45 +#define AUDIO_CTS_M 0x46 +#define AUDIO_CTS_L 0x47 + + +#define DDC_CLK_L 0x4b +#define DDC_CLK_H 0x4c + +#define EDID_SEGMENT_POINTER 0x4d +#define EDID_WORD_ADDR 0x4e +#define EDID_FIFO_OFFSET 0x4f +#define EDID_FIFO_ADDR 0x50 + +/* CONTROL_PACKET_BUF_INDEX */ +#define CONTROL_PACKET_BUF_INDEX 0x9f +enum { + INFOFRAME_AVI = 0x06, + INFOFRAME_AAI = 0x08 +}; +#define CONTROL_PACKET_ADDR 0xa0 + + +#define SIZE_AVI_INFOFRAME 0x11 // 14 bytes +#define SIZE_AUDIO_INFOFRAME 0x0F // 15 bytes +enum { + AVI_COLOR_MODE_RGB = 0, + AVI_COLOR_MODE_YCBCR422, + AVI_COLOR_MODE_YCBCR444 +}; +enum { + AVI_COLORIMETRY_NO_DATA = 0, + AVI_COLORIMETRY_SMPTE_170M, + AVI_COLORIMETRY_ITU709, + AVI_COLORIMETRY_EXTENDED +}; +enum { + AVI_CODED_FRAME_ASPECT_NO_DATA, + AVI_CODED_FRAME_ASPECT_4_3, + AVI_CODED_FRAME_ASPECT_16_9 +}; +enum { + ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME = 0x08, + ACTIVE_ASPECT_RATE_4_3, + ACTIVE_ASPECT_RATE_16_9, + ACTIVE_ASPECT_RATE_14_9 +}; + +#define HDCP_CTRL 0x52 + #define m_HDMI_DVI (1 << 1) + #define v_HDMI_DVI(n) (n << 1) + +#define INTERRUPT_MASK1 0xc0 +#define INTERRUPT_STATUS1 0xc1 + #define m_INT_HOTPLUG (1 << 7) + #define m_INT_ACTIVE_VSYNC (1 << 6) + #define m_INT_EDID_READY (1 << 2) + +#define INTERRUPT_MASK2 0xc2 +#define INTERRUPT_STATUS2 0xc3 + #define m_INT_HDCP_ERR (1 << 7) + #define m_INT_BKSV_FLAG (1 << 6) + #define m_INT_HDCP_OK (1 << 4) + +#define HDMI_STATUS 0xc8 + #define m_HOTPLUG (1 << 7) + #define m_DDC_SDA (1 << 5) + #define m_DDC_SDC (1 << 4) + +#define PHY_SYNC 0xce //sync phy parameter + +#define PHY_DRIVER 0xe1 + #define v_MAIN_DRIVER(n) (n << 4) + #define v_PRE_DRIVER(n) (n << 2) + #define v_TX_ENABLE(n) (n << 1) + +#define PHY_PRE_EMPHASIS 0xe2 + #define v_PRE_EMPHASIS(n) (n << 4) + #define v_TMDS_PWRDOWN(n) (n) + +#define PHY_PLL_TEST 0xe3 +#define PHY_BANDGAP_PWR 0xe4 + #define v_BANDGAP_PWR_DOWN 0x03 + #define v_BANDGAP_PWR_UP 0 + +#define PHY_PLL_CTRL 0xe5 + #define v_PLL_DISABLE(n) (n << 4) + #define v_PLL_RESET(n) (n << 3) + #define v_TMDS_RESET(n) (n << 2) + +#define PHY_PLL_LDO_PWR 0xe7 + #define v_LDO_PWR_DOWN(n) (n << 2) + +#define HDMIRdReg(addr) __raw_readl(hdmi->regbase + addr) +#define HDMIWrReg(addr, val) __raw_writel((val), hdmi->regbase + addr); +#define HDMIMskReg(temp, addr, msk, val) \ + temp = __raw_readl(hdmi->regbase + addr) & (0xFF - (msk)) ; \ + __raw_writel(temp | ( (val) & (msk) ), hdmi->regbase + addr); + +extern int rk2928_hdmi_initial(void); + +#endif diff --git a/drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi_hw.hbak b/drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi_hw.hbak new file mode 100755 index 000000000000..fe06fe040278 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi_hw.hbak @@ -0,0 +1,497 @@ +#ifndef __RK2928_HDMI_HW_H__ +#define __RK2928_HDMI_HW_H__ + + +//0x00 +#define INT_POL 1 +#define SYS_PWR_ON 0 +#define SYS_PWR_OFF 1 +#define PHY_CLK 0 +#define SYS_CLK 1 + +#define MCLK_FS 0x01 //256fs +//0x01 +// INPUT_VIDEO_FORMAT +#define RGB_YUV444 0x00 +#define DDR_RGB444_YUV444 0x05 +#define DDR_YUV422 0x06 + +//0x02 +//video output format +#define RGB444 0x00 +#define YUV444 0x01 +#define YUV422 0x02 + +//DATA WIDTH +#define DATA_12BIT 0X00 +#define DATA_10BIT 0X01 +#define DATA_8BIT 0X03 + +//0X04 +//1:after 0:not After 1st sof for external DE sample +#define DE_AFTER_SOF 0 +#define DE_NOAFTER_SOF 1 + +#define CSC_ENABLE 0 +#define CSC_DISABLE 1 + +//0X05 +#define CLEAR_AVMUTE(x) (x)<<7 +#define SET_AVMUTE(x) (x)<<6 +#define AUDIO_MUTE(x) (x)<<1 +#define VIDEO_BLACK(x) (x)<<0 //1:black 0:normal + +//0x08 +#define VSYNC_POL(x) (x)<<3 //0:Negative 1:Positive +#define HSYNC_POL(x) (x)<<2 //0:Negative 1:Positive +#define INTER_PROGRESSIVE(x) (x)<<1 //0: progressive 1:interlace +#define VIDEO_SET_ENABLE(x) (x)<<0 //0:disable 1: enable + +/* 0xe1 */ +//Main-driver strength :0000~1111: the strength from low to high +#define M_DRIVER_STR(x) (((x)&0xf)<<4) +//Pre-driver strength :00~11: the strength from low to high +#define P_DRIVER_STR(x) (((x)&3)<<2) +//TX driver enable 1: enable 0: disable +#define TX_DRIVER_EN(x) (((x)&1)<<1) +/* 0xe2 */ +//Pre-emphasis strength 00~11: the strength from 0 to high +#define P_EMPHASIS_STR(x) (((x)&3)<<4) +//Power down TMDS driver 1: power down. 0: not +#define PWR_DOWN_TMDS(x) (((x)&1)<<0) +/* 0xe3 */ +//PLL out enable. Just for test. need set to 1’b0 +#define PLL_OUT_EN(x) (((x)&1)<<7) +/* 0xe4 */ +// Band-Gap power down 11: power down 00: not +#define BAND_PWR(x) (((x)&3)<<0) +/* 0xe5 */ +//PLL disable 1: disable 0: enable +#define PLL_PWR(x) (((x)&1)<<4) +// PLL reset 1: reset 0: not +#define PLL_RST(x) (((x)&1)<<3) +//PHY TMDS channels reset 1: reset 0: not +#define TMDS_RST(x) (((x)&1)<<2) +/* 0xe7 */ +// PLL LDO power down 1: power down 0: not +#define PLL_LDO_PWR(x) (((x)&1)<<2) + +enum PWR_MODE{ + NORMAL, + LOWER_PWR, +}; + +#if 0 +/* HDMI_SYS_CONTROL */ +#define SYS_CTRL 0x0 + +enum { + PWR_SAVE_MODE_A = 1, + PWR_SAVE_MODE_B = 2, + PWR_SAVE_MODE_D = 4, + PWR_SAVE_MODE_E = 8 +}; +#define m_PWR_SAVE_MODE 0xF0 +#define v_PWR_SAVE_MODE(n) (n << 4) +#define PLL_B_RESET (1 << 3) + +#define N_32K 0x1000 +#define N_441K 0x1880 +#define N_882K 0x3100 +#define N_1764K 0x6200 +#define N_48K 0x1800 +#define N_96K 0x3000 +#define N_192K 0x6000 + +#define LR_SWAP_N3 0x04 +#define N_2 0x08 +#define N_1 0x0c + +#define AUDIO_CTRL1 0x28 +#define AUDIO_CTRL2 0x2c +#define I2S_AUDIO_CTRL 0x30 +enum { + I2S_MODE_STANDARD = 0, + I2S_MODE_RIGHT_JUSTIFIED, + I2S_MODE_LEFT_JUSTIFIED +}; +#define v_I2S_MODE(n) n +enum { + I2S_CHANNEL_1_2 = 1, + I2S_CHANNEL_3_4 = 3, + I2S_CHANNEL_5_6 = 7, + I2S_CHANNEL_7_8 = 0xf +}; +#define v_I2S_CHANNEL(n) ( (n) << 2 ) + +#define I2S_INPUT_SWAP 0x40 + +#define SRC_NUM_AUDIO_LEN 0x50 + +/* HDMI_AV_CTRL1*/ +#define AV_CTRL1 0x54 +enum { + AUDIO_32K = 0x3, + AUDIO_441K = 0x0, + AUDIO_48K = 0x2, + AUDIO_882K = 0x8, + AUDIO_96K = 0xa, + AUDIO_1764K = 0xc, + AUDIO_192K = 0xe, +}; +#define m_AUDIO_SAMPLE_RATE 0xF0 +#define v_AUDIO_SAMPLE_RATE(n) (n << 4) +#define m_INPUT_VIDEO_MODE (7 << 1) +#define v_INPUT_VIDEO_MODE(n) (n << 1) +enum { + INTERNAL_DE = 0, + EXTERNAL_DE +}; +#define m_DE_SIGNAL_SELECT (1 << 0) + +/* HDMI_AV_CTRL2 */ +#define AV_CTRL2 0xec +#define m_CSC_ENABLE (1 << 0) +#define v_CSC_ENABLE(n) (n) + +/* HDMI_VIDEO_CTRL1 */ +#define VIDEO_CTRL1 0x58 + +#define m_VIDEO_OUTPUT_MODE (0x3 << 6) +#define v_VIDEO_OUTPUT_MODE(n) (n << 6) +enum { + VIDEO_INPUT_DEPTH_12BIT = 0, + VIDEO_INPUT_DEPTH_10BIT = 0x1, + VIDEO_INPUT_DEPTH_8BIT = 0x3 +}; +#define m_VIDEO_INPUT_DEPTH (3 << 4) +#define v_VIDEO_INPUT_DEPTH(n) (n << 4) +enum { + VIDEO_EMBEDDED_SYNC_LOCATION_0 = 0, + VIDEO_EMBEDDED_SYNC_LOCATION_1, + VIDEO_EMBEDDED_SYNC_LOCATION_2 +}; +#define m_VIDEO_EMBEDDED_SYNC_LOCATION (3 << 2) +#define VIDEO_EMBEDDED_SYNC_LOCATION(n) (n << 2) +#define m_VIDEO_INPUT_COLOR_MODE (1 << 0) + +/* DEEP_COLOR_MODE */ +#define DEEP_COLOR_MODE 0x5c +enum{ + TMDS_CLOCK_MODE_8BIT = 0, + TMDS_CLOKK_MODE_10BIT, + TMDS_CLOKK_MODE_12BIT +}; +#define TMDS_CLOCK_MODE_MASK 0x3 << 6 +#define TMDS_CLOCK_MODE(n) (n) << 6 + +/* VIDEO_CTRL2 */ +#define VIDEO_SETTING2 0x114 +#define m_UNMUTE (1 << 7) +#define m_MUTE (1 << 6) +#define m_AUDIO_RESET (1 << 2) +#define m_NOT_SEND_AUDIO (1 << 1) +#define m_NOT_SEND_VIDEO (1 << 0) +#define AV_UNMUTE (1 << 7) // Unmute video and audio, send normal video and audio data +#define AV_MUTE (1 << 6) // Mute video and audio, send black video data and silent audio data +#define AUDIO_CAPTURE_RESET (1 << 2) // Reset audio process logic, only available in pwr_e mode. +#define NOT_SEND_AUDIO (1 << 1) // Send silent audio data +#define NOT_SEND_VIDEO (1 << 0) // Send black video data + +/* Color Space Convertion Parameter*/ +#define CSC_PARA_C0_H 0x60 +#define CSC_PARA_C0_L 0x64 +#define CSC_PARA_C1_H 0x68 +#define CSC_PARA_C1_L 0x6c +#define CSC_PARA_C2_H 0x70 +#define CSC_PARA_C2_L 0x74 +#define CSC_PARA_C3_H 0x78 +#define CSC_PARA_C3_L 0x7c +#define CSC_PARA_C4_H 0x80 +#define CSC_PARA_C4_L 0x84 +#define CSC_PARA_C5_H 0x88 +#define CSC_PARA_C5_L 0x8c +#define CSC_PARA_C6_H 0x90 +#define CSC_PARA_C6_L 0x94 +#define CSC_PARA_C7_H 0x98 +#define CSC_PARA_C7_L 0x9c +#define CSC_PARA_C8_H 0xa0 +#define CSC_PARA_C8_L 0xa4 +#define CSC_PARA_C9_H 0xa8 +#define CSC_PARA_C9_L 0xac +#define CSC_PARA_C10_H 0xac +#define CSC_PARA_C10_L 0xb4 +#define CSC_PARA_C11_H 0xb8 +#define CSC_PARA_C11_L 0xbc + +#define CSC_CONFIG1 0x34c +#define m_CSC_MODE (1 << 7) +#define m_CSC_COEF_MODE (0xF << 3) //Only used in auto csc mode +#define m_CSC_STATUS (1 << 2) +#define m_CSC_VID_SELECT (1 << 1) +#define m_CSC_BRSWAP_DIABLE (1) + +enum { + CSC_MODE_MANUAL = 0, + CSC_MODE_AUTO +}; +#define v_CSC_MODE(n) (n << 7) +enum { + COE_SDTV_LIMITED_RANGE = 0x08, + COE_SDTV_FULL_RANGE = 0x04, + COE_HDTV_60Hz = 0x2, + COE_HDTV_50Hz = 0x1 +}; +#define v_CSC_COE_MODE(n) (n << 3) +enum { + CSC_INPUT_VID_5_19 = 0, + CSC_INPUT_VID_28_29 +}; +#define v_CSC_VID_SELECT(n) (n << 1) +#define v_CSC_BRSWAP_DIABLE(n) (n) +#endif +/* CONTROL_PACKET_BUF_INDEX */ +#define CONTROL_PACKET_BUF_INDEX 0x17c +enum { + INFOFRAME_AVI = 0x06, + INFOFRAME_AAI = 0x08 +}; +#define CONTROL_PACKET_HB0 0x180 +#define CONTROL_PACKET_HB1 0x184 +#define CONTROL_PACKET_HB2 0x188 +#define CONTROL_PACKET_PB_ADDR 0x18c +#define SIZE_AVI_INFOFRAME 0x11 // 17 bytes +#define SIZE_AUDIO_INFOFRAME 0x0F // 15 bytes +enum { + AVI_COLOR_MODE_RGB = 0, + AVI_COLOR_MODE_YCBCR422, + AVI_COLOR_MODE_YCBCR444 +}; +enum { + AVI_COLORIMETRY_NO_DATA = 0, + AVI_COLORIMETRY_SMPTE_170M, + AVI_COLORIMETRY_ITU709, + AVI_COLORIMETRY_EXTENDED +}; +enum { + AVI_CODED_FRAME_ASPECT_NO_DATA, + AVI_CODED_FRAME_ASPECT_4_3, + AVI_CODED_FRAME_ASPECT_16_9 +}; +enum { + ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME = 0x08, + ACTIVE_ASPECT_RATE_4_3, + ACTIVE_ASPECT_RATE_16_9, + ACTIVE_ASPECT_RATE_14_9 +}; +#if 0 +/* External Video Parameter Setting*/ +#define EXT_VIDEO_PARA 0xC0 +#define m_VSYNC_OFFSET (0xF << 4) +#define m_VSYNC_POLARITY (1 << 3) +#define m_HSYNC_POLARITY (1 << 2) +#define m_INTERLACE (1 << 1) +#define m_EXT_VIDEO_ENABLE (1 << 0) + +#define v_VSYNC_OFFSET(n) (n << 4) +#define v_VSYNC_POLARITY(n) (n << 3) +#define v_HSYNC_POLARITY(n) (n << 2) +#define v_INTERLACE(n) (n << 1) +#define v_EXT_VIDEO_ENABLE(n) (n << 0) + +#define EXT_VIDEO_PARA_HTOTAL_L 0xC4 +#define EXT_VIDEO_PARA_HTOTAL_H 0xC8 +#define EXT_VIDEO_PARA_HBLANK_L 0xCC +#define EXT_VIDEO_PARA_HBLANK_H 0xD0 +#define EXT_VIDEO_PARA_HDELAY_L 0xD4 +#define EXT_VIDEO_PARA_HDELAY_H 0xD8 +#define EXT_VIDEO_PARA_HSYNCWIDTH_L 0xDC +#define EXT_VIDEO_PARA_HSYNCWIDTH_H 0xE0 + +#define EXT_VIDEO_PARA_VTOTAL_L 0xE4 +#define EXT_VIDEO_PARA_VTOTAL_H 0xE8 +#define EXT_VIDEO_PARA_VBLANK_L 0xF4 +#define EXT_VIDEO_PARA_VDELAY 0xF8 +#define EXT_VIDEO_PARA_VSYNCWIDTH 0xFC + +#define PHY_PLL_SPEED 0x158 + #define v_TEST_EN(n) (n << 6) + #define v_PLLA_BYPASS(n) (n << 4) + #define v_PLLB_SPEED(n) (n << 2) + #define v_PLLA_SPEED(n) (n) + enum { + PLL_SPEED_LOWEST = 0, + PLL_SPEED_MIDLOW, + PLL_SPEED_MIDHIGH, + PLL_SPEED_HIGHEST + }; + +#define PHY_PLL_17 0x15c // PLL A & B config bit 17 + #define v_PLLA_BIT17(n) (n << 2) + #define v_PLLB_BIT17(n) (n << 1) + +#define PHY_BGR 0x160 + #define v_BGR_DISCONNECT(n) (n << 7) + #define v_BGR_V_OFFSET(n) (n << 4) + #define v_BGR_I_OFFSET(n) (n) + +#define PHY_PLLA_1 0x164 +#define PHY_PLLA_2 0x168 +#define PHY_PLLB_1 0x16c +#define PHY_PLLB_2 0x170 + +#define PHY_DRIVER_PREEMPHASIS 0x174 + #define v_TMDS_SWING(n) (n << 4) + #define v_PRE_EMPHASIS(n) (n) + +#define PHY_PLL_16_AML 0x178 // PLL A & B config bit 16 and AML control + #define v_PLLA_BIT16(n) (n << 5) + #define v_PLLB_BIT16(n) (n << 4) + #define v_AML(n) (n) +#endif +/* Interrupt Setting */ +#define INTR_MASK1 0xc0 +#define INTR_STATUS1 0xc1 + #define m_INT_HOTPLUG (1 << 7) + #define m_INT_VSYNC (1 << 5) + #define m_INT_EDID_READY (1 << 2) + +#define INTR_MASK2 0xc2 +#define INTR_STATUS2 0xc3 + #define m_INT_HDCP_ERR (1 << 7) // HDCP error detected + #define m_INT_BKSV_RPRDY (1 << 6) // BKSV list ready from repeater + #define m_INT_AUTH_DONE (1 << 4) // HDCP authentication done + +#if 0 +#define DDC_READ_FIFO_ADDR 0x200 +#define DDC_BUS_FREQ_L 0x204 +#define DDC_BUS_FREQ_H 0x208 +#define DDC_BUS_CTRL 0x2dc +#define DDC_I2C_LEN 0x278 +#define DDC_I2C_OFFSET 0x280 +#define DDC_I2C_CTRL 0x284 +#define DDC_I2C_READ_BUF0 0x288 +#define DDC_I2C_READ_BUF1 0x28c +#define DDC_I2C_READ_BUF2 0x290 +#define DDC_I2C_READ_BUF3 0x294 +#define DDC_I2C_WRITE_BUF0 0x298 +#define DDC_I2C_WRITE_BUF1 0x29c +#define DDC_I2C_WRITE_BUF2 0x2a0 +#define DDC_I2C_WRITE_BUF3 0x2a4 +#define DDC_I2C_WRITE_BUF4 0x2ac +#define DDC_I2C_WRITE_BUF5 0x2b0 +#define DDC_I2C_WRITE_BUF6 0x2b4 + +#endif +#define EDID_SEGMENT_POINTER 0x4d +#define EDID_WORD_ADDR 0x4e +#define EDID_FIFO_ADDR 0x4f + +#define PIN_STATUS 0xc8 +#define m_HOTPLUG_STATUS (1 << 7) +#define m_DDCSDA_STATUS (1 << 5) +#define m_DDCSCL_STATUS (1 << 4) +#if 0 +/* HDCP_CTRL */ +#define HDCP_CTRL 0x2bc + enum { + OUTPUT_DVI = 0, + OUTPUT_HDMI + }; + #define m_HDCP_AUTH_START (1 << 7) // start hdcp + #define m_HDCP_BKSV_PASS (1 << 6) // bksv valid + #define m_HDCP_BKSV_FAILED (1 << 5) // bksv invalid + #define m_HDCP_FRAMED_ENCRYPED (1 << 4) + #define m_HDCP_AUTH_STOP (1 << 3) // stop hdcp + #define m_HDCP_ADV_CIPHER (1 << 2) // advanced cipher mode + #define m_HDMI_DVI (1 << 1) + #define m_HDCP_RESET (1 << 0) // reset hdcp + #define v_HDCP_AUTH_START(n) (n << 7) + #define v_HDCP_BKSV_PASS(n) (n << 6) + #define v_HDCP_BKSV_FAILED(n) (n << 5) + #define v_HDCP_FRAMED_ENCRYPED(n) (n << 4) + #define v_HDCP_AUTH_STOP(n) (n << 3) + #define v_HDCP_ADV_CIPHER(n) (n << 2) + #define v_HDMI_DVI(n) (n << 1) + #define v_HDCP_RESET(n) (n << 0) +#define HDCP_CTRL2 0x340 + +/* HDCP Key Memory Access Control */ +#define HDCP_KEY_ACCESS_CTRL1 0x338 +#define HDCP_KEY_ACCESS_CTRL2 0x33c + #define m_LOAD_FACSIMILE_HDCP_KEY (1 << 1) + #define m_LOAD_HDCP_KEY (1 << 0) +/* HDCP Key Memory Control */ +#define HDCP_KEY_MEM_CTRL 0x348 + #define m_USE_KEY1 (1 << 6) + #define m_USE_KEY2 (1 << 5) + #define m_LOAD_AKSV (1 << 4) + #define m_KSV_SELECTED (1 << 3) + #define m_KSV_VALID (1 << 2) + #define m_KEY_VALID (1 << 1) + #define m_KEY_READY (1 << 0) + #define v_USE_KEY1(n) (n << 6) + #define v_USE_KEY2(n) (n << 5) + #define v_LOAD_AKSV(n) (n << 4) + +/* HDCP B device capacity */ +#define HDCP_BCAPS 0x2f8 + #define m_HDMI_RECEIVED (1 << 7) //If HDCP receiver support HDMI, this bit must be 1. + #define m_REPEATER (1 << 6) + #define m_KSV_FIFO_READY (1 << 5) + #define m_DDC_FAST (1 << 4) + #define m_1_1_FEATURE (1 << 1) + #define m_FAST_REAUTHENTICATION (1 << 0) //For HDMI, this function is supported whether this bit is enabled or not. + +/* HDCP KSV Value */ +#define HDCP_KSV_BYTE0 0x2fc +#define HDCP_KSV_BYTE1 0x300 +#define HDCP_KSV_BYTE2 0x304 +#define HDCP_KSV_BYTE3 0x308 +#define HDCP_KSV_BYTE4 0x30c + +/* HDCP error status */ +#define HDCP_ERROR 0x320 + +/* HDCP 100 ms timer */ +#define HDCP_TIMER_100MS 0x324 +/* HDCP 5s timer */ +#define HDCP_TIMER_5S 0x328 + +/* HDCP Key ram address */ +#define HDCP_RAM_KEY_KSV1 0x400 +#define HDCP_RAM_KEY_KSV2 0x407 +#define HDCP_RAM_KEY_PRIVATE 0x40e +#define HDCP_KEY_LENGTH 0x13C + + +#define HDCP_ENABLE_HW_AUTH // Enable hardware authentication mode +#define HDMI_INTERANL_CLK_DIV 0x19 + +#define HDMIRdReg(addr) __raw_readl(hdmi->regbase + addr) +#define HDMIWrReg(addr, val) __raw_writel((val), hdmi->regbase + addr); +#define HDMIMskReg(temp, addr, msk, val) \ + temp = __raw_readl(hdmi->regbase + addr) & (0xFF - (msk)) ; \ + __raw_writel(temp | ( (val) & (msk) ), hdmi->regbase + addr); + + + +/* Color Space Convertion Mode */ +enum { + CSC_RGB_0_255_TO_ITU601_16_235 = 0, //RGB 0-255 input to YCbCr 16-235 output according BT601 + CSC_RGB_0_255_TO_ITU709_16_235, //RGB 0-255 input to YCbCr 16-235 output accroding BT709 + CSC_ITU601_16_235_TO_RGB_16_235, //YCbCr 16-235 input to RGB 16-235 output according BT601 + CSC_ITU709_16_235_TO_RGB_16_235, //YCbCr 16-235 input to RGB 16-235 output according BT709 + CSC_ITU601_16_235_TO_RGB_0_255, //YCbCr 16-235 input to RGB 0-255 output according BT601 + CSC_ITU709_16_235_TO_RGB_0_255 //YCbCr 16-235 input to RGB 0-255 output according BT709 +}; +#endif +extern int rk2928_hdmi_initial(void); +extern int rk2928_hdmi_detect_hotplug(void); +extern int rk2928_hdmi_read_edid(int block, unsigned char *buff); +extern int rk2928_hdmi_removed(void); +extern int rk2928_hdmi_config_video(struct hdmi_video_para *vpara); +extern int rk2928_hdmi_config_audio(struct hdmi_audio *audio); +extern void rk2928_hdmi_control_output(int enable); + +#endif diff --git a/drivers/video/rockchip/hdmi/chips/rk30/Kconfig b/drivers/video/rockchip/hdmi/chips/rk30/Kconfig new file mode 100755 index 000000000000..c33318d43622 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk30/Kconfig @@ -0,0 +1,15 @@ +config HDMI_RK30_CTL_CODEC + bool "Mute Codec When HDMI Actived" + depends on HDMI_RK30 + default n + help + If you say y heres, Codec will be mute when hdmi inserted, + and unmute when removed. + +config HDMI_RK30_DEBUG + bool "RK30 HDMI Debugging" + depends on HDMI_RK30 && LCDC_RK30 + default n + help + Enableds verbose debugging the the HDMI drivers + diff --git a/drivers/video/rockchip/hdmi/chips/rk30/Makefile b/drivers/video/rockchip/hdmi/chips/rk30/Makefile new file mode 100755 index 000000000000..98dfae276674 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk30/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for HDMI linux kernel module. +# + +ccflags-$(CONFIG_HDMI_RK30_DEBUG) = -DDEBUG -DHDMI_DEBUG + +obj-$(CONFIG_HDMI_RK30) += rk30_hdmi_hw.o rk30_hdmi.o +obj-$(CONFIG_HDCP_RK30) += hdcp/ diff --git a/drivers/video/rockchip/hdmi/chips/rk30/hdcp/Kconfig b/drivers/video/rockchip/hdmi/chips/rk30/hdcp/Kconfig new file mode 100755 index 000000000000..1bb3c3a24f56 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk30/hdcp/Kconfig @@ -0,0 +1,14 @@ +config HDCP_RK30 + bool "RK30 HDCP support" + depends on LCDC_RK30 && HDMI_RK30 + default n + help + HDCP Interface. This adds the High Definition Content Protection Interface. + See http://www.digital-cp.com/ for HDCP specification. + +config HDCP_RK30_DEBUG + bool "RK30 HDCP Debugging" + depends on HDCP_RK30 + default n + help + Enableds verbose debugging the the HDCP drivers diff --git a/drivers/video/rockchip/hdmi/chips/rk30/hdcp/Makefile b/drivers/video/rockchip/hdmi/chips/rk30/hdcp/Makefile new file mode 100755 index 000000000000..108b67ca134e --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk30/hdcp/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for HDCP linux kernel module. +# + +ccflags-$(CONFIG_HDCP_RK30_DEBUG) = -DDEBUG -DHDCP_DEBUG + +obj-$(CONFIG_HDCP_RK30) += hdcp.o +hdcp-y := rk30_hdcp.o rk30_hdmi_hdcp.o diff --git a/drivers/video/rockchip/hdmi/chips/rk30/hdcp/rk30_hdcp.c b/drivers/video/rockchip/hdmi/chips/rk30/hdcp/rk30_hdcp.c new file mode 100755 index 000000000000..68d0ac841a67 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk30/hdcp/rk30_hdcp.c @@ -0,0 +1,570 @@ +#include +#include +#include +#include +#include +#include +#include +#include "../rk30_hdmi.h" +#include "../rk30_hdmi_hw.h" +#include "rk30_hdmi_hdcp.h" + +struct hdcp *hdcp = NULL; + +static void hdcp_work_queue(struct work_struct *work); + +/*----------------------------------------------------------------------------- + * Function: hdcp_submit_work + *----------------------------------------------------------------------------- + */ +static struct delayed_work *hdcp_submit_work(int event, int delay) +{ + struct hdcp_delayed_work *work; + + DBG("%s event %04x delay %d", __FUNCTION__, event, delay); + + work = kmalloc(sizeof(struct hdcp_delayed_work), GFP_ATOMIC); + + if (work) { + INIT_DELAYED_WORK(&work->work, hdcp_work_queue); + work->event = event; + queue_delayed_work(hdcp->workqueue, + &work->work, + msecs_to_jiffies(delay)); + } else { + printk(KERN_WARNING "HDCP: Cannot allocate memory to " + "create work\n"); + return 0; + } + + return &work->work; +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_cancel_work + *----------------------------------------------------------------------------- + */ +static void hdcp_cancel_work(struct delayed_work **work) +{ + int ret = 0; + + if (*work) { + ret = cancel_delayed_work(*work); + if (ret != 1) { + ret = cancel_work_sync(&((*work)->work)); + printk(KERN_INFO "Canceling work failed - " + "cancel_work_sync done %d\n", ret); + } + kfree(*work); + *work = 0; + } +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_wq_authentication_failure + *----------------------------------------------------------------------------- + */ +static void hdcp_wq_authentication_failure(void) +{ + if (hdcp->hdmi_state == HDMI_STOPPED) { + return; + } + + rk30_hdcp_disable(); + rk30_hdmi_control_output(false); + + hdcp_cancel_work(&hdcp->pending_wq_event); + + if (hdcp->retry_cnt && (hdcp->hdmi_state != HDMI_STOPPED)) { + if (hdcp->retry_cnt < HDCP_INFINITE_REAUTH) { + hdcp->retry_cnt--; + printk(KERN_INFO "HDCP: authentication failed - " + "retrying, attempts=%d\n", + hdcp->retry_cnt); + } else + printk(KERN_INFO "HDCP: authentication failed - " + "retrying\n"); + + hdcp->hdcp_state = HDCP_AUTHENTICATION_START; + + hdcp->pending_wq_event = hdcp_submit_work(HDCP_AUTH_REATT_EVENT, + HDCP_REAUTH_DELAY); + } else { + printk(KERN_INFO "HDCP: authentication failed - " + "HDCP disabled\n"); + hdcp->hdcp_state = HDCP_ENABLE_PENDING; + } + +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_wq_start_authentication + *----------------------------------------------------------------------------- + */ +static void hdcp_wq_start_authentication(void) +{ + int status = HDCP_OK; + + hdcp->hdcp_state = HDCP_AUTHENTICATION_START; + + DBG("HDCP: authentication start"); + + status = rk30_hdcp_start_authentication(); + + if (status != HDCP_OK) { + DBG("HDCP: authentication failed"); + hdcp_wq_authentication_failure(); + } else { + hdcp->hdcp_state = HDCP_WAIT_KSV_LIST; + } +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_wq_check_bksv + *----------------------------------------------------------------------------- + */ +static void hdcp_wq_check_bksv(void) +{ + int status = HDCP_OK; + + DBG("Check BKSV start"); + + status = rk30_hdcp_check_bksv(); + + if (status != HDCP_OK) { + printk(KERN_INFO "HDCP: Check BKSV failed"); + hdcp->retry_cnt = 0; + hdcp_wq_authentication_failure(); + } + else { + DBG("HDCP: Check BKSV successful"); + + hdcp->hdcp_state = HDCP_LINK_INTEGRITY_CHECK; + + /* Restore retry counter */ + if(hdcp->retry_times == 0) + hdcp->retry_cnt = HDCP_INFINITE_REAUTH; + else + hdcp->retry_cnt = hdcp->retry_times; + } +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_wq_authentication_sucess + *----------------------------------------------------------------------------- + */ +static void hdcp_wq_authentication_sucess(void) +{ + printk(KERN_INFO "HDCP: authentication pass"); + rk30_hdmi_control_output(true); +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_wq_disable + *----------------------------------------------------------------------------- + */ +static void hdcp_wq_disable(int event) +{ + printk(KERN_INFO "HDCP: disabled"); + + hdcp_cancel_work(&hdcp->pending_wq_event); + rk30_hdcp_disable(); + if(event == HDCP_DISABLE_CTL) { + hdcp->hdcp_state = HDCP_DISABLED; + if(hdcp->hdmi_state == HDMI_STARTED) + rk30_hdmi_control_output(true); + } + else if(event == HDCP_STOP_FRAME_EVENT) + hdcp->hdcp_state = HDCP_ENABLE_PENDING; +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_work_queue + *----------------------------------------------------------------------------- + */ +static void hdcp_work_queue(struct work_struct *work) +{ + struct hdcp_delayed_work *hdcp_w = + container_of(work, struct hdcp_delayed_work, work.work); + int event = hdcp_w->event; + + mutex_lock(&hdcp->lock); + + DBG("hdcp_work_queue() - START - %u hdmi=%d hdcp=%d evt= %x %d", + jiffies_to_msecs(jiffies), + hdcp->hdmi_state, + hdcp->hdcp_state, + (event & 0xFF00) >> 8, + event & 0xFF); + + if(event == HDCP_STOP_FRAME_EVENT) { + hdcp->hdmi_state = HDMI_STOPPED; + } + + if (event == HDCP_DISABLE_CTL || event == HDCP_STOP_FRAME_EVENT) { + hdcp_wq_disable(event); + } + + if (event & HDCP_WORKQUEUE_SRC) + hdcp->pending_wq_event = 0; + + /* First handle HDMI state */ + if (event == HDCP_START_FRAME_EVENT) { + hdcp->pending_start = 0; + hdcp->hdmi_state = HDMI_STARTED; + } + + /**********************/ + /* HDCP state machine */ + /**********************/ + switch (hdcp->hdcp_state) { + case HDCP_DISABLED: + /* HDCP enable control or re-authentication event */ + if (event == HDCP_ENABLE_CTL) { + if(hdcp->retry_times == 0) + hdcp->retry_cnt = HDCP_INFINITE_REAUTH; + else + hdcp->retry_cnt = hdcp->retry_times; + if (hdcp->hdmi_state == HDMI_STARTED) + hdcp_wq_start_authentication(); + else + hdcp->hdcp_state = HDCP_ENABLE_PENDING; + } + break; + + case HDCP_ENABLE_PENDING: + /* HDMI start frame event */ + if (event == HDCP_START_FRAME_EVENT) + hdcp_wq_start_authentication(); + + break; + + case HDCP_AUTHENTICATION_START: + /* Re-authentication */ + if (event == HDCP_AUTH_REATT_EVENT) + hdcp_wq_start_authentication(); + + break; + + case HDCP_WAIT_KSV_LIST: + /* KSV failure */ + if (event == HDCP_FAIL_EVENT) { + printk(KERN_INFO "HDCP: KSV switch failure\n"); + + hdcp_wq_authentication_failure(); + } + /* KSV list ready event */ + else if (event == HDCP_KSV_LIST_RDY_EVENT) + hdcp_wq_check_bksv(); + break; + + case HDCP_LINK_INTEGRITY_CHECK: + /* Ri failure */ + if (event == HDCP_FAIL_EVENT) { + printk(KERN_INFO "HDCP: Ri check failure\n"); + hdcp_wq_authentication_failure(); + } + else if(event == HDCP_AUTH_PASS_EVENT) + hdcp_wq_authentication_sucess(); + break; + + default: + printk(KERN_WARNING "HDCP: error - unknow HDCP state\n"); + break; + } + + kfree(hdcp_w); + if(event == HDCP_STOP_FRAME_EVENT) + complete(&hdcp->complete); + + mutex_unlock(&hdcp->lock); +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_start_frame_cb + *----------------------------------------------------------------------------- + */ +static void hdcp_start_frame_cb(void) +{ + DBG("hdcp_start_frame_cb()"); + + /* Cancel any pending work */ + if (hdcp->pending_start) + hdcp_cancel_work(&hdcp->pending_start); + if (hdcp->pending_wq_event) + hdcp_cancel_work(&hdcp->pending_wq_event); + + hdcp->pending_start = hdcp_submit_work(HDCP_START_FRAME_EVENT, + HDCP_ENABLE_DELAY); +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_irq_cb + *----------------------------------------------------------------------------- + */ +static void hdcp_irq_cb(int interrupt) +{ + int value; + DBG("%s 0x%x", __FUNCTION__, interrupt); + if(interrupt & m_INT_HDCP_ERR) + { + value = HDMIRdReg(HDCP_ERROR); + HDMIWrReg(HDCP_ERROR, value); + printk(KERN_INFO "HDCP: Error 0x%02x\n", value); + + if( (hdcp->hdcp_state != HDCP_DISABLED) && + (hdcp->hdcp_state != HDCP_ENABLE_PENDING) ) + { + hdcp_submit_work(HDCP_FAIL_EVENT, 0); + } + } + else if(interrupt & (m_INT_BKSV_RPRDY | m_INT_BKSV_RCRDY)) + hdcp_submit_work(HDCP_KSV_LIST_RDY_EVENT, 0); + else if(interrupt & m_INT_AUTH_DONE) + hdcp_submit_work(HDCP_AUTH_PASS_EVENT, 0); +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_power_on_cb + *----------------------------------------------------------------------------- + */ +static int hdcp_power_on_cb(void) +{ + DBG("%s", __FUNCTION__); + return rk30_hdcp_load_key2mem(hdcp->keys); +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_power_off_cb + *----------------------------------------------------------------------------- + */ +static void hdcp_power_off_cb(void) +{ + DBG("%s", __FUNCTION__); + if(!hdcp->enable) + return; + + hdcp_cancel_work(&hdcp->pending_start); + hdcp_cancel_work(&hdcp->pending_wq_event); + init_completion(&hdcp->complete); + /* Post event to workqueue */ + if (hdcp_submit_work(HDCP_STOP_FRAME_EVENT, 0)) + wait_for_completion_interruptible_timeout(&hdcp->complete, + msecs_to_jiffies(2000)); +} + +// Load HDCP key to external HDCP memory +static void hdcp_load_keys_cb(const struct firmware *fw, void *context) +{ + if (!fw) { + pr_err("HDCP: failed to load keys\n"); + return; + } + + if(fw->size < HDCP_KEY_SIZE) { + pr_err("HDCP: firmware wrong size %d\n", fw->size); + return; + } + + hdcp->keys = kmalloc(HDCP_KEY_SIZE, GFP_KERNEL); + if(hdcp->keys == NULL) { + pr_err("HDCP: can't allocated space for keys\n"); + return; + } + + memcpy(hdcp->keys, fw->data, HDCP_KEY_SIZE); + + rk30_hdcp_load_key2mem(hdcp->keys); + printk(KERN_INFO "HDCP: loaded hdcp key success\n"); + + if(fw->size > HDCP_KEY_SIZE) { + DBG("%s invalid key size %d", __FUNCTION__, fw->size - HDCP_KEY_SIZE); + if((fw->size - HDCP_KEY_SIZE) % 5) { + pr_err("HDCP: failed to load invalid keys\n"); + return; + } + hdcp->invalidkeys = kmalloc(fw->size - HDCP_KEY_SIZE, GFP_KERNEL); + if(hdcp->invalidkeys == NULL) { + pr_err("HDCP: can't allocated space for invalid keys\n"); + return; + } + memcpy(hdcp->invalidkeys, fw->data + HDCP_KEY_SIZE, fw->size - HDCP_KEY_SIZE); + hdcp->invalidkey = (fw->size - HDCP_KEY_SIZE)/5; + printk(KERN_INFO "HDCP: loaded hdcp invalid key success\n"); + } +} + +static ssize_t hdcp_enable_read(struct device *device, + struct device_attribute *attr, char *buf) +{ + int enable = 0; + + if(hdcp) + enable = hdcp->enable; + + return snprintf(buf, PAGE_SIZE, "%d\n", enable); +} + +static ssize_t hdcp_enable_write(struct device *device, + struct device_attribute *attr, const char *buf, size_t count) +{ + int enable; + + if(hdcp == NULL) + return -EINVAL; + + sscanf(buf, "%d", &enable); + if(hdcp->enable != enable) + { + /* Post event to workqueue */ + if(enable) { + if (hdcp_submit_work(HDCP_ENABLE_CTL, 0) == 0) + return -EFAULT; + } + else { + hdcp_cancel_work(&hdcp->pending_start); + hdcp_cancel_work(&hdcp->pending_wq_event); + + /* Post event to workqueue */ + if (hdcp_submit_work(HDCP_DISABLE_CTL, 0) == 0) + return -EFAULT; + } + hdcp->enable = enable; + } + return count; +} + +static DEVICE_ATTR(enable, S_IRUGO|S_IWUSR, hdcp_enable_read, hdcp_enable_write); + +static ssize_t hdcp_trytimes_read(struct device *device, + struct device_attribute *attr, char *buf) +{ + int trytimes = 0; + + if(hdcp) + trytimes = hdcp->retry_times; + + return snprintf(buf, PAGE_SIZE, "%d\n", trytimes); +} + +static ssize_t hdcp_trytimes_wrtie(struct device *device, + struct device_attribute *attr, const char *buf, size_t count) +{ + int trytimes; + + if(hdcp == NULL) + return -EINVAL; + + sscanf(buf, "%d", &trytimes); + if(hdcp->retry_times != trytimes) + hdcp->retry_times = trytimes; + + return count; +} + + +static DEVICE_ATTR(trytimes, S_IRUGO|S_IWUSR, hdcp_trytimes_read, hdcp_trytimes_wrtie); + + +static struct miscdevice mdev; + +static int __init rk30_hdcp_init(void) +{ + int ret; + + DBG("[%s] %u", __FUNCTION__, jiffies_to_msecs(jiffies)); + + hdcp = kmalloc(sizeof(struct hdcp), GFP_KERNEL); + if(!hdcp) + { + printk(KERN_ERR ">>HDCP: kmalloc fail!"); + ret = -ENOMEM; + goto error0; + } + memset(hdcp, 0, sizeof(struct hdcp)); + mutex_init(&hdcp->lock); + + mdev.minor = MISC_DYNAMIC_MINOR; + mdev.name = "hdcp"; + mdev.mode = 0666; + if (misc_register(&mdev)) { + printk(KERN_ERR "HDCP: Could not add character driver\n"); + ret = HDMI_ERROR_FALSE; + goto error1; + } + ret = device_create_file(mdev.this_device, &dev_attr_enable); + if(ret) + { + printk(KERN_ERR "HDCP: Could not add sys file enable\n"); + ret = -EINVAL; + goto error2; + } + + ret = device_create_file(mdev.this_device, &dev_attr_trytimes); + if(ret) + { + printk(KERN_ERR "HDCP: Could not add sys file trytimes\n"); + ret = -EINVAL; + goto error3; + } + + hdcp->workqueue = create_singlethread_workqueue("hdcp"); + if (hdcp->workqueue == NULL) { + printk(KERN_ERR "HDCP,: create workqueue failed.\n"); + goto error4; + } + + + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG, + "hdcp.keys", mdev.this_device, GFP_KERNEL, + hdcp, hdcp_load_keys_cb); + if (ret < 0) { + printk(KERN_ERR "HDCP: request_firmware_nowait failed: %d\n", ret); + goto error5; + } + + rk30_hdmi_register_hdcp_callbacks( hdcp_start_frame_cb, + hdcp_irq_cb, + hdcp_power_on_cb, + hdcp_power_off_cb); + + DBG("%s success %u", __FUNCTION__, jiffies_to_msecs(jiffies)); + return 0; + +error5: + destroy_workqueue(hdcp->workqueue); +error4: + device_remove_file(mdev.this_device, &dev_attr_trytimes); +error3: + device_remove_file(mdev.this_device, &dev_attr_enable); +error2: + misc_deregister(&mdev); +error1: + if(hdcp->keys) + kfree(hdcp->keys); + if(hdcp->invalidkeys) + kfree(hdcp->invalidkeys); + kfree(hdcp); +error0: + return ret; +} + +static void __exit rk30_hdcp_exit(void) +{ + if(hdcp) { + mutex_lock(&hdcp->lock); + rk30_hdmi_register_hdcp_callbacks(0, 0, 0, 0); + device_remove_file(mdev.this_device, &dev_attr_enable); + misc_deregister(&mdev); + destroy_workqueue(hdcp->workqueue); + if(hdcp->keys) + kfree(hdcp->keys); + if(hdcp->invalidkeys) + kfree(hdcp->invalidkeys); + mutex_unlock(&hdcp->lock); + kfree(hdcp); + } +} + +module_init(rk30_hdcp_init); +module_exit(rk30_hdcp_exit); \ No newline at end of file diff --git a/drivers/video/rockchip/hdmi/chips/rk30/hdcp/rk30_hdmi_hdcp.c b/drivers/video/rockchip/hdmi/chips/rk30/hdcp/rk30_hdmi_hdcp.c new file mode 100755 index 000000000000..1184989a57b2 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk30/hdcp/rk30_hdmi_hdcp.c @@ -0,0 +1,157 @@ +#include +#include +#include +#include "../rk30_hdmi.h" +#include "../rk30_hdmi_hw.h" +#include "rk30_hdmi_hdcp.h" + +static void rk30_hdcp_write_mem(int addr_8, char value) +{ + int temp; + int addr_32 = addr_8 - addr_8%4; + int shift = (addr_8%4) * 8; + + temp = HDMIRdReg(addr_32); + temp &= ~(0xff << shift); + temp |= value << shift; +// printk("temp is %08x\n", temp); + HDMIWrReg(addr_32, temp); +} + +int rk30_hdcp_load_key2mem(struct hdcp_keys *key) +{ + int i; + + if(key == NULL) return HDMI_ERROR_FALSE; + + HDMIWrReg(0x800, HDMI_INTERANL_CLK_DIV); + + for(i = 0; i < 7; i++) + rk30_hdcp_write_mem(HDCP_RAM_KEY_KSV1 + i, key->KSV[i]); + for(i = 0; i < 7; i++) + rk30_hdcp_write_mem(HDCP_RAM_KEY_KSV2 + i, key->KSV[i]); + for(i = 0; i < HDCP_PRIVATE_KEY_SIZE; i++) + rk30_hdcp_write_mem(HDCP_RAM_KEY_PRIVATE + i, key->DeviceKey[i]); + for(i = 0; i < HDCP_KEY_SHA_SIZE; i++) + rk30_hdcp_write_mem(HDCP_RAM_KEY_PRIVATE + HDCP_PRIVATE_KEY_SIZE + i, key->sha1[i]); + + HDMIWrReg(0x800, HDMI_INTERANL_CLK_DIV | 0x20); + return HDCP_OK; +} + +void rk30_hdcp_disable(void) +{ + int temp; + // Diable HDCP Interrupt + HDMIWrReg(INTR_MASK2, 0x00); + // Stop and Reset HDCP + HDMIMskReg(temp, HDCP_CTRL, m_HDCP_FRAMED_ENCRYPED | m_HDCP_AUTH_STOP | m_HDCP_RESET, + v_HDCP_FRAMED_ENCRYPED(0) | v_HDCP_AUTH_STOP(1) | v_HDCP_RESET(1) ); +} + +static int rk30_hdcp_load_key(void) +{ + int value, temp = 0; + + if(hdcp->keys == NULL) { + pr_err("[%s] HDCP key not loaded.\n", __FUNCTION__); + return HDCP_KEY_ERR; + } + + value = HDMIRdReg(HDCP_KEY_MEM_CTRL); + //Check HDCP key loaded from external HDCP memory + while((value & (m_KSV_VALID | m_KEY_VALID | m_KEY_READY)) != (m_KSV_VALID | m_KEY_VALID | m_KEY_READY)) { + if(temp > 10) { + pr_err("[%s] loaded hdcp key is incorrectable %02x\n", __FUNCTION__, value & 0xFF); + return HDCP_KEY_ERR; + } + //Load HDCP Key from external HDCP memory + HDMIWrReg(HDCP_KEY_ACCESS_CTRL2, m_LOAD_HDCP_KEY); + msleep(1); + value = HDMIRdReg(HDCP_KEY_MEM_CTRL); + temp++; + } + + return HDCP_OK; +} + + +int rk30_hdcp_start_authentication(void) +{ + int rc, temp; + + rc = rk30_hdcp_load_key(); + if(rc != HDCP_OK) + return rc; + + // Set 100ms & 5 sec timer + switch(hdmi->vic) + { + case HDMI_720x576p_50Hz_4_3: + case HDMI_720x576p_50Hz_16_9: + case HDMI_1280x720p_50Hz: + case HDMI_1920x1080i_50Hz: + case HDMI_720x576i_50Hz_4_3: + case HDMI_720x576i_50Hz_16_9: + case HDMI_1920x1080p_50Hz: + HDMIWrReg(HDCP_TIMER_100MS, 5); + HDMIWrReg(HDCP_TIMER_5S, 250); + break; + + default: + HDMIWrReg(HDCP_TIMER_100MS, 0x26); + HDMIWrReg(HDCP_TIMER_5S, 0x2c); + break; + } + // Config DDC Clock + temp = (hdmi->tmdsclk/HDCP_DDC_CLK)/4; + HDMIWrReg(DDC_BUS_FREQ_L, temp & 0xFF); + HDMIWrReg(DDC_BUS_FREQ_H, (temp >> 8) & 0xFF); + // Enable HDCP Interrupt + HDMIWrReg(INTR_MASK2, m_INT_HDCP_ERR | m_INT_BKSV_RPRDY | m_INT_BKSV_RCRDY | m_INT_AUTH_DONE | m_INT_AUTH_READY); + // Start HDCP + HDMIMskReg(temp, HDCP_CTRL, m_HDCP_AUTH_START | m_HDCP_FRAMED_ENCRYPED, v_HDCP_AUTH_START(1) | v_HDCP_FRAMED_ENCRYPED(0)); + + return HDCP_OK; +} + +int rk30_hdcp_check_bksv(void) +{ + int i, temp; + char bksv[5]; + char *invalidkey; + + temp = HDMIRdReg(HDCP_BCAPS); + DBG("Receiver capacity is 0x%02x", temp); + +#ifdef DEBUG + if(temp & m_HDMI_RECEIVED) + DBG("Receiver support HDMI"); + if(temp & m_REPEATER) + DBG("Receiver is a repeater"); + if(temp & m_DDC_FAST) + DBG("Receiver support 400K DDC"); + if(temp & m_1_1_FEATURE) + DBG("Receiver support 1.1 features, such as advanced cipher, EESS."); + if(temp & m_FAST_REAUTHENTICATION) + DBG("Receiver support fast reauthentication."); +#endif + + for(i = 0; i < 5; i++) { + bksv[i] = HDMIRdReg(HDCP_KSV_BYTE0 + (4 - i)*4) & 0xFF; + } + + DBG("bksv is 0x%02x%02x%02x%02x%02x", bksv[0], bksv[1], bksv[2], bksv[3], bksv[4]); + + for(i = 0; i < hdcp->invalidkey; i++) + { + invalidkey = hdcp->invalidkeys + i *5; + if(memcmp(bksv, invalidkey, 5) == 0) { + printk(KERN_ERR "HDCP: BKSV was revocated!!!\n"); + HDMIMskReg(temp, HDCP_CTRL, m_HDCP_BKSV_FAILED | m_HDCP_FRAMED_ENCRYPED, v_HDCP_BKSV_FAILED(1) | v_HDCP_FRAMED_ENCRYPED(0)); + return HDCP_KSV_ERR; + } + } + HDMIMskReg(temp, HDCP_CTRL, m_HDCP_BKSV_PASS | m_HDCP_FRAMED_ENCRYPED, v_HDCP_BKSV_PASS(1) | v_HDCP_FRAMED_ENCRYPED(1)); + return HDCP_OK; +} diff --git a/drivers/video/rockchip/hdmi/chips/rk30/hdcp/rk30_hdmi_hdcp.h b/drivers/video/rockchip/hdmi/chips/rk30/hdcp/rk30_hdmi_hdcp.h new file mode 100755 index 000000000000..0224d88ab134 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk30/hdcp/rk30_hdmi_hdcp.h @@ -0,0 +1,99 @@ +#ifndef __RK30_HDMI_HDCP_H__ +#define __RK30_HDMI_HDCP_H__ + +/***************************/ +/* Definitions */ +/***************************/ + +/* Status / error codes */ +#define HDCP_OK 0 +#define HDCP_KEY_ERR 1 +#define HDCP_KSV_ERR 2 + +/* Delays */ +#define HDCP_ENABLE_DELAY 300 +#define HDCP_REAUTH_DELAY 100 + +/* Event source */ +#define HDCP_SRC_SHIFT 8 +#define HDCP_IOCTL_SRC (0x1 << HDCP_SRC_SHIFT) +#define HDCP_HDMI_SRC (0x2 << HDCP_SRC_SHIFT) +#define HDCP_IRQ_SRC (0x4 << HDCP_SRC_SHIFT) +#define HDCP_WORKQUEUE_SRC (0x8 << HDCP_SRC_SHIFT) + +/* Event */ +#define HDCP_ENABLE_CTL (HDCP_IOCTL_SRC | 0) +#define HDCP_DISABLE_CTL (HDCP_IOCTL_SRC | 1) +#define HDCP_START_FRAME_EVENT (HDCP_HDMI_SRC | 2) +#define HDCP_STOP_FRAME_EVENT (HDCP_HDMI_SRC | 3) +#define HDCP_KSV_LIST_RDY_EVENT (HDCP_IRQ_SRC | 4) +#define HDCP_FAIL_EVENT (HDCP_IRQ_SRC | 5) +#define HDCP_AUTH_PASS_EVENT (HDCP_IRQ_SRC | 6) +#define HDCP_AUTH_REATT_EVENT (HDCP_WORKQUEUE_SRC | 7) + +/* Key size */ +#define HDCP_KEY_SIZE 308 + +/* Authentication retry times */ +#define HDCP_INFINITE_REAUTH 0x100 + +enum hdcp_states { + HDCP_DISABLED, + HDCP_ENABLE_PENDING, + HDCP_AUTHENTICATION_START, + HDCP_WAIT_KSV_LIST, + HDCP_LINK_INTEGRITY_CHECK, +}; + +enum hdmi_states { + HDMI_STOPPED, + HDMI_STARTED +}; + +#define HDCP_PRIVATE_KEY_SIZE 280 +#define HDCP_KEY_SHA_SIZE 20 +#define HDCP_DDC_CLK 100000 + +struct hdcp_keys{ + u8 KSV[8]; + u8 DeviceKey[HDCP_PRIVATE_KEY_SIZE]; + u8 sha1[HDCP_KEY_SHA_SIZE]; +}; + +struct hdcp_delayed_work { + struct delayed_work work; + int event; +}; + +struct hdcp { + int enable; + int retry_times; + struct hdcp_keys *keys; + int invalidkey; + char *invalidkeys; + struct mutex lock; + struct completion complete; + struct workqueue_struct *workqueue; + + enum hdmi_states hdmi_state; + enum hdcp_states hdcp_state; + + struct delayed_work *pending_start; + struct delayed_work *pending_wq_event; + int retry_cnt; +}; + +extern struct hdcp *hdcp; + +#ifdef HDCP_DEBUG +#define DBG(format, ...) \ + printk(KERN_INFO "HDCP: " format "\n", ## __VA_ARGS__) +#else +#define DBG(format, ...) +#endif + +extern void rk30_hdcp_disable(void); +extern int rk30_hdcp_start_authentication(void); +extern int rk30_hdcp_check_bksv(void); +extern int rk30_hdcp_load_key2mem(struct hdcp_keys *key); +#endif /* __RK30_HDMI_HDCP_H__ */ \ No newline at end of file diff --git a/drivers/video/rockchip/hdmi/rk30_hdmi.c b/drivers/video/rockchip/hdmi/chips/rk30/rk30_hdmi.c similarity index 100% rename from drivers/video/rockchip/hdmi/rk30_hdmi.c rename to drivers/video/rockchip/hdmi/chips/rk30/rk30_hdmi.c diff --git a/drivers/video/rockchip/hdmi/chips/rk30/rk30_hdmi.h b/drivers/video/rockchip/hdmi/chips/rk30/rk30_hdmi.h new file mode 100755 index 000000000000..4695086bf329 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk30/rk30_hdmi.h @@ -0,0 +1,11 @@ +#ifndef __RK30_HDMI_H__ +#define __RK30_HDMI_H__ + +#include "../../rk_hdmi.h" + + +extern int rk30_hdmi_register_hdcp_callbacks(void (*hdcp_cb)(void), + void (*hdcp_irq_cb)(int status), + int (*hdcp_power_on_cb)(void), + void (*hdcp_power_off_cb)(void)); +#endif /* __RK30_HDMI_H__ */ diff --git a/drivers/video/rockchip/hdmi/rk30_hdmi_hw.c b/drivers/video/rockchip/hdmi/chips/rk30/rk30_hdmi_hw.c similarity index 97% rename from drivers/video/rockchip/hdmi/rk30_hdmi_hw.c rename to drivers/video/rockchip/hdmi/chips/rk30/rk30_hdmi_hw.c index 8cb76ebfb14d..e8b5f1e74b74 100755 --- a/drivers/video/rockchip/hdmi/rk30_hdmi_hw.c +++ b/drivers/video/rockchip/hdmi/chips/rk30/rk30_hdmi_hw.c @@ -14,6 +14,14 @@ static inline void delay100us(void) int rk30_hdmi_initial(void) { int rc = HDMI_ERROR_SUCESS; + + hdmi->pwr_mode = PWR_SAVE_MODE_A; + hdmi->hdmi_removed = rk30_hdmi_removed ; + hdmi->control_output = rk30_hdmi_control_output; + hdmi->config_video = rk30_hdmi_config_video; + hdmi->config_audio = rk30_hdmi_config_audio; + hdmi->detect_hotplug = rk30_hdmi_detect_hotplug; + hdmi->read_edid = rk30_hdmi_read_edid; // internal hclk = hdmi_hclk/20 HDMIWrReg(0x800, HDMI_INTERANL_CLK_DIV); @@ -292,7 +300,7 @@ static char coeff_csc[][24] = { }; -static void rk30_hdmi_config_csc(struct rk30_hdmi_video_para *vpara) +static void rk30_hdmi_config_csc(struct hdmi_video_para *vpara) { int i, mode; char *coeff = NULL; @@ -339,7 +347,7 @@ static void rk30_hdmi_config_csc(struct rk30_hdmi_video_para *vpara) HDMIWrReg(AV_CTRL2, v_CSC_ENABLE(1)); } -int rk30_hdmi_config_video(struct rk30_hdmi_video_para *vpara) +int rk30_hdmi_config_video(struct hdmi_video_para *vpara) { int value; struct fb_videomode *mode; @@ -548,9 +556,10 @@ void rk30_hdmi_control_output(int enable) { hdmi_dbg(hdmi->dev, "[%s] %d\n", __FUNCTION__, enable); if(enable == 0) { - HDMIWrReg(VIDEO_SETTING2, 0x03); + HDMIWrReg(AV_MUTE, v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1)); } else { + HDMIWrReg(VIDEO_SETTING2, 0x03); if(hdmi->pwr_mode == PWR_SAVE_MODE_B) { // Switch to power save mode_d rk30_hdmi_set_pwr_mode(PWR_SAVE_MODE_D); diff --git a/drivers/video/rockchip/hdmi/rk30_hdmi_hw.h b/drivers/video/rockchip/hdmi/chips/rk30/rk30_hdmi_hw.h similarity index 94% rename from drivers/video/rockchip/hdmi/rk30_hdmi_hw.h rename to drivers/video/rockchip/hdmi/chips/rk30/rk30_hdmi_hw.h index 6a5fc3368a62..c42f0614b942 100755 --- a/drivers/video/rockchip/hdmi/rk30_hdmi_hw.h +++ b/drivers/video/rockchip/hdmi/chips/rk30/rk30_hdmi_hw.h @@ -60,15 +60,6 @@ enum { }; #define m_AUDIO_SAMPLE_RATE 0xF0 #define v_AUDIO_SAMPLE_RATE(n) (n << 4) -enum { - VIDEO_INPUT_RGB_YCBCR_444 = 0, - VIDEO_INPUT_YCBCR422, - VIDEO_INPUT_YCBCR422_EMBEDDED_SYNC, - VIDEO_INPUT_2X_CLOCK, - VIDEO_INPUT_2X_CLOCK_EMBEDDED_SYNC, - VIDEO_INPUT_RGB444_DDR, - VIDEO_INPUT_YCBCR422_DDR -}; #define m_INPUT_VIDEO_MODE (7 << 1) #define v_INPUT_VIDEO_MODE(n) (n << 1) enum { @@ -84,11 +75,7 @@ enum { /* HDMI_VIDEO_CTRL1 */ #define VIDEO_CTRL1 0x58 -enum { - VIDEO_OUTPUT_RGB444 = 0, - VIDEO_OUTPUT_YCBCR444, - VIDEO_OUTPUT_YCBCR422 -}; + #define m_VIDEO_OUTPUT_MODE (0x3 << 6) #define v_VIDEO_OUTPUT_MODE(n) (n << 6) enum { @@ -105,10 +92,6 @@ enum { }; #define m_VIDEO_EMBEDDED_SYNC_LOCATION (3 << 2) #define VIDEO_EMBEDDED_SYNC_LOCATION(n) (n << 2) -enum { - VIDEO_INPUT_COLOR_RGB = 0, - VIDEO_INPUT_COLOR_YCBCR -}; #define m_VIDEO_INPUT_COLOR_MODE (1 << 0) /* DEEP_COLOR_MODE */ @@ -416,14 +399,7 @@ enum { temp = __raw_readl(hdmi->regbase + addr) & (0xFF - (msk)) ; \ __raw_writel(temp | ( (val) & (msk) ), hdmi->regbase + addr); -/* RK30 HDMI Video Configure Parameters */ -struct rk30_hdmi_video_para { - int vic; - int input_mode; //input video data interface - int input_color; //input video color mode - int output_mode; //output hdmi or dvi - int output_color; //output video color mode -}; + /* Color Space Convertion Mode */ enum { @@ -439,8 +415,8 @@ extern int rk30_hdmi_initial(void); extern int rk30_hdmi_detect_hotplug(void); extern int rk30_hdmi_read_edid(int block, unsigned char *buff); extern int rk30_hdmi_removed(void); -extern int rk30_hdmi_config_video(struct rk30_hdmi_video_para *vpara); +extern int rk30_hdmi_config_video(struct hdmi_video_para *vpara); extern int rk30_hdmi_config_audio(struct hdmi_audio *audio); extern void rk30_hdmi_control_output(int enable); -#endif \ No newline at end of file +#endif diff --git a/drivers/video/rockchip/hdmi/rk30_hdmi.h b/drivers/video/rockchip/hdmi/rk30_hdmi.h deleted file mode 100755 index 54f5a5c0a676..000000000000 --- a/drivers/video/rockchip/hdmi/rk30_hdmi.h +++ /dev/null @@ -1,108 +0,0 @@ -#ifndef __RK30_HDMI_H__ -#define __RK30_HDMI_H__ - -#include -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_SWITCH -#include -#endif -#ifdef CONFIG_HAS_EARLYSUSPEND -#include -#endif -#include -#include -#include -#include "rk_hdmi.h" - -// HDMI video source -enum { - HDMI_SOURCE_LCDC0 = 0, - HDMI_SOURCE_LCDC1 = 1 -}; - -/* default HDMI video source */ -#define HDMI_SOURCE_DEFAULT HDMI_SOURCE_LCDC1 - -/* If HDMI_ENABLE, system will auto configure output mode according to EDID - * If HDMI_DISABLE, system will output mode according to macro HDMI_VIDEO_DEFAULT_MODE - */ -#define HDMI_AUTO_CONFIGURE HDMI_ENABLE - -/* default HDMI output video mode */ -#define HDMI_VIDEO_DEFAULT_MODE HDMI_1280x720p_60Hz//HDMI_1920x1080p_60Hz -/* default HDMI output audio mode */ -#define HDMI_AUDIO_DEFAULT_CHANNEL 2 -#define HDMI_AUDIO_DEFAULT_RATE HDMI_AUDIO_FS_44100 -#define HDMI_AUDIO_DEFAULT_WORD_LENGTH HDMI_AUDIO_WORD_LENGTH_16bit - -struct hdmi { - struct device *dev; - struct clk *hclk; //HDMI AHP clk - int regbase; - int irq; - int regbase_phy; - int regsize_phy; - struct rk_lcdc_device_driver *lcdc; - - #ifdef CONFIG_SWITCH - struct switch_dev switch_hdmi; - #endif - - struct workqueue_struct *workqueue; - struct delayed_work delay_work; - - spinlock_t irq_lock; - struct mutex enable_mutex; - - int wait; - struct completion complete; - - int suspend; -#ifdef CONFIG_HAS_EARLYSUSPEND - struct early_suspend early_suspend; -#endif - - struct hdmi_edid edid; - int enable; // Enable HDMI output or not - int vic; // HDMI output video mode code - struct hdmi_audio audio; // HDMI output audio type. - - int pwr_mode; // power mode - int hotplug; // hot plug status - int state; // hdmi state machine status - int autoconfig; // if true, auto config hdmi output mode according to EDID. - int command; // HDMI configuration command - int display; // HDMI display status - int xscale; // x direction scale value - int yscale; // y directoon scale value - int tmdsclk; // TDMS Clock frequency - // call back for hdcp operatoion - void (*hdcp_cb)(void); - void (*hdcp_irq_cb)(int); - int (*hdcp_power_on_cb)(void); - void (*hdcp_power_off_cb)(void); -}; - -extern struct hdmi *hdmi; - -extern int hdmi_sys_init(void); -extern int hdmi_sys_parse_edid(struct hdmi* hdmi); -extern const char *hdmi_get_video_mode_name(unsigned char vic); -extern int hdmi_videomode_to_vic(struct fb_videomode *vmode); -extern const struct fb_videomode* hdmi_vic_to_videomode(int vic); -extern int hdmi_add_videomode(const struct fb_videomode *mode, struct list_head *head); -extern struct hdmi_video_timing * hdmi_find_mode(int vic); -extern int hdmi_find_best_mode(struct hdmi* hdmi, int vic); -extern int hdmi_ouputmode_select(struct hdmi *hdmi, int edid_ok); -extern int hdmi_switch_fb(struct hdmi *hdmi, int vic); -extern void hdmi_sys_remove(void); -extern int rk30_hdmi_register_hdcp_callbacks(void (*hdcp_cb)(void), - void (*hdcp_irq_cb)(int status), - int (*hdcp_power_on_cb)(void), - void (*hdcp_power_off_cb)(void)); -#endif /* __RK30_HDMI_H__ */ diff --git a/drivers/video/rockchip/hdmi/rk_hdmi.h b/drivers/video/rockchip/hdmi/rk_hdmi.h index 534ba51ab5bd..cf6d34c6ce80 100755 --- a/drivers/video/rockchip/hdmi/rk_hdmi.h +++ b/drivers/video/rockchip/hdmi/rk_hdmi.h @@ -1,6 +1,61 @@ #ifndef __RK_HDMI_H__ #define __RK_HDMI_H__ +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_SWITCH +#include +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif +#include +#include +#include + +/* default HDMI output video mode */ +#define HDMI_VIDEO_DEFAULT_MODE HDMI_1280x720p_60Hz//HDMI_1920x1080p_60Hz + +// HDMI video source +enum { + HDMI_SOURCE_LCDC0 = 0, + HDMI_SOURCE_LCDC1 = 1 +}; + +/* default HDMI video source */ +#define HDMI_SOURCE_DEFAULT HDMI_SOURCE_LCDC1 +/* If HDMI_ENABLE, system will auto configure output mode according to EDID + * If HDMI_DISABLE, system will output mode according to macro HDMI_VIDEO_DEFAULT_MODE + */ +#define HDMI_AUTO_CONFIGURE HDMI_ENABLE + +/* default HDMI output audio mode */ +#define HDMI_AUDIO_DEFAULT_CHANNEL 2 +#define HDMI_AUDIO_DEFAULT_RATE HDMI_AUDIO_FS_44100 +#define HDMI_AUDIO_DEFAULT_WORD_LENGTH HDMI_AUDIO_WORD_LENGTH_16bit +enum { + VIDEO_INPUT_RGB_YCBCR_444 = 0, + VIDEO_INPUT_YCBCR422, + VIDEO_INPUT_YCBCR422_EMBEDDED_SYNC, + VIDEO_INPUT_2X_CLOCK, + VIDEO_INPUT_2X_CLOCK_EMBEDDED_SYNC, + VIDEO_INPUT_RGB444_DDR, + VIDEO_INPUT_YCBCR422_DDR +}; +enum { + VIDEO_OUTPUT_RGB444 = 0, + VIDEO_OUTPUT_YCBCR444, + VIDEO_OUTPUT_YCBCR422 +}; +enum { + VIDEO_INPUT_COLOR_RGB = 0, + VIDEO_INPUT_COLOR_YCBCR +}; /******************************************************************** ** ½á¹¹¶¨Òå * ********************************************************************/ @@ -186,6 +241,72 @@ struct hdmi_edid { }; extern const struct fb_videomode hdmi_mode[]; +/* RK HDMI Video Configure Parameters */ +struct hdmi_video_para { + int vic; + int input_mode; //input video data interface + int input_color; //input video color mode + int output_mode; //output hdmi or dvi + int output_color; //output video color mode +}; +struct hdmi { + struct device *dev; + struct clk *hclk; //HDMI AHP clk + int id; + int regbase; + int irq; + int regbase_phy; + int regsize_phy; + struct rk_lcdc_device_driver *lcdc; + + #ifdef CONFIG_SWITCH + struct switch_dev switch_hdmi; + #endif + + struct workqueue_struct *workqueue; + struct delayed_work delay_work; + + spinlock_t irq_lock; + struct mutex enable_mutex; + + int wait; + struct completion complete; + + int suspend; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#endif + + struct hdmi_edid edid; + int enable; // Enable HDMI output or not + int vic; // HDMI output video mode code + struct hdmi_audio audio; // HDMI output audio type. + + int pwr_mode; // power mode + int hotplug; // hot plug status + int state; // hdmi state machine status + int autoconfig; // if true, auto config hdmi output mode according to EDID. + int command; // HDMI configuration command + int display; // HDMI display status + int xscale; // x direction scale value + int yscale; // y directoon scale value + int tmdsclk; // TDMS Clock frequency + + + int (*hdmi_removed)(void); + void (*control_output)(int enable); + int (*config_video)(struct hdmi_video_para *vpara); + int (*config_audio)(struct hdmi_audio *audio); + int (*detect_hotplug)(void); + // call back for edid + int (*read_edid)(int block, unsigned char *buff); + + // call back for hdcp operatoion + void (*hdcp_cb)(void); + void (*hdcp_irq_cb)(int); + int (*hdcp_power_on_cb)(void); + void (*hdcp_power_off_cb)(void); +}; #define hdmi_err(dev, format, arg...) \ dev_printk(KERN_ERR , dev , format , ## arg) @@ -197,7 +318,22 @@ extern const struct fb_videomode hdmi_mode[]; #define hdmi_dbg(dev, format, arg...) #endif +extern struct hdmi *hdmi; +extern struct hdmi *hdmi_register(int extra); +extern void hdmi_unregister(struct hdmi *hdmi); extern int hdmi_get_hotplug(void); extern int hdmi_set_info(struct rk29fb_screen *screen, unsigned int vic); extern void hdmi_init_lcdc(struct rk29fb_screen *screen, struct rk29lcd_info *lcd_info); +extern int hdmi_sys_init(void); +extern int hdmi_sys_parse_edid(struct hdmi* hdmi); +extern const char *hdmi_get_video_mode_name(unsigned char vic); +extern int hdmi_videomode_to_vic(struct fb_videomode *vmode); +extern const struct fb_videomode* hdmi_vic_to_videomode(int vic); +extern int hdmi_add_videomode(const struct fb_videomode *mode, struct list_head *head); +extern struct hdmi_video_timing * hdmi_find_mode(int vic); +extern int hdmi_find_best_mode(struct hdmi* hdmi, int vic); +extern int hdmi_ouputmode_select(struct hdmi *hdmi, int edid_ok); +extern int hdmi_switch_fb(struct hdmi *hdmi, int vic); +extern void hdmi_sys_remove(void); + #endif diff --git a/drivers/video/rockchip/hdmi/rk_hdmi_core.c b/drivers/video/rockchip/hdmi/rk_hdmi_core.c new file mode 100755 index 000000000000..1b17185b6da9 --- /dev/null +++ b/drivers/video/rockchip/hdmi/rk_hdmi_core.c @@ -0,0 +1,254 @@ +#include +#include +#include +#include +#include +#include +#include + + +struct class *hdmi_class; +struct hdmi_id_ref_info { + struct hdmi *hdmi; + int id; + int ref; +}ref_info[HDMI_MAX_ID]; +#ifdef CONFIG_SYSFS + +extern int hdmi_create_attrs(struct hdmi *hdmi); +extern void hdmi_remove_attrs(struct hdmi *hdmi); + +#else + +static inline int hdmi_create_attrs(struct hdmi *hdmi) +{ return 0; } +static inline void hdmi_remove_attrs(struct hdmi *hdmi) {} + +#endif /* CONFIG_SYSFS */ +static void __hdmi_changed(struct hdmi *hdmi) +{ + int precent; + + mutex_lock(&hdmi->lock); + precent = hdmi->ops->hdmi_precent(hdmi); + if(precent && (hdmi->mode == DISP_ON_LCD) && hdmi->display_on){ + if(hdmi->ops->insert(hdmi) == 0){ + hdmi->mode = hdmi->display_on; + kobject_uevent(&hdmi->dev->kobj, KOBJ_CHANGE); + } + else + hdmi_dbg(hdmi->dev, "insert error\n"); + hdmi_set_backlight(hdmi->display_on==DISP_ON_HDMI?HDMI_DISABLE: HDMI_ENABLE); + + } + else if(precent &&(hdmi->mode != hdmi->display_on)&& hdmi->display_on){ + hdmi->mode = hdmi->display_on; + hdmi_set_backlight(hdmi->display_on==DISP_ON_HDMI?HDMI_DISABLE: HDMI_ENABLE); + } + else if((!precent || !hdmi->display_on) && hdmi->mode != DISP_ON_LCD){ + if(hdmi->ops->remove(hdmi) == 0){ + hdmi->mode = DISP_ON_LCD; + hdmi_set_backlight(HDMI_ENABLE); + kobject_uevent(&hdmi->dev->kobj, KOBJ_CHANGE); + } + else + hdmi_dbg(hdmi->dev, "remove error\n"); + } + mutex_unlock(&hdmi->lock); + return; +} + +void hdmi_changed(struct hdmi *hdmi, int msec) +{ + schedule_delayed_work(&hdmi->work, msecs_to_jiffies(msec)); + return; +} +void hdmi_suspend(struct hdmi *hdmi) +{ + del_timer(&hdmi->timer); + flush_delayed_work(&hdmi->work); + if(hdmi->mode != DISP_ON_LCD){ + hdmi->ops->remove(hdmi); + hdmi->mode = DISP_ON_LCD; + } + return; +} +void hdmi_resume(struct hdmi *hdmi) +{ + mod_timer(&hdmi->timer, jiffies + msecs_to_jiffies(10)); + return; +} + +static void hdmi_changed_work(struct work_struct *work) +{ + struct hdmi *hdmi = container_of(work, struct hdmi, + work.work); + + __hdmi_changed(hdmi); + return; +} + +void *hdmi_priv(struct hdmi *hdmi) +{ + return (void *)hdmi->priv; +} +static void hdmi_detect_timer(unsigned long data) +{ + struct hdmi *hdmi = (struct hdmi*)data; + + int precent = hdmi->ops->hdmi_precent(hdmi); + + if((precent && hdmi->mode == DISP_ON_LCD) || + (!precent && hdmi->mode != DISP_ON_LCD)) + hdmi_changed(hdmi, 100); + mod_timer(&hdmi->timer, jiffies + msecs_to_jiffies(200)); +} +struct hdmi *hdmi_register(int extra, struct device *parent) +{ + int rc = 0, i; + char name[8]; + struct hdmi *hdmi = kzalloc(sizeof(struct hdmi)+ extra, GFP_KERNEL); + + if(!hdmi) + return NULL; + for(i = 0; i < HDMI_MAX_ID; i++) + { + if(ref_info[i].ref == 0) + { + ref_info[i].ref = 1; + hdmi->id = i; + break; + } + } + if(i == HDMI_MAX_ID) + { + kfree(hdmi); + return NULL; + } + sprintf(name, "hdmi-%d", hdmi->id); + + hdmi->dev = device_create(hdmi_class, parent, 0, + "%s", name); + if (IS_ERR(hdmi->dev)) { + rc = PTR_ERR(hdmi->dev); + goto dev_create_failed; + } + + dev_set_drvdata(hdmi->dev, hdmi); + ref_info[i].hdmi = hdmi; + + INIT_DELAYED_WORK(&hdmi->work, hdmi_changed_work); + + rc = hdmi_create_attrs(hdmi); + if (rc) + goto create_attrs_failed; + + goto success; + +create_attrs_failed: + device_unregister(hdmi->dev); +dev_create_failed: + hdmi_remove_attrs(hdmi); + kfree(hdmi); + return NULL; +success: + mutex_init(&hdmi->lock); + setup_timer(&hdmi->timer, hdmi_detect_timer,(unsigned long)hdmi); + mod_timer(&hdmi->timer, jiffies + msecs_to_jiffies(200)); + return hdmi; +} +void hdmi_unregister(struct hdmi *hdmi) +{ + int id; + + if(!hdmi) + return; + id = hdmi->id; + del_timer(&hdmi->timer); + flush_scheduled_work(); + hdmi_remove_attrs(hdmi); + device_unregister(hdmi->dev); + + kfree(hdmi); + hdmi = NULL; + ref_info[id].ref = 0; + ref_info[id].hdmi = NULL; +} +struct hdmi *get_hdmi_struct(int nr) +{ + if(ref_info[nr].ref == 0) + return NULL; + else + return ref_info[nr].hdmi; +} +int hdmi_is_insert(void) +{ + struct hdmi *hdmi = get_hdmi_struct(0); + + if(hdmi && hdmi->ops && hdmi->ops->hdmi_precent) + return hdmi->ops->hdmi_precent(hdmi); + else + return 0; +} +int hdmi_get_scale(void) +{ + struct hdmi* hdmi = get_hdmi_struct(0); + if(!hdmi) + return 100; + else if(hdmi->mode != DISP_ON_LCD) + return hdmi->scale; + else + return 100; +} + +int hdmi_set_scale(int event, char *data, int len) +{ + int result; + struct hdmi* hdmi = get_hdmi_struct(0); + + if(!hdmi) + return -1; + if(len != 4) + return -1; + if(fb_get_video_mode() || hdmi->mode == DISP_ON_LCD) + return -1; + + result = data[0] | data[1]<<1 | data[2]<<2; + if(event != MOUSE_NONE && (result & event) != event) + return -1; + + hdmi->scale += data[3]; + + hdmi->scale = (hdmi->scale>100)?100:hdmi->scale; + hdmi->scale = (hdmi->scalescale; + return 0; +} + +static int __init hdmi_class_init(void) +{ + int i; + + hdmi_class = class_create(THIS_MODULE, "hdmi"); + + if (IS_ERR(hdmi_class)) + return PTR_ERR(hdmi_class); + for(i = 0; i < HDMI_MAX_ID; i++) { + ref_info[i].id = i; + ref_info[i].ref = 0; + ref_info[i].hdmi = NULL; + } + return 0; +} + +static void __exit hdmi_class_exit(void) +{ + class_destroy(hdmi_class); +} +EXPORT_SYMBOL(hdmi_changed); +EXPORT_SYMBOL(hdmi_register); +EXPORT_SYMBOL(hdmi_unregister); +EXPORT_SYMBOL(get_hdmi_struct); + +subsys_initcall(hdmi_class_init); +module_exit(hdmi_class_exit); + diff --git a/drivers/video/rockchip/hdmi/rk30_hdmi_edid.c b/drivers/video/rockchip/hdmi/rk_hdmi_edid.c similarity index 95% rename from drivers/video/rockchip/hdmi/rk30_hdmi_edid.c rename to drivers/video/rockchip/hdmi/rk_hdmi_edid.c index 9fec195fea3c..ff44f4898f34 100755 --- a/drivers/video/rockchip/hdmi/rk30_hdmi_edid.c +++ b/drivers/video/rockchip/hdmi/rk_hdmi_edid.c @@ -1,5 +1,4 @@ -#include "rk30_hdmi.h" -#include "rk30_hdmi_hw.h" +#include "rk_hdmi.h" #include "../../edid.h" #define hdmi_edid_error(fmt, ...) \ @@ -374,7 +373,7 @@ int hdmi_sys_parse_edid(struct hdmi* hdmi) } // Read base block edid. memset(buff, 0 , HDMI_EDID_BLOCK_SIZE); - rc = rk30_hdmi_read_edid(0, buff); + rc = hdmi->read_edid(0, buff); if(rc) { dev_err(hdmi->dev, "[HDMI] read edid base block error\n"); @@ -389,7 +388,7 @@ int hdmi_sys_parse_edid(struct hdmi* hdmi) for(i = 1; i < extendblock + 1; i++) { memset(buff, 0 , HDMI_EDID_BLOCK_SIZE); - rc = rk30_hdmi_read_edid(i, buff); + rc = hdmi->read_edid(i, buff); if(rc) { printk("[HDMI] read edid block %d error\n", i); @@ -407,4 +406,4 @@ out: kfree(buff); rc = hdmi_ouputmode_select(hdmi, rc); return rc; -} \ No newline at end of file +} diff --git a/drivers/video/rockchip/hdmi/rk30_hdmi_lcdc.c b/drivers/video/rockchip/hdmi/rk_hdmi_lcdc.c similarity index 99% rename from drivers/video/rockchip/hdmi/rk30_hdmi_lcdc.c rename to drivers/video/rockchip/hdmi/rk_hdmi_lcdc.c index 406837e2e200..da445b953211 100755 --- a/drivers/video/rockchip/hdmi/rk30_hdmi_lcdc.c +++ b/drivers/video/rockchip/hdmi/rk_hdmi_lcdc.c @@ -1,7 +1,6 @@ #include -#include "rk30_hdmi.h" -#include "rk30_hdmi_hw.h" #include +#include "rk_hdmi.h" #define OUT_TYPE SCREEN_HDMI #define OUT_FACE OUT_P888 @@ -524,4 +523,4 @@ int hdmi_get_hotplug(void) return hdmi->hotplug; else return HDMI_HPD_REMOVED; -} \ No newline at end of file +} diff --git a/drivers/video/rockchip/hdmi/rk30_hdmi_sysfs.c b/drivers/video/rockchip/hdmi/rk_hdmi_sysfs.c similarity index 99% rename from drivers/video/rockchip/hdmi/rk30_hdmi_sysfs.c rename to drivers/video/rockchip/hdmi/rk_hdmi_sysfs.c index 828b77120f2f..45d28eea57b5 100755 --- a/drivers/video/rockchip/hdmi/rk30_hdmi_sysfs.c +++ b/drivers/video/rockchip/hdmi/rk_hdmi_sysfs.c @@ -2,7 +2,7 @@ #include #include #include -#include "rk30_hdmi.h" +#include "rk_hdmi.h" static int hdmi_get_enable(struct rk_display_device *device) { @@ -174,4 +174,4 @@ void hdmi_unregister_display_sysfs(struct hdmi *hdmi) if(display_device_hdmi) rk_display_device_unregister(display_device_hdmi); } -#endif \ No newline at end of file +#endif diff --git a/drivers/video/rockchip/hdmi/rk30_hdmi_task.c b/drivers/video/rockchip/hdmi/rk_hdmi_task.c similarity index 94% rename from drivers/video/rockchip/hdmi/rk30_hdmi_task.c rename to drivers/video/rockchip/hdmi/rk_hdmi_task.c index cbee83a5d509..233d1757ff04 100755 --- a/drivers/video/rockchip/hdmi/rk30_hdmi_task.c +++ b/drivers/video/rockchip/hdmi/rk_hdmi_task.c @@ -1,13 +1,13 @@ #include #include -#include "rk30_hdmi.h" -#include "rk30_hdmi_hw.h" +#include "rk_hdmi.h" #ifdef CONFIG_HDMI_RK30_CTL_CODEC extern void codec_set_spk(bool on); #endif #define HDMI_MAX_TRY_TIMES 1 +#define HDMI_MAX_ID 1 static char *envp[] = {"INTERFACE=HDMI", NULL}; @@ -50,7 +50,6 @@ static void hdmi_sys_show_state(int state) int hdmi_sys_init(void) { - hdmi->pwr_mode = PWR_SAVE_MODE_A; hdmi->hotplug = HDMI_HPD_REMOVED; hdmi->state = HDMI_SLEEP; hdmi->enable = HDMI_ENABLE; @@ -97,7 +96,7 @@ static void hdmi_sys_sleep(void) if(hdmi->enable) disable_irq(hdmi->irq); hdmi->state = HDMI_SLEEP; - rk30_hdmi_removed(); + hdmi->hdmi_removed(); if(hdmi->enable) enable_irq(hdmi->irq); mutex_unlock(&hdmi->enable_mutex); @@ -122,7 +121,7 @@ static int hdmi_process_command(void) hdmi_sys_remove(); hdmi->state = HDMI_SLEEP; hdmi->hotplug = HDMI_HPD_REMOVED; - rk30_hdmi_removed(); + hdmi->hdmi_removed(); state = HDMI_SLEEP; } mutex_unlock(&hdmi->enable_mutex); @@ -168,7 +167,7 @@ void hdmi_work(struct work_struct *work) { int hotplug, state_last; int rc = HDMI_ERROR_SUCESS, trytimes = 0; - struct rk30_hdmi_video_para video; + struct hdmi_video_para video; mutex_lock(&work_mutex); /* Process hdmi command */ @@ -178,7 +177,7 @@ void hdmi_work(struct work_struct *work) mutex_unlock(&work_mutex); return; } - hotplug = rk30_hdmi_detect_hotplug(); + hotplug = hdmi->detect_hotplug(); hdmi_dbg(hdmi->dev, "[%s] hotplug %02x curvalue %d\n", __FUNCTION__, hotplug, hdmi->hotplug); if(hotplug != hdmi->hotplug) @@ -193,7 +192,7 @@ void hdmi_work(struct work_struct *work) hdmi_sys_sleep(); else { hdmi->state = WAIT_HOTPLUG; - rk30_hdmi_removed(); + hdmi->hdmi_removed(); } if(hdmi->wait == 1) { complete(&hdmi->complete); @@ -204,7 +203,7 @@ void hdmi_work(struct work_struct *work) } else if(hotplug == HDMI_HPD_REMOVED) { hdmi->state = HDMI_SLEEP; - rk30_hdmi_removed(); + hdmi->hdmi_removed(); } hdmi->hotplug = hotplug; } @@ -256,7 +255,7 @@ void hdmi_work(struct work_struct *work) if(hdmi->edid.sink_hdmi == 0) video.output_color = VIDEO_OUTPUT_RGB444; - rc = rk30_hdmi_config_video(&video); + rc = hdmi->config_video(&video); if(rc == HDMI_ERROR_SUCESS) { if(hdmi->edid.sink_hdmi) @@ -266,14 +265,14 @@ void hdmi_work(struct work_struct *work) } break; case CONFIG_AUDIO: - rc = rk30_hdmi_config_audio(&(hdmi->audio)); + rc = hdmi->config_audio(&(hdmi->audio)); if(rc == HDMI_ERROR_SUCESS) hdmi->state = PLAY_BACK; break; case PLAY_BACK: if(hdmi->display != HDMI_ENABLE) { - rk30_hdmi_control_output(HDMI_ENABLE); + hdmi->control_output(HDMI_ENABLE); hdmi->display = HDMI_ENABLE; if(hdmi->hdcp_cb) { hdmi->hdcp_cb(); @@ -309,4 +308,5 @@ void hdmi_work(struct work_struct *work) // } hdmi_dbg(hdmi->dev, "[%s] done\n", __FUNCTION__); mutex_unlock(&work_mutex); -} \ No newline at end of file +} + -- 2.34.1