rk3036 hdmi: rk3036 hdmi driver independent from rk616

This commit is contained in:
hjc
2014-07-15 19:03:02 +08:00
parent 0e21a6d15e
commit 302f5ef634
12 changed files with 2348 additions and 2 deletions

View File

@@ -39,7 +39,7 @@ endif
config HDMI_RK616
bool "RK616 HDMI support"
#depends on MFD_RK616 || ARCH_RK3026
depends on MFD_RK616 || ARCH_RK3026
default y
help
Support rk616 hdmi if you say y here
@@ -48,6 +48,16 @@ if HDMI_RK616
source "drivers/video/rockchip/hdmi/chips/rk616/Kconfig"
endif
config HDMI_RK3036
bool "RK3036 HDMI support"
default y
help
Support rk3036 hdmi if you say y here
if HDMI_RK3036
source "drivers/video/rockchip/hdmi/chips/rk3036/Kconfig"
endif
choice
prompt "HDMI Source LCDC select"
config HDMI_SOURCE_LCDC0

View File

@@ -9,4 +9,5 @@ obj-$(CONFIG_HDMI_RK2928) += rk2928/
obj-$(CONFIG_HDMI_RK610) += rk610/
obj-$(CONFIG_HDMI_CAT66121) += cat66121/
obj-$(CONFIG_HDMI_RK616) += rk616/
obj-$(CONFIG_HDMI_RK3036) += rk3036/
obj-y += rk3288/

View File

@@ -0,0 +1,13 @@
config HDCP_RK3036
bool "RK3036 HDCP support"
default n
help
HDCP Interface. This adds the High Definition Content Protection Interface.
See http://www.digital-cp.com/ for HDCP specification.
config HDCP_RK3036_DEBUG
bool "RK3036 HDCP Debugging"
depends on HDCP_RK3036
default n
help
Enableds verbose debugging the the HDCP drivers

View File

@@ -0,0 +1,9 @@
#
# Makefile for HDMI linux kernel module.
#
ccflags-$(CONFIG_RK_HDMI_DEBUG) = -DDEBUG -DHDMI_DEBUG
ccflags-$(CONFIG_HDCP_RK616_DEBUG) = -DHDCP_DEBUG
obj-$(CONFIG_HDMI_RK3036) += rk3036_hdmi_hw.o rk3036_hdmi.o
obj-$(CONFIG_HDCP_RK3036) += rk3036_hdmi_hdcp.o rk3036_hdcp.o

View File

@@ -0,0 +1,563 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/miscdevice.h>
#include <linux/workqueue.h>
#include <linux/firmware.h>
#include "rk3036_hdmi.h"
#include "rk3036_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;
}
rk3036_hdcp_disable();
rk3036_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 = rk3036_hdcp_start_authentication();
if (status != HDCP_OK) {
DBG("HDCP: authentication failed");
hdcp_wq_authentication_failure();
} else {
hdcp->hdcp_state = HDCP_WAIT_KSV_LIST;
// hdcp->hdcp_state = HDCP_LINK_INTEGRITY_CHECK;
}
}
/*-----------------------------------------------------------------------------
* Function: hdcp_wq_check_bksv
*-----------------------------------------------------------------------------
*/
static void hdcp_wq_check_bksv(void)
{
int status = HDCP_OK;
DBG("Check BKSV start");
status = rk3036_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)
{
rk3036_hdmi_control_output(true);
printk(KERN_INFO "HDCP: authentication pass");
}
/*-----------------------------------------------------------------------------
* Function: hdcp_wq_disable
*-----------------------------------------------------------------------------
*/
static void hdcp_wq_disable(int event)
{
printk(KERN_INFO "HDCP: disabled");
hdcp_cancel_work(&hdcp->pending_wq_event);
rk3036_hdcp_disable();
if(event == HDCP_DISABLE_CTL) {
hdcp->hdcp_state = HDCP_DISABLED;
if(hdcp->hdmi_state == HDMI_STARTED)
rk3036_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 status)
{
char interrupt1;
char interrupt2;
rk3036_hdcp_interrupt(&interrupt1, &interrupt2);
DBG("%s 0x%02x 0x%02x", __FUNCTION__, interrupt1, interrupt2);
if(interrupt1 & m_INT_HDCP_ERR)
{
if( (hdcp->hdcp_state != HDCP_DISABLED) &&
(hdcp->hdcp_state != HDCP_ENABLE_PENDING) )
{
hdcp_submit_work(HDCP_FAIL_EVENT, 0);
}
}
else if(interrupt1 & (m_INT_BKSV_READY | m_INT_BKSV_UPDATE))
hdcp_submit_work(HDCP_KSV_LIST_RDY_EVENT, 0);
else if(interrupt1 & m_INT_AUTH_SUCCESS)
hdcp_submit_work(HDCP_AUTH_PASS_EVENT, 0);
}
/*-----------------------------------------------------------------------------
* Function: hdcp_power_on_cb
*-----------------------------------------------------------------------------
*/
static int hdcp_power_on_cb(void)
{
DBG("%s", __FUNCTION__);
// return rk3036_hdcp_load_key2mem(hdcp->keys);
return HDCP_OK;
}
/*-----------------------------------------------------------------------------
* 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(5000));
}
// 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);
printk(KERN_INFO "HDCP: load 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 rk3036_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;
}
rk3036_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 rk3036_hdcp_exit(void)
{
device_remove_file(mdev.this_device, &dev_attr_enable);
misc_deregister(&mdev);
if(hdcp->keys)
kfree(hdcp->keys);
if(hdcp->invalidkeys)
kfree(hdcp->invalidkeys);
kfree(hdcp);
}
module_init(rk3036_hdcp_init);
module_exit(rk3036_hdcp_exit);

View File

@@ -0,0 +1,190 @@
#ifndef __RK3036_HDCP_H__
#define __RK3036_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
/* HDCP DDC Clock */
#define HDCP_DDC_CLK 100000
/* Authentication retry times */
#define HDCP_INFINITE_REAUTH 0x100
/* HDCP Regs */
#define HDCP_CTRL1 0x52
#define m_AUTH_START (1 << 7)
#define m_BKSV_VALID (1 << 6)
#define m_BKSV_INVALID (1 << 5)
#define m_ENCRYPT_ENABLE (1 << 4)
#define m_AUTH_STOP (1 << 3)
#define m_ADVANED_ENABLE (1 << 2)
#define m_HDMI_DVI (1 << 1)
#define m_HDCP_RESET (1 << 0)
#define v_AUTH_START(n) (n << 7)
#define v_BKSV_VALID(n) (n << 6)
#define v_BKSV_INVALID(n) (n << 5)
#define v_ENCRYPT_ENABLE(n) (n << 4)
#define v_AUTH_STOP(n) (n << 3)
#define v_ADVANED_ENABLE(n) (n << 2)
#define v_HDMI_DVI(n) (n << 1)
#define v_HDCP_RESET(n) (n << 0)
#define HDCP_CTRL2 0x53
#define m_DISABLE_127_CHECK (1 << 7)
#define m_SKIP_BKSV_CHECK (1 << 6)
#define m_ENABLE_PJ_CHECK (1 << 5)
#define m_DISABLE_DEVICE_NUMBER_CHECK (1 << 4)
#define m_DELAY_RI_1_CLK (1 << 3)
#define m_USE_PRESET_AN (1 << 2)
#define m_KEY_COMBINATION (3 << 0)
#define v_DISABLE_127_CHECK(n) (n << 7)
#define v_SKIP_BKSV_CHECK(n) (n << 6)
#define v_ENABLE_PJ_CHECK(n) (n << 5)
#define v_DISABLE_DEVICE_NUMBER_CHECK(n)(n << 4)
#define v_DELAY_RI_1_CLK(n) (n << 3)
#define v_USE_PRESET_AN(n) (n << 2)
#define v_KEY_COMBINATION(n) (n << 0)
#define HDCP_KEY_STATUS 0x54
#define m_KEY_READY (1 << 0)
#define HDCP_CTRL_SOFT 0x57
#define m_DISABLE_127_CHECK (1 << 7)
#define m_SKIP_BKSV_CHECK (1 << 6)
#define m_NOT_AUTHENTICATED (1 << 5)
#define m_ENCRYPTED (1 << 4)
#define m_ADVANCED_CIPHER (1 << 3)
#define HDCP_BCAPS_RX 0x58
#define HDCP_TIMER_100MS 0x63
#define HDCP_TIMER_5S 0x64
#define HDCP_ERROR 0x65
#define m_DDC_NO_ACK (1 << 3)
#define m_PJ_MISMACH (1 << 2)
#define m_RI_MISMACH (1 << 1)
#define m_BKSV_WRONG (1 << 0)
#define HDCP_KSV_BYTE0 0x66
#define HDCP_KSV_BYTE1 0x67
#define HDCP_KSV_BYTE2 0x68
#define HDCP_KSV_BYTE3 0x69
#define HDCP_KSV_BYTE4 0x6a
#define HDCP_AN_SEED 0x6c
#define HDCP_BCAPS_TX 0x80
#define HDCP_BSTATE_0 0x81
#define HDCP_BSTATE_1 0x82
#define HDCP_KEY_FIFO 0x98
#define HDCP_INT_MASK1 0xc2
#define HDCP_INT_STATUS1 0xc3
#define m_INT_HDCP_ERR (1 << 7)
#define m_INT_BKSV_READY (1 << 6)
#define m_INT_BKSV_UPDATE (1 << 5)
#define m_INT_AUTH_SUCCESS (1 << 4)
#define m_INT_AUTH_READY (1 << 3)
#define HDCP_INT_MASK2 0xc4
#define HDCP_INT_STATUS2 0xc5
#define m_INT_SOFT_MODE_READY (1 << 7)
#define m_INT_AUTH_M0_REDAY (1 << 6)
#define m_INT_1st_FRAME_ARRIVE (1 << 5)
#define m_INT_AN_READY (1 << 4)
#define m_INT_ENCRYPTED (1 << 2)
#define m_INT_NOT_ENCRYPTED_AVMUTE (1 << 1)
#define m_INT_NOT_ENCRYPTED_AVUNMUTE (1 << 0)
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
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 rk3036_hdcp_disable(void);
extern int rk3036_hdcp_start_authentication(void);
extern int rk3036_hdcp_check_bksv(void);
extern int rk3036_hdcp_load_key2mem(struct hdcp_keys *key);
extern void rk3036_hdcp_interrupt(char *status1, char *status2);
#endif /* __rk3036_HDCP_H__ */

View File

@@ -0,0 +1,483 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/uaccess.h>
#include <linux/of_gpio.h>
#include <linux/rk_fb.h>
#if defined(CONFIG_DEBUG_FS)
#include <linux/fs.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#endif
#include "rk3036_hdmi.h"
#include "rk3036_hdmi_hw.h"
static struct rk_hdmi_device *hdmi_dev;
#if defined(CONFIG_DEBUG_FS)
static int rk3036_hdmi_reg_show(struct seq_file *s, void *v)
{
int i = 0;
u32 val = 0;
seq_puts(s, "\n\n>>>rk3036_ctl reg");
for (i = 0; i < 16; i++)
seq_printf(s, " %2x", i);
seq_puts(s,
"\n-----------------------------------------------------------------");
for (i = 0; i <= PHY_PRE_DIV_RATIO; i++) {
hdmi_readl(hdmi_dev, i, &val);
if (i % 16 == 0)
seq_printf(s, "\n>>>rk3036_ctl %2x:", i);
seq_printf(s, " %02x", val);
}
seq_puts(s,
"\n-----------------------------------------------------------------\n");
return 0;
}
static ssize_t rk3036_hdmi_reg_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
u32 reg;
u32 val;
char kbuf[25];
struct hdmi *hdmi_drv = &hdmi_dev->driver;
if (copy_from_user(kbuf, buf, count))
return -EFAULT;
sscanf(kbuf, "%x%x", &reg, &val);
if ((reg < 0) || (reg > 0xed)) {
dev_info(hdmi_drv->dev, "it is no hdmi reg\n");
return count;
}
dev_info(hdmi_drv->dev, "/**********rk3036 reg config******/");
dev_info(hdmi_drv->dev, "\n reg=%x val=%x\n", reg, val);
hdmi_writel(hdmi_dev, reg, val);
return count;
}
static int rk3036_hdmi_reg_open(struct inode *inode, struct file *file)
{
return single_open(file, rk3036_hdmi_reg_show, NULL);
}
static const struct file_operations rk3036_hdmi_reg_fops = {
.owner = THIS_MODULE,
.open = rk3036_hdmi_reg_open,
.read = seq_read,
.write = rk3036_hdmi_reg_write,
.llseek = seq_lseek,
.release = single_release,
};
#endif
static int rk3036_hdmi_clk_enable(struct rk_hdmi_device *hdmi_dev)
{
if (!hdmi_dev->clk_on) {
clk_prepare_enable(hdmi_dev->hclk);
spin_lock(&hdmi_dev->reg_lock);
hdmi_dev->clk_on = 1;
spin_unlock(&hdmi_dev->reg_lock);
}
return 0;
}
static int rk3036_hdmi_clk_disable(struct rk_hdmi_device *hdmi_dev)
{
if (!hdmi_dev->clk_on) {
spin_lock(&hdmi_dev->reg_lock);
hdmi_dev->clk_on = 0;
spin_unlock(&hdmi_dev->reg_lock);
clk_disable_unprepare(hdmi_dev->hclk);
}
return 0;
}
int rk3036_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))
{
struct hdmi *hdmi_drv = &hdmi_dev->driver;
if (hdmi_drv == NULL)
return HDMI_ERROR_FALSE;
hdmi_drv->hdcp_cb = hdcp_cb;
hdmi_drv->hdcp_irq_cb = hdcp_irq_cb;
hdmi_drv->hdcp_power_on_cb = hdcp_power_on_cb;
hdmi_drv->hdcp_power_off_cb = hdcp_power_off_cb;
return HDMI_ERROR_SUCESS;
}
static void rk3036_hdmi_early_suspend(void)
{
struct hdmi *hdmi_drv = &hdmi_dev->driver;
hdmi_dbg(hdmi_drv->dev, "hdmi enter early suspend pwr %d state %d\n",
hdmi_drv->pwr_mode, hdmi_drv->state);
flush_delayed_work(&hdmi_drv->delay_work);
mutex_lock(&hdmi_drv->enable_mutex);
hdmi_drv->suspend = 1;
if (!hdmi_drv->enable) {
mutex_unlock(&hdmi_drv->enable_mutex);
return;
}
if (hdmi_drv->irq)
disable_irq(hdmi_drv->irq);
mutex_unlock(&hdmi_drv->enable_mutex);
hdmi_drv->command = HDMI_CONFIG_ENABLE;
init_completion(&hdmi_drv->complete);
hdmi_drv->wait = 1;
queue_delayed_work(hdmi_drv->workqueue, &hdmi_drv->delay_work, 0);
wait_for_completion_interruptible_timeout(&hdmi_drv->complete,
msecs_to_jiffies(5000));
flush_delayed_work(&hdmi_drv->delay_work);
}
static void rk3036_hdmi_early_resume(void)
{
struct hdmi *hdmi_drv = &hdmi_dev->driver;
hdmi_dbg(hdmi_drv->dev, "hdmi exit early resume\n");
mutex_lock(&hdmi_drv->enable_mutex);
hdmi_drv->suspend = 0;
rk3036_hdmi_initial(hdmi_drv);
if (hdmi_drv->enable && hdmi_drv->irq) {
enable_irq(hdmi_drv->irq);
rk3036_hdmi_irq(hdmi_drv);
}
queue_delayed_work(hdmi_drv->workqueue, &hdmi_drv->delay_work,
msecs_to_jiffies(10));
mutex_unlock(&hdmi_drv->enable_mutex);
}
static int rk3036_hdmi_fb_event_notify(struct notifier_block *self,
unsigned long action, void *data)
{
struct fb_event *event = data;
int blank_mode = *((int *)event->data);
if (action == FB_EARLY_EVENT_BLANK) {
switch (blank_mode) {
case FB_BLANK_UNBLANK:
break;
default:
rk3036_hdmi_early_suspend();
break;
}
} else if (action == FB_EVENT_BLANK) {
switch (blank_mode) {
case FB_BLANK_UNBLANK:
rk3036_hdmi_early_resume();
break;
default:
break;
}
}
return NOTIFY_OK;
}
static struct notifier_block rk3036_hdmi_fb_notifier = {
.notifier_call = rk3036_hdmi_fb_event_notify,
};
static void rk3036_delay_work_func(struct work_struct *work)
{
struct hdmi *hdmi_drv = &hdmi_dev->driver;
if (hdmi_drv->suspend == 0) {
if (hdmi_drv->enable == 1)
rk3036_hdmi_irq(hdmi_drv);
if (hdmi_drv->irq == 0)
queue_delayed_work(hdmi_drv->workqueue, &hdmi_dev->rk3036_delay_work,
msecs_to_jiffies(100));
}
}
static irqreturn_t rk3036_hdmi_irq_func(int irq, void *dev_id)
{
struct hdmi *hdmi_drv = &hdmi_dev->driver;
if ((hdmi_drv->suspend == 0) && (hdmi_drv->enable == 1)) {
hdmi_dbg(hdmi_drv->dev,
"line = %d, rk3036_hdmi_irq_func irq triggered.\n",
__LINE__);
rk3036_hdmi_irq(hdmi_drv);
}
return IRQ_HANDLED;
}
static int rk3036_hdmi_drv_init(struct hdmi *hdmi_drv)
{
int ret = 0;
int lcdc_id = 0;
struct rk_screen screen;
rk_fb_get_prmry_screen(&screen);
/* hdmi is extend as default,TODO modify if hdmi is primary */
/*lcdc_id = (screen.lcdc_id == 0) ? 1 : 0;*/
lcdc_id = screen.lcdc_id;//for box,hdmi is primary
if (lcdc_id == 0)
hdmi_drv->lcdc = rk_get_lcdc_drv("lcdc0");
else
hdmi_drv->lcdc = rk_get_lcdc_drv("lcdc1");
if (IS_ERR(hdmi_drv->lcdc)) {
dev_err(hdmi_drv->dev,
"can not connect to video source lcdc\n");
ret = -ENXIO;
return ret;
}
hdmi_drv->xscale = 100;
hdmi_drv->yscale = 100;
/*spin_lock_init(&hdmi_drv->irq_lock);*/
mutex_init(&hdmi_drv->enable_mutex);
hdmi_sys_init(hdmi_drv);
ret = rk3036_hdmi_initial(hdmi_drv);
return ret;
}
#if defined(CONFIG_OF)
static const struct of_device_id rk3036_hdmi_of_match[] = {
{.compatible = "rockchip,rk3036-hdmi",},
{}
};
MODULE_DEVICE_TABLE(of, rk3036_hdmi_of_match);
#endif
static int rk3036_hdmi_probe(struct platform_device *pdev)
{
int ret;
struct hdmi *hdmi_drv;
struct resource *res;
hdmi_dev = devm_kzalloc(&pdev->dev, sizeof(struct rk_hdmi_device),
GFP_KERNEL);
if (!hdmi_dev) {
dev_err(&pdev->dev, ">>rk3036_hdmi kmalloc fail!");
return -ENOMEM;
}
hdmi_drv = &hdmi_dev->driver;
hdmi_drv->dev = &pdev->dev;
platform_set_drvdata(pdev, hdmi_dev);
spin_lock_init(&hdmi_dev->reg_lock);
#ifdef CONFIG_SWITCH
hdmi_drv->switch_hdmi.name = "hdmi";
switch_dev_register(&(hdmi_drv->switch_hdmi));
#endif
hdmi_register_display_sysfs(hdmi_drv, NULL);
fb_register_client(&rk3036_hdmi_fb_notifier);
hdmi_drv->workqueue = create_singlethread_workqueue("hdmi");
INIT_DELAYED_WORK(&(hdmi_drv->delay_work), hdmi_work);
INIT_DELAYED_WORK(&hdmi_dev->rk3036_delay_work, rk3036_delay_work_func);
/* enable clk */
hdmi_dev->hclk = devm_clk_get(hdmi_drv->dev, "pclk_hdmi");
if (IS_ERR(hdmi_dev->hclk)) {
dev_err(hdmi_drv->dev, "Unable to get hdmi hclk\n");
ret = -ENXIO;
goto err1;
}
rk3036_hdmi_clk_enable(hdmi_dev); /* enable clk may move to irq func */
hdmi_dev->hclk_rate = clk_get_rate(hdmi_dev->hclk);
/* request and remap iomem */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(hdmi_drv->dev, "Unable to get register resource\n");
ret = -ENXIO;
goto err2;
}
hdmi_dev->regbase_phy = res->start;
hdmi_dev->regsize_phy = resource_size(res);
hdmi_dev->regbase = devm_ioremap_resource(hdmi_drv->dev, res);
if (IS_ERR(hdmi_dev->regbase)) {
ret = PTR_ERR(hdmi_dev->regbase);
dev_err(hdmi_drv->dev, "cannot ioremap registers,err=%d\n",
ret);
goto err2;
}
if (rk3036_hdmi_drv_init(hdmi_drv))
goto err0;
/* get the IRQ */
hdmi_drv->irq = platform_get_irq(pdev, 0);
if (hdmi_drv->irq <= 0) {
dev_err(hdmi_drv->dev, "failed to get hdmi irq resource (%d).\n",
hdmi_drv->irq);
hdmi_drv->irq = 0;
} else {
/* request the IRQ */
ret = devm_request_irq(hdmi_drv->dev, hdmi_drv->irq,
rk3036_hdmi_irq_func, 0,
dev_name(hdmi_drv->dev), hdmi_drv);
if (ret) {
dev_err(hdmi_drv->dev, "hdmi request_irq failed (%d)\n",
ret);
goto err2;
}
}
#if defined(CONFIG_DEBUG_FS)
hdmi_dev->debugfs_dir = debugfs_create_dir("rk3036", NULL);
if (IS_ERR(hdmi_dev->debugfs_dir)) {
dev_err(hdmi_drv->dev,
"failed to create debugfs dir for rk3036!\n");
} else {
debugfs_create_file("hdmi", S_IRUSR,
hdmi_dev->debugfs_dir, hdmi_drv,
&rk3036_hdmi_reg_fops);
}
#endif
queue_delayed_work(hdmi_drv->workqueue, &hdmi_dev->rk3036_delay_work,
msecs_to_jiffies(0));
dev_info(hdmi_drv->dev, "rk3036 hdmi probe success.\n");
return 0;
err2:
rk3036_hdmi_clk_disable(hdmi_dev);
err1:
fb_unregister_client(&rk3036_hdmi_fb_notifier);
hdmi_unregister_display_sysfs(hdmi_drv);
#ifdef CONFIG_SWITCH
switch_dev_unregister(&(hdmi_drv->switch_hdmi));
#endif
err0:
hdmi_dbg(hdmi_drv->dev, "rk3036 hdmi probe error.\n");
kfree(hdmi_dev);
hdmi_dev = NULL;
return ret;
}
static int rk3036_hdmi_remove(struct platform_device *pdev)
{
struct rk_hdmi_device *hdmi_dev = platform_get_drvdata(pdev);
struct hdmi *hdmi_drv = NULL;
if (hdmi_dev) {
hdmi_drv = &hdmi_dev->driver;
mutex_lock(&hdmi_drv->enable_mutex);
if (!hdmi_drv->suspend && hdmi_drv->enable && hdmi_drv->irq)
disable_irq(hdmi_drv->irq);
mutex_unlock(&hdmi_drv->enable_mutex);
if (hdmi_drv->irq)
free_irq(hdmi_drv->irq, NULL);
flush_workqueue(hdmi_drv->workqueue);
destroy_workqueue(hdmi_drv->workqueue);
#ifdef CONFIG_SWITCH
switch_dev_unregister(&(hdmi_drv->switch_hdmi));
#endif
hdmi_unregister_display_sysfs(hdmi_drv);
#ifdef CONFIG_HAS_EARLYSUSPEND
unregister_early_suspend(&hdmi_drv->early_suspend);
#endif
fb_destroy_modelist(&hdmi_drv->edid.modelist);
if (hdmi_drv->edid.audio)
kfree(hdmi_drv->edid.audio);
if (hdmi_drv->edid.specs) {
if (hdmi_drv->edid.specs->modedb)
kfree(hdmi_drv->edid.specs->modedb);
kfree(hdmi_drv->edid.specs);
}
hdmi_dbg(hdmi_drv->dev, "rk3036 hdmi removed.\n");
kfree(hdmi_dev);
hdmi_dev = NULL;
}
return 0;
}
static void rk3036_hdmi_shutdown(struct platform_device *pdev)
{
struct rk_hdmi_device *hdmi_dev = platform_get_drvdata(pdev);
struct hdmi *hdmi_drv = NULL;
if (hdmi_dev) {
hdmi_drv = &hdmi_dev->driver;
#ifdef CONFIG_HAS_EARLYSUSPEND
unregister_early_suspend(&hdmi_drv->early_suspend);
#endif
flush_delayed_work(&hdmi_drv->delay_work);
mutex_lock(&hdmi_drv->enable_mutex);
hdmi_drv->suspend = 1;
if (!hdmi_drv->enable) {
mutex_unlock(&hdmi_drv->enable_mutex);
return;
}
if (hdmi_drv->irq)
disable_irq(hdmi_drv->irq);
mutex_unlock(&hdmi_drv->enable_mutex);
}
hdmi_dbg(hdmi_drv->dev, "rk3036 hdmi shut down.\n");
}
static struct platform_driver rk3036_hdmi_driver = {
.probe = rk3036_hdmi_probe,
.remove = rk3036_hdmi_remove,
.driver = {
.name = "rk3036-hdmi",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(rk3036_hdmi_of_match),
},
.shutdown = rk3036_hdmi_shutdown,
};
static int __init rk3036_hdmi_init(void)
{
return platform_driver_register(&rk3036_hdmi_driver);
}
static void __exit rk3036_hdmi_exit(void)
{
platform_driver_unregister(&rk3036_hdmi_driver);
}
late_initcall(rk3036_hdmi_init);
module_exit(rk3036_hdmi_exit);

View File

@@ -0,0 +1,38 @@
#ifndef __RK3036_HDMI_H__
#define __RK3036_HDMI_H__
#include "../../rk_hdmi.h"
enum {
INPUT_IIS,
INPUT_SPDIF
};
#if defined(CONFIG_SND_RK_SOC_HDMI_SPDIF)
#define HDMI_CODEC_SOURCE_SELECT INPUT_SPDIF
#else
#define HDMI_CODEC_SOURCE_SELECT INPUT_IIS
#endif
extern void rk3036_hdmi_control_output(struct hdmi *hdmi, int enable);
extern int rk3036_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));
struct rk_hdmi_device {
int clk_on;
spinlock_t reg_lock;
struct hdmi driver;
void __iomem *regbase;
int regbase_phy;
int regsize_phy;
struct clk *pd;
struct clk *hclk; /* HDMI AHP clk */
struct delayed_work rk3036_delay_work;
struct work_struct rk3036_irq_work_struct;
struct dentry *debugfs_dir;
unsigned int hclk_rate;
};
#endif /* __RK3036_HDMI_H__ */

View File

@@ -0,0 +1,143 @@
#include <linux/delay.h>
#include "rk3036_hdmi.h"
#include "rk3036_hdmi_hw.h"
#include "rk3036_hdcp.h"
#define HDCPWrReg HDMIWrReg
#define HDCPRdReg HDMIRdReg
#define HDCPMskReg(temp,addr,Msk,val) do{ \
HDMIRdReg(addr,&temp); \
HDMIWrReg(addr, ((val)&(Msk))|(temp&(~Msk))); \
}while(0)
void rk3036_hdcp_disable(void)
{
char temp;
// Diable HDCP Interrupt
HDCPWrReg(HDCP_INT_MASK1, 0x00);
// Stop and Reset HDCP
HDCPMskReg(temp, HDCP_CTRL1, m_ENCRYPT_ENABLE | m_AUTH_STOP | m_HDCP_RESET,
v_ENCRYPT_ENABLE(0) | v_AUTH_STOP(1) | v_HDCP_RESET(1) );
}
int rk3036_hdcp_load_key2mem(struct hdcp_keys *key)
{
int i;
DBG("HDCP: rk3036_hdcp_load_key2mem start");
// Write 40 private key
for(i = 0; i < HDCP_PRIVATE_KEY_SIZE; i++)
HDCPWrReg(HDCP_KEY_FIFO, key->DeviceKey[i]);
// Write 1st aksv
for(i = 0; i < 5; i++)
HDCPWrReg(HDCP_KEY_FIFO, key->KSV[i]);
// Write 2nd aksv
for(i = 0; i < 5; i++)
HDCPWrReg(HDCP_KEY_FIFO, key->KSV[i]);
DBG("HDCP: rk3036_hdcp_load_key2mem end");
return HDCP_OK;
}
int rk3036_hdcp_start_authentication(void)
{
char temp;
int retry = 0;
if(hdcp->keys == NULL) {
printk(KERN_ERR "HDCP: key is not loaded\n");
return HDCP_KEY_ERR;
}
// Select TMDS CLK to configure regs
HDCPMskReg(temp, SYS_CTRL, m_REG_CLK_SOURCE, v_REG_CLK_SOURCE_TMDS);
HDCPRdReg(HDCP_KEY_STATUS,&temp);
while( ( temp & m_KEY_READY) == 0 ) {
if(retry > 10) {
printk(KERN_ERR "HDCP: loaded key error\n");
return HDCP_KEY_ERR;
}
rk3036_hdcp_load_key2mem(hdcp->keys);
msleep(1);
HDCPRdReg(HDCP_KEY_STATUS,&temp);
}
// Config DDC bus clock: ddc_clk = reg_clk/4*(reg 0x4c 0x4b)
DBG("TMDS frequency %d", hdmi->tmdsclk);
retry = hdmi->tmdsclk/(HDCP_DDC_CLK*4);
HDCPWrReg(DDC_CLK_L, retry & 0xFF);
HDCPWrReg(DDC_CLK_H, (retry >> 8) & 0xFF);
HDCPWrReg(HDCP_CTRL2, 0x00);
//Enable interrupt
HDCPWrReg(HDCP_INT_MASK1, m_INT_HDCP_ERR | m_INT_BKSV_READY | m_INT_BKSV_UPDATE | m_INT_AUTH_SUCCESS | m_INT_AUTH_READY);
// HDCPWrReg(HDCP_INT_MASK2, 0xFF);
//Start authentication
HDCPMskReg(temp, HDCP_CTRL1, m_AUTH_START | m_ENCRYPT_ENABLE | m_ADVANED_ENABLE, v_AUTH_START(1) | v_ENCRYPT_ENABLE(1) | v_ADVANED_ENABLE(0));
return HDCP_OK;
}
int rk3036_hdcp_check_bksv(void)
{
int i, j;
char temp = 0, bksv[5];
char *invalidkey;
for(i = 0; i < 5; i++) {
HDCPRdReg(HDCP_KSV_BYTE0 + (4 - i),&temp);
bksv[i] = temp & 0xFF;
}
DBG("bksv is 0x%02x%02x%02x%02x%02x", bksv[0], bksv[1], bksv[2], bksv[3], bksv[4]);
temp = 0;
for (i = 0; i < 5; i++)
{
for (j = 0; j < 8; j++)
{
if (bksv[i] & 0x01)
{
temp++;
}
bksv[i] >>= 1;
}
}
if (temp != 20)
return HDCP_KSV_ERR;
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");
HDCPMskReg(temp, HDCP_CTRL1, m_BKSV_INVALID | m_ENCRYPT_ENABLE, v_BKSV_INVALID(1) | v_ENCRYPT_ENABLE(1));
return HDCP_KSV_ERR;
}
}
HDCPMskReg(temp, HDCP_CTRL1, m_BKSV_VALID | m_ENCRYPT_ENABLE, v_BKSV_VALID(1) | v_ENCRYPT_ENABLE(1));
return HDCP_OK;
}
void rk3036_hdcp_interrupt(char *status1, char *status2)
{
char interrupt1 = 0;
char interrupt2 = 0;
char temp =0;
HDCPRdReg(HDCP_INT_STATUS1,&interrupt1);
HDCPRdReg(HDCP_INT_STATUS2,&interrupt2);
if(interrupt1) {
HDCPWrReg(HDCP_INT_STATUS1, interrupt1);
if(interrupt1 & m_INT_HDCP_ERR){
HDCPRdReg(HDCP_ERROR,&temp);
printk(KERN_INFO "HDCP: Error 0x%02x\n", temp);
}
}
if(interrupt2)
HDCPWrReg(HDCP_INT_STATUS2, interrupt2);
*status1 = interrupt1;
*status2 = interrupt2;
}

View File

@@ -0,0 +1,573 @@
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/of_irq.h>
#include "rk3036_hdmi.h"
#include "rk3036_hdmi_hw.h"
static int __maybe_unused rk3036_hdmi_show_reg(struct hdmi *hdmi_drv)
{
int i = 0;
u32 val = 0;
struct rk_hdmi_device *hdmi_dev = container_of(hdmi_drv,
struct rk_hdmi_device,
driver);
printk("\n>>>rk3036_ctl reg");
for (i = 0; i < 16; i++)
printk(" %2x", i);
printk("\n-----------------------------------------------------------------");
for (i = 0; i <= PHY_PRE_DIV_RATIO; i++) {
hdmi_readl(hdmi_dev, i, &val);
if (i % 16 == 0)
printk("\n>>>rk3036_ctl %2x:", i);
printk(" %02x", val);
}
printk("\n-----------------------------------------------------------------\n");
return 0;
}
static inline void delay100us(void)
{
msleep(1);
}
static void rk3036_hdmi_av_mute(struct hdmi *hdmi_drv, bool enable)
{
struct rk_hdmi_device *hdmi_dev = container_of(hdmi_drv,
struct rk_hdmi_device,
driver);
hdmi_writel(hdmi_dev, AV_MUTE,
v_AUDIO_MUTE(enable) | v_VIDEO_MUTE(enable));
}
static void rk3036_hdmi_sys_power(struct hdmi *hdmi_drv, bool enable)
{
struct rk_hdmi_device *hdmi_dev = container_of(hdmi_drv,
struct rk_hdmi_device,
driver);
if (enable)
hdmi_msk_reg(hdmi_dev, SYS_CTRL, m_POWER, v_PWR_ON);
else
hdmi_msk_reg(hdmi_dev, SYS_CTRL, m_POWER, v_PWR_OFF);
}
static void rk3036_hdmi_set_pwr_mode(struct hdmi *hdmi_drv, int mode)
{
struct rk_hdmi_device *hdmi_dev = container_of(hdmi_drv,
struct rk_hdmi_device,
driver);
if (hdmi_drv->pwr_mode == mode)
return;
hdmi_dbg(hdmi_drv->dev, "%s change pwr_mode %d --> %d\n", __func__,
hdmi_drv->pwr_mode, mode);
switch (mode) {
case NORMAL:
hdmi_dbg(hdmi_drv->dev,
"%s change pwr_mode NORMAL pwr_mode = %d, mode = %d\n",
__func__, hdmi_drv->pwr_mode, mode);
rk3036_hdmi_sys_power(hdmi_drv, false);
hdmi_writel(hdmi_dev, PHY_DRIVER, 0x99);
hdmi_writel(hdmi_dev, PHY_PRE_EMPHASIS, 0x0f);
hdmi_writel(hdmi_dev, PHY_SYS_CTL, 0x15);
hdmi_writel(hdmi_dev, PHY_SYS_CTL, 0x14);
hdmi_writel(hdmi_dev, PHY_SYS_CTL, 0x10);
hdmi_writel(hdmi_dev, PHY_CHG_PWR, 0x0f);
hdmi_writel(hdmi_dev, 0xce, 0x00);
hdmi_writel(hdmi_dev, 0xce, 0x01);
rk3036_hdmi_av_mute(hdmi_drv, 1);
rk3036_hdmi_sys_power(hdmi_drv, true);
break;
case LOWER_PWR:
hdmi_dbg(hdmi_drv->dev,
"%s change pwr_mode LOWER_PWR pwr_mode = %d, mode = %d\n",
__func__, hdmi_drv->pwr_mode, mode);
rk3036_hdmi_av_mute(hdmi_drv, 0);
rk3036_hdmi_sys_power(hdmi_drv, false);
hdmi_writel(hdmi_dev, PHY_DRIVER, 0x00);
hdmi_writel(hdmi_dev, PHY_PRE_EMPHASIS, 0x00);
hdmi_writel(hdmi_dev, PHY_CHG_PWR, 0x00);
hdmi_writel(hdmi_dev, PHY_SYS_CTL, 0x17);
break;
default:
hdmi_dbg(hdmi_drv->dev, "unkown rk3036 hdmi pwr mode %d\n",
mode);
}
hdmi_drv->pwr_mode = mode;
}
int rk3036_hdmi_detect_hotplug(struct hdmi *hdmi_drv)
{
int value = 0;
struct rk_hdmi_device *hdmi_dev = container_of(hdmi_drv,
struct rk_hdmi_device,
driver);
hdmi_dbg(hdmi_drv->dev, "[%s] value %02x\n", __func__, value);
hdmi_readl(hdmi_dev, HDMI_STATUS, &value);
value &= m_HOTPLUG;
if (value == m_HOTPLUG)
return HDMI_HPD_ACTIVED;
else if (value)
return HDMI_HPD_INSERT;
else
return HDMI_HPD_REMOVED;
}
int rk3036_hdmi_insert(struct hdmi *hdmi_drv)
{
rk3036_hdmi_set_pwr_mode(hdmi_drv, NORMAL);
return 0;
}
int rk3036_hdmi_read_edid(struct hdmi *hdmi_drv, int block, u8 *buf)
{
u32 c = 0;
u8 segment = 0;
u8 offset = 0;
int ret = -1;
int i, j;
int ddc_bus_freq;
int trytime;
int checksum = 0;
struct rk_hdmi_device *hdmi_dev = container_of(hdmi_drv,
struct rk_hdmi_device,
driver);
if (block % 2)
offset = HDMI_EDID_BLOCK_SIZE;
if (block / 2)
segment = 1;
ddc_bus_freq = (hdmi_dev->hclk_rate >> 2) / HDMI_SCL_RATE;
hdmi_writel(hdmi_dev, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
hdmi_writel(hdmi_dev, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
hdmi_dbg(hdmi_drv->dev,
"EDID DATA (Segment = %d Block = %d Offset = %d):\n",
(int)segment, (int)block, (int)offset);
disable_irq(hdmi_drv->irq);
/* Enable edid interrupt */
hdmi_writel(hdmi_dev, INTERRUPT_MASK1, m_INT_EDID_READY);
for (trytime = 0; trytime < 10; trytime++) {
checksum = 0;
hdmi_writel(hdmi_dev, INTERRUPT_STATUS1, 0x04);
/* Set edid fifo first addr */
hdmi_writel(hdmi_dev, EDID_FIFO_OFFSET, 0x00);
/* Set edid word address 0x00/0x80 */
hdmi_writel(hdmi_dev, EDID_WORD_ADDR, offset);
/* Set edid segment pointer */
hdmi_writel(hdmi_dev, EDID_SEGMENT_POINTER, segment);
for (i = 0; i < 10; i++) {
/* Wait edid interrupt */
msleep(10);
c = 0x00;
hdmi_readl(hdmi_dev, INTERRUPT_STATUS1, &c);
if (c & m_INT_EDID_READY)
break;
}
if (c & m_INT_EDID_READY) {
for (j = 0; j < HDMI_EDID_BLOCK_SIZE; j++) {
c = 0;
hdmi_readl(hdmi_dev, 0x50, &c);
buf[j] = c;
checksum += c;
#ifdef HDMI_DEBUG
if (j % 16 == 0)
printk("\n>>>0x%02x: ",j);
printk("0x%02x ", c);
#endif
}
/* clear EDID interrupt reg */
hdmi_writel(hdmi_dev, INTERRUPT_STATUS1,
m_INT_EDID_READY);
if ((checksum & 0xff) == 0) {
ret = 0;
hdmi_dbg(hdmi_drv->dev,
"[%s] edid read sucess\n", __func__);
break;
}
}
}
/*close edid irq*/
hdmi_writel(hdmi_dev, INTERRUPT_MASK1, 0);
enable_irq(hdmi_drv->irq);
return ret;
}
static void rk3036_hdmi_config_avi(struct hdmi *hdmi_drv,
unsigned char vic, unsigned char output_color)
{
int i;
char info[SIZE_AVI_INFOFRAME];
struct rk_hdmi_device *hdmi_dev = container_of(hdmi_drv,
struct rk_hdmi_device,
driver);
memset(info, 0, SIZE_AVI_INFOFRAME);
hdmi_writel(hdmi_dev, 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++)
hdmi_writel(hdmi_dev, CONTROL_PACKET_ADDR + i, info[i]);
}
static int rk3036_hdmi_config_video(struct hdmi *hdmi_drv,
struct hdmi_video_para *vpara)
{
int value;
struct fb_videomode *mode;
struct rk_hdmi_device *hdmi_dev = container_of(hdmi_drv,
struct rk_hdmi_device,
driver);
hdmi_dbg(hdmi_drv->dev, "[%s]\n", __func__);
if (vpara == NULL) {
hdmi_err(hdmi_drv->dev, "[%s] input parameter error\n",
__func__);
return -1;
}
/* Output RGB as default */
vpara->output_color = VIDEO_OUTPUT_RGB444;
if (hdmi_drv->pwr_mode == LOWER_PWR)
rk3036_hdmi_set_pwr_mode(hdmi_drv, NORMAL);
/* Disable video and audio output */
hdmi_writel(hdmi_dev, AV_MUTE, v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1));
/* Input video mode is SDR RGB24bit, Data enable signal from external */
hdmi_writel(hdmi_dev, VIDEO_CONTRL1,
v_VIDEO_INPUT_FORMAT(VIDEO_INPUT_SDR_RGB444) |
v_DE_EXTERNAL);
hdmi_writel(hdmi_dev, VIDEO_CONTRL2,
v_VIDEO_INPUT_BITS(VIDEO_INPUT_8BITS) |
v_VIDEO_OUTPUT_FORMAT(vpara->output_color & 0xFF));
/* Set HDMI Mode */
hdmi_writel(hdmi_dev, 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;
hdmi_writel(hdmi_dev, VIDEO_CONTRL3, value);
/* Set ext video timing */
#if 1
hdmi_writel(hdmi_dev, VIDEO_TIMING_CTL, 0);
mode = (struct fb_videomode *)hdmi_vic_to_videomode(vpara->vic);
if (mode == NULL) {
hdmi_err(hdmi_drv->dev, "[%s] not found vic %d\n", __func__,
vpara->vic);
return -ENOENT;
}
hdmi_drv->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);
hdmi_writel(hdmi_dev, VIDEO_TIMING_CTL, value);
value = mode->left_margin + mode->xres + mode->right_margin +
mode->hsync_len;
hdmi_writel(hdmi_dev, VIDEO_EXT_HTOTAL_L, value & 0xFF);
hdmi_writel(hdmi_dev, VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF);
value = mode->left_margin + mode->right_margin + mode->hsync_len;
hdmi_writel(hdmi_dev, VIDEO_EXT_HBLANK_L, value & 0xFF);
hdmi_writel(hdmi_dev, VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF);
value = mode->left_margin + mode->hsync_len;
hdmi_writel(hdmi_dev, VIDEO_EXT_HDELAY_L, value & 0xFF);
hdmi_writel(hdmi_dev, VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF);
value = mode->hsync_len;
hdmi_writel(hdmi_dev, VIDEO_EXT_HDURATION_L, value & 0xFF);
hdmi_writel(hdmi_dev, VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF);
value = mode->upper_margin + mode->yres + mode->lower_margin +
mode->vsync_len;
hdmi_writel(hdmi_dev, VIDEO_EXT_VTOTAL_L, value & 0xFF);
hdmi_writel(hdmi_dev, VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF);
value = mode->upper_margin + mode->vsync_len + mode->lower_margin;
hdmi_writel(hdmi_dev, 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;
hdmi_writel(hdmi_dev, VIDEO_EXT_VDELAY, value & 0xFF);
value = mode->vsync_len;
hdmi_writel(hdmi_dev, VIDEO_EXT_VDURATION, value & 0xFF);
#endif
if (vpara->output_mode == OUTPUT_HDMI) {
rk3036_hdmi_config_avi(hdmi_drv, vpara->vic,
vpara->output_color);
hdmi_dbg(hdmi_drv->dev, "[%s] sucess output HDMI.\n", __func__);
} else {
hdmi_dbg(hdmi_drv->dev, "[%s] sucess output DVI.\n", __func__);
}
/* rk3028a */
hdmi_writel(hdmi_dev, PHY_PRE_DIV_RATIO, 0x1e);
hdmi_writel(hdmi_dev, PHY_FEEDBACK_DIV_RATIO_LOW, 0x2c);
hdmi_writel(hdmi_dev, PHY_FEEDBACK_DIV_RATIO_HIGH, 0x01);
return 0;
}
static void rk3036_hdmi_config_aai(struct hdmi *hdmi_drv)
{
int i;
char info[SIZE_AUDIO_INFOFRAME];
struct rk_hdmi_device *hdmi_dev = container_of(hdmi_drv,
struct rk_hdmi_device,
driver);
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];
hdmi_writel(hdmi_dev, CONTROL_PACKET_BUF_INDEX, INFOFRAME_AAI);
for (i = 0; i < SIZE_AUDIO_INFOFRAME; i++)
hdmi_writel(hdmi_dev, CONTROL_PACKET_ADDR + i, info[i]);
}
static int rk3036_hdmi_config_audio(struct hdmi *hdmi_drv,
struct hdmi_audio *audio)
{
int rate, N, channel, mclk_fs;
struct rk_hdmi_device *hdmi_dev = container_of(hdmi_drv,
struct rk_hdmi_device,
driver);
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_drv->dev, "[%s] not support such sample rate %d\n",
__func__, audio->rate);
return -ENOENT;
}
/* set_audio source I2S */
if (HDMI_CODEC_SOURCE_SELECT == INPUT_IIS) {
hdmi_writel(hdmi_dev, AUDIO_CTRL1, 0x00);
hdmi_writel(hdmi_dev, AUDIO_SAMPLE_RATE, rate);
hdmi_writel(hdmi_dev, AUDIO_I2S_MODE,
v_I2S_MODE(I2S_STANDARD) | v_I2S_CHANNEL(channel));
hdmi_writel(hdmi_dev, AUDIO_I2S_MAP, 0x00);
/* no swap */
hdmi_writel(hdmi_dev, AUDIO_I2S_SWAPS_SPDIF, 0);
} else {
hdmi_writel(hdmi_dev, AUDIO_CTRL1, 0x08);
/* no swap */
hdmi_writel(hdmi_dev, AUDIO_I2S_SWAPS_SPDIF, 0);
}
/* Set N value */
hdmi_writel(hdmi_dev, AUDIO_N_H, (N >> 16) & 0x0F);
hdmi_writel(hdmi_dev, AUDIO_N_M, (N >> 8) & 0xFF);
hdmi_writel(hdmi_dev, AUDIO_N_L, N & 0xFF);
rk3036_hdmi_config_aai(hdmi_drv);
return 0;
}
void rk3036_hdmi_control_output(struct hdmi *hdmi_drv, int enable)
{
int mutestatus = 0;
struct rk_hdmi_device *hdmi_dev = container_of(hdmi_drv,
struct rk_hdmi_device,
driver);
if (enable) {
if (hdmi_drv->pwr_mode == LOWER_PWR)
rk3036_hdmi_set_pwr_mode(hdmi_drv, NORMAL);
hdmi_readl(hdmi_dev, AV_MUTE, &mutestatus);
if (mutestatus && (m_AUDIO_MUTE | m_VIDEO_BLACK)) {
hdmi_writel(hdmi_dev, AV_MUTE,
v_AUDIO_MUTE(0) | v_VIDEO_MUTE(0));
}
rk3036_hdmi_sys_power(hdmi_drv, true);
rk3036_hdmi_sys_power(hdmi_drv, false);
rk3036_hdmi_sys_power(hdmi_drv, true);
hdmi_writel(hdmi_dev, 0xce, 0x00);
delay100us();
hdmi_writel(hdmi_dev, 0xce, 0x01);
} else {
hdmi_writel(hdmi_dev, AV_MUTE,
v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1));
}
}
int rk3036_hdmi_removed(struct hdmi *hdmi_drv)
{
dev_info(hdmi_drv->dev, "Removed.\n");
if (hdmi_drv->hdcp_power_off_cb)
hdmi_drv->hdcp_power_off_cb();
rk3036_hdmi_set_pwr_mode(hdmi_drv, LOWER_PWR);
return HDMI_ERROR_SUCESS;
}
void rk3036_hdmi_irq(struct hdmi *hdmi_drv)
{
u32 interrupt = 0;
struct rk_hdmi_device *hdmi_dev = container_of(hdmi_drv,
struct rk_hdmi_device,
driver);
hdmi_readl(hdmi_dev, HDMI_STATUS, &interrupt);
if(interrupt) {
hdmi_writel(hdmi_dev, HDMI_STATUS, interrupt);
}
if (interrupt & m_INT_HOTPLUG) {
if (hdmi_drv->state == HDMI_SLEEP)
hdmi_drv->state = WAIT_HOTPLUG;
queue_delayed_work(hdmi_drv->workqueue, &hdmi_drv->delay_work,
msecs_to_jiffies(40));
}/*plug out*/
if (hdmi_drv->hdcp_irq_cb)
hdmi_drv->hdcp_irq_cb(0);
}
static void rk3036_hdmi_reset(struct hdmi *hdmi_drv)
{
u32 val = 0;
u32 msk = 0;
struct rk_hdmi_device *hdmi_dev = container_of(hdmi_drv,
struct rk_hdmi_device,
driver);
hdmi_msk_reg(hdmi_dev, SYS_CTRL, m_RST_DIGITAL, v_NOT_RST_DIGITAL);
delay100us();
hdmi_msk_reg(hdmi_dev, SYS_CTRL, m_RST_ANALOG, v_NOT_RST_ANALOG);
delay100us();
msk = m_REG_CLK_INV | m_REG_CLK_SOURCE | m_POWER | m_INT_POL;
val = v_REG_CLK_INV | v_REG_CLK_SOURCE_SYS | v_PWR_ON | v_INT_POL_HIGH;
hdmi_msk_reg(hdmi_dev, SYS_CTRL, msk, val);
rk3036_hdmi_set_pwr_mode(hdmi_drv, LOWER_PWR);
}
int rk3036_hdmi_initial(struct hdmi *hdmi_drv)
{
int rc = HDMI_ERROR_SUCESS;
hdmi_drv->pwr_mode = NORMAL;
hdmi_drv->remove = rk3036_hdmi_removed;
hdmi_drv->control_output = rk3036_hdmi_control_output;
hdmi_drv->config_video = rk3036_hdmi_config_video;
hdmi_drv->config_audio = rk3036_hdmi_config_audio;
hdmi_drv->detect_hotplug = rk3036_hdmi_detect_hotplug;
hdmi_drv->read_edid = rk3036_hdmi_read_edid;
hdmi_drv->insert = rk3036_hdmi_insert;
rk3036_hdmi_reset_pclk();
rk3036_hdmi_reset(hdmi_drv);
if (hdmi_drv->hdcp_power_on_cb)
rc = hdmi_drv->hdcp_power_on_cb();
return rc;
}

View File

@@ -0,0 +1,323 @@
#ifndef _RK3036_HDMI_HW_H
#define _RK3036_HDMI_HW_H
#include <linux/rockchip/iomap.h>
enum PWR_MODE {
NORMAL,
LOWER_PWR,
};
enum {
OUTPUT_DVI = 0,
OUTPUT_HDMI
};
#ifdef RK616_USE_MCLK_12M
#define HDMI_SYS_FREG_CLK 12000000
#else
#define HDMI_SYS_FREG_CLK 11289600
#endif
#define HDMI_SCL_RATE (100*1000)
#define DDC_BUS_FREQ_L 0x4b
#define DDC_BUS_FREQ_H 0x4c
#define SYS_CTRL 0x00
#define m_RST_ANALOG (1 << 6)
#define v_RST_ANALOG (0 << 6)
#define v_NOT_RST_ANALOG (1 << 6)
#define m_RST_DIGITAL (1 << 5)
#define v_RST_DIGITAL (0 << 5)
#define v_NOT_RST_DIGITAL (1 << 5)
#define m_REG_CLK_INV (1 << 4)
#define v_REG_CLK_NOT_INV (0 << 4)
#define v_REG_CLK_INV (1 << 4)
#define m_VCLK_INV (1 << 3)
#define v_VCLK_NOT_INV (0 << 3)
#define v_VCLK_INV (1 << 3)
#define m_REG_CLK_SOURCE (1 << 2)
#define v_REG_CLK_SOURCE_TMDS (0 << 2)
#define v_REG_CLK_SOURCE_SYS (1 << 2)
#define m_POWER (1 << 1)
#define v_PWR_ON (0 << 1)
#define v_PWR_OFF (1 << 1)
#define m_INT_POL (1 << 0)
#define v_INT_POL_HIGH 1
#define v_INT_POL_LOW 0
#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_ACTIVE_VSYNC (1 << 5)
#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_MASK_INT_HOTPLUG (1 << 5)
#define m_INT_HOTPLUG (1 << 1)
#define HDMI_COLORBAR 0xc9
#define PHY_SYNC 0xce /* sync phy parameter */
#define PHY_SYS_CTL 0xe0
#define m_TMDS_CLK_SOURCE (1 << 5)
#define v_TMDS_FROM_PLL (0 << 5)
#define v_TMDS_FROM_GEN (1 << 5)
#define m_PHASE_CLK (1 << 4)
#define v_DEFAULT_PHASE (0 << 4)
#define v_SYNC_PHASE (1 << 4)
#define m_TMDS_CURRENT_PWR (1 << 3)
#define v_TURN_ON_CURRENT (0 << 3)
#define v_CAT_OFF_CURRENT (1 << 3)
#define m_BANDGAP_PWR (1 << 2)
#define v_BANDGAP_PWR_UP (0 << 2)
#define v_BANDGAP_PWR_DOWN (1 << 2)
#define m_PLL_PWR (1 << 1)
#define v_PLL_PWR_UP (0 << 1)
#define v_PLL_PWR_DOWN (1 << 1)
#define m_TMDS_CHG_PWR (1 << 0)
#define v_TMDS_CHG_PWR_UP (0 << 0)
#define v_TMDS_CHG_PWR_DOWN (1 << 0)
#define PHY_CHG_PWR 0xe1
#define v_CLK_CHG_PWR(n) ((n & 1) << 3)
#define v_DATA_CHG_PWR(n) ((n & 7) << 0)
#define PHY_DRIVER 0xe2
#define v_CLK_MAIN_DRIVER(n) (n << 4)
#define v_DATA_MAIN_DRIVER(n) (n << 0)
#define PHY_PRE_EMPHASIS 0xe3
#define v_PRE_EMPHASIS(n) ((n & 7) << 4)
#define v_CLK_PRE_DRIVER(n) ((n & 3) << 2)
#define v_DATA_PRE_DRIVER(n) ((n & 3) << 0)
#define PHY_FEEDBACK_DIV_RATIO_LOW 0xe7
#define v_FEEDBACK_DIV_LOW(n) (n & 0xff)
#define PHY_FEEDBACK_DIV_RATIO_HIGH 0xe8
#define v_FEEDBACK_DIV_HIGH(n) (n & 1)
#define PHY_PRE_DIV_RATIO 0xed
#define v_PRE_DIV_RATIO(n) (n & 0x1f)
static inline int hdmi_readl(struct rk_hdmi_device *hdmi_dev, u16 offset,
u32 *val)
{
int ret = 0;
*val = readl_relaxed(hdmi_dev->regbase + (offset) * 0x04);
return ret;
}
static inline int hdmi_writel(struct rk_hdmi_device *hdmi_dev, u16 offset,
u32 val)
{
int ret = 0;
writel_relaxed(val, hdmi_dev->regbase + (offset) * 0x04);
return ret;
}
static inline int hdmi_msk_reg(struct rk_hdmi_device *hdmi_dev, u16 offset,
u32 msk, u32 val)
{
int ret = 0;
u32 temp;
temp = readl_relaxed(hdmi_dev->regbase + (offset) * 0x04) & (0xFF - (msk));
writel_relaxed(temp | ((val) & (msk)), hdmi_dev->regbase + (offset) * 0x04);
return ret;
}
static inline void rk3036_hdmi_reset_pclk(void)
{
writel_relaxed(0x00010001, RK_CRU_VIRT+ 0x128);
msleep(100);
writel_relaxed(0x00010000, RK_CRU_VIRT + 0x128);
}
extern int rk3036_hdmi_initial(struct hdmi *hdmi);
extern void rk3036_hdmi_irq(struct hdmi *hdmi);
#endif

View File

@@ -124,7 +124,7 @@ int hdmi_set_info(struct rk_screen *screen, unsigned int vic)
screen->hdmi_resolution = hdmi_mode[i].flag;
/* Pin polarity */
#if defined(CONFIG_HDMI_RK616) && !defined(CONFIG_ARCH_RK3026) && !defined(SOC_CONFIG_RK3036)
#if defined(CONFIG_HDMI_RK616) && !defined(CONFIG_ARCH_RK3026)
screen->pin_hsync = 0;
screen->pin_vsync = 0;
#else