rk3036 hdmi: add support HDMI CEC function

This commit is contained in:
hjc
2014-08-01 17:04:55 +08:00
parent a73af9e69b
commit 266161a208
11 changed files with 702 additions and 1 deletions

View File

@@ -11,3 +11,9 @@ config HDCP_RK3036_DEBUG
default n
help
Enableds verbose debugging the the HDCP drivers
config CEC_RK3036
bool "RK3036 CEC support"
default n
help
CEC Interface. This adds the HDMI CEC Interface.

View File

@@ -7,3 +7,4 @@ 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
obj-$(CONFIG_CEC_RK3036) += rk3036_hdmi_cec.o

View File

@@ -132,6 +132,22 @@ int rk3036_hdmi_register_hdcp_callbacks(void (*hdcp_cb)(void),
return HDMI_ERROR_SUCESS;
}
int rk3036_hdmi_register_cec_callbacks(void (*cec_irq)(void),
void (*cec_set_device_pa)(int addr),
int (*cec_enumerate)(void))
{
struct hdmi *hdmi_drv = &hdmi_dev->driver;
if (hdmi_drv == NULL)
return HDMI_ERROR_FALSE;
hdmi_drv->cec_irq = cec_irq;
hdmi_drv->cec_set_device_pa = cec_set_device_pa;
hdmi_drv->cec_enumerate = cec_enumerate;
return HDMI_ERROR_SUCESS;
}
static void rk3036_hdmi_early_suspend(void)
{
struct hdmi *hdmi_drv = &hdmi_dev->driver;

View File

@@ -19,6 +19,11 @@ 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));
extern int rk3036_hdmi_register_cec_callbacks(void (*cec_irq)(void),
void (*cec_set_device_pa)(int addr),
int (*cec_enumerate)(void));
extern struct rk_hdmi_device *hdmi_dev;
struct rk_hdmi_device {

View File

@@ -0,0 +1,459 @@
#include "rk3036_hdmi.h"
#include "rk3036_hdmi_hw.h"
#include "rk3036_hdmi_cec.h"
static cec_t cec;
static char la_player[3] = {CEC_LOGADDR_PLAYBACK1,
CEC_LOGADDR_PLAYBACK2,
CEC_LOGADDR_PLAYBACK3};
static int cec_read_frame(cec_framedata_t *frame)
{
int i, length,val;
char *data = (char *)frame;//modify by hjc
if(frame == NULL)
return -1;
hdmi_readl(hdmi_dev, CEC_RX_LENGTH, &length);
hdmi_writel(hdmi_dev, CEC_RX_OFFSET, 0);
printk("CEC: %s length is %d\n", __FUNCTION__, length);
for(i = 0; i < length; i++) {
hdmi_readl(hdmi_dev, CEC_DATA, &val);
data[i] = val;
printk("%02x\n", data[i]);
}
return 0;
}
static int cec_send_frame(cec_framedata_t *frame)
{
int i;
CECDBG("CEC: TX srcdestaddr %02x opcode %02x ",
frame->srcdestaddr, frame->opcode);
if(frame->argcount) {
CECDBG("args:");
for(i = 0; i < frame->argcount; i++) {
CECDBG("%02x ", frame->args[i]);
}
}
CECDBG("\n");
hdmi_writel(hdmi_dev, CEC_TX_OFFSET, 0);
hdmi_writel(hdmi_dev, CEC_DATA, frame->srcdestaddr);
hdmi_writel(hdmi_dev, CEC_DATA, frame->opcode);
for(i = 0; i < frame->argcount; i++)
hdmi_writel(hdmi_dev, CEC_DATA, frame->args[i]);
hdmi_writel(hdmi_dev, CEC_TX_LENGTH, frame->argcount + 2);
/*Wait for bus free*/
cec.busfree = 1;
hdmi_writel(hdmi_dev, CEC_CTRL, m_BUSFREETIME_ENABLE);
CECDBG("start wait bus free\n");
if(wait_event_interruptible_timeout(cec.wait, cec.busfree == 0, msecs_to_jiffies(17))) {
return -1;
}
CECDBG("end wait bus free,start tx,busfree=%d\n",cec.busfree);
/*Start TX*/
cec.tx_done = 0;
hdmi_writel(hdmi_dev, CEC_CTRL, m_BUSFREETIME_ENABLE|m_START_TX);
if(wait_event_interruptible_timeout(cec.wait, cec.tx_done != 0, msecs_to_jiffies(100)))
hdmi_writel(hdmi_dev, CEC_CTRL, 0);
CECDBG("end tx,tx_done=%d\n",cec.tx_done);
if (cec.tx_done == 1) {
cec.tx_done = 0;//hjc add ,need?
return 0;
} else
return -1;
}
static int cec_send_message(char opcode, char dest)
{
cec_framedata_t cecframe;
CECDBG("CEC: cec_send_message\n");
cecframe.opcode = opcode;
cecframe.srcdestaddr = MAKE_SRCDEST(cec.address_logic, dest);
cecframe.argcount = 0;
return cec_send_frame(&cecframe);
}
static void cec_send_feature_abort ( cec_framedata_t *pcpi, char reason )
{
cec_framedata_t cecFrame;
CECDBG("CEC: cec_send_feature_abort\n");
if (( pcpi->srcdestaddr & 0x0F) != CEC_LOGADDR_UNREGORBC )
{
cecFrame.opcode = CECOP_FEATURE_ABORT;
cecFrame.srcdestaddr = MAKE_SRCDEST( cec.address_logic, (pcpi->srcdestaddr & 0xF0) >> 4 );
cecFrame.args[0] = pcpi->opcode;
cecFrame.args[1] = reason;
cecFrame.argcount = 2;
cec_send_frame( &cecFrame );
}
}
static void cec_send_active_source(void)
{
cec_framedata_t cecframe;
CECDBG("CEC: start_active_source\n");
cecframe.opcode = CECOP_ACTIVE_SOURCE;
cecframe.srcdestaddr = MAKE_SRCDEST( cec.address_logic, CEC_LOGADDR_UNREGORBC);
cecframe.args[0] = (cec.address_phy & 0xFF00) >> 8; /* [Physical Address]*/
cecframe.args[1] = (cec.address_phy & 0x00FF); /* [Physical Address]*/
cecframe.argcount = 2;
cec_send_frame( &cecframe );
}
static void start_active_source(void)
{
int i;
CECDBG("CEC: start_active_source\n");
/* GPIO simulate CEC timing may be not correct, so we try more times.*/
/*send image view on first*/
for(i = 0; i < 1; i++) {
if(cec_send_message(CECOP_IMAGE_VIEW_ON,CEC_LOGADDR_TV) == 0) {
cec_send_active_source();
}
}
}
static void cec_handle_inactive_source ( cec_framedata_t *pcpi )
{
}
static void cec_handle_feature_abort( cec_framedata_t *pcpi )
{
}
static bool validate_cec_message(cec_framedata_t *pcpi)
{
char parametercount = 0;
bool countok = true;
CECDBG("CEC: validate_cec_message,opcode=%d\n",pcpi->opcode);
/* Determine required parameter count */
switch (pcpi->opcode)
{
case CECOP_IMAGE_VIEW_ON:
case CECOP_TEXT_VIEW_ON:
case CECOP_STANDBY:
case CECOP_GIVE_PHYSICAL_ADDRESS:
case CECOP_GIVE_DEVICE_POWER_STATUS:
case CECOP_GET_MENU_LANGUAGE:
case CECOP_GET_CEC_VERSION:
parametercount = 0;
break;
case CECOP_REPORT_POWER_STATUS: // power status*/
case CECOP_CEC_VERSION: // cec version*/
parametercount = 1;
break;
case CECOP_INACTIVE_SOURCE: // physical address*/
case CECOP_FEATURE_ABORT: // feature opcode / abort reason*/
case CECOP_ACTIVE_SOURCE: // physical address*/
parametercount = 2;
break;
case CECOP_REPORT_PHYSICAL_ADDRESS: // physical address / device type*/
case CECOP_DEVICE_VENDOR_ID: // vendor id*/
parametercount = 3;
break;
case CECOP_SET_OSD_NAME: // osd name (1-14 bytes)*/
case CECOP_SET_OSD_STRING: // 1 + x display control / osd string (1-13 bytes)*/
parametercount = 1; // must have a minimum of 1 operands*/
break;
case CECOP_ABORT:
break;
case CECOP_ARC_INITIATE:
break;
case CECOP_ARC_REPORT_INITIATED:
break;
case CECOP_ARC_REPORT_TERMINATED:
break;
case CECOP_ARC_REQUEST_INITIATION:
break;
case CECOP_ARC_REQUEST_TERMINATION:
break;
case CECOP_ARC_TERMINATE:
break;
default:
break;
}
/* Test for correct parameter count. */
if (pcpi->argcount < parametercount) {
CECDBG("CEC: pcpi->argcount[%d] < parametercount[%d]\n",
pcpi->argcount,parametercount);
countok = false;
}
return(countok );
}
static bool cec_rx_msg_handler_last(cec_framedata_t *pcpi)
{
bool isdirectaddressed;
cec_framedata_t cecFrame;
CECDBG("CEC: cec_rx_msg_handler_last,opcode=%d\n",pcpi->opcode);
isdirectaddressed = !((pcpi->srcdestaddr & 0x0F) == CEC_LOGADDR_UNREGORBC);
if (validate_cec_message(pcpi)) {/* If invalid message, ignore it, but treat it as handled*/
if (isdirectaddressed) {
switch (pcpi->opcode) {
case CECOP_FEATURE_ABORT:
cec_handle_feature_abort(pcpi);
break;
case CECOP_IMAGE_VIEW_ON: /*In our case, respond the same to both these messages*/
case CECOP_TEXT_VIEW_ON:
break;
case CECOP_STANDBY: /* Direct and Broadcast*/
/* Setting this here will let the main task know */
/* (via SI_CecGetPowerState) and at the same time */
/* prevent us from broadcasting a STANDBY message */
/* of our own when the main task responds by */
/* calling SI_CecSetPowerState( STANDBY ); */
cec.powerstatus = CEC_POWERSTATUS_STANDBY;
break;
case CECOP_INACTIVE_SOURCE:
cec_handle_inactive_source(pcpi);
break;
case CECOP_GIVE_PHYSICAL_ADDRESS:
/* TV responds by broadcasting its Physical Address: 0.0.0.0 */
cecFrame.opcode = CECOP_REPORT_PHYSICAL_ADDRESS;
cecFrame.srcdestaddr = MAKE_SRCDEST(cec.address_logic,
CEC_LOGADDR_UNREGORBC);
cecFrame.args[0] = (cec.address_phy&0xFF00)>>8; /* [Physical Address]*/
cecFrame.args[1] = (cec.address_phy&0x00FF); /*[Physical Address]*/
cecFrame.args[2] = cec.address_logic;/*CEC_LOGADDR_PLAYBACK1;//2011.08.03 CEC_LOGADDR_TV; // [Device Type] = 0 = TV*/
cecFrame.argcount = 3;
cec_send_frame(&cecFrame);
break;
case CECOP_GIVE_DEVICE_POWER_STATUS:
/* TV responds with power status. */
cecFrame.opcode = CECOP_REPORT_POWER_STATUS;
cecFrame.srcdestaddr = MAKE_SRCDEST(cec.address_logic, (pcpi->srcdestaddr & 0xF0) >> 4);
cecFrame.args[0] = cec.powerstatus;
cecFrame.argcount = 1;
cec_send_frame(&cecFrame);
break;
case CECOP_GET_MENU_LANGUAGE:
/* TV Responds with a Set Menu language command. */
cecFrame.opcode = CECOP_SET_MENU_LANGUAGE;
cecFrame.srcdestaddr = CEC_LOGADDR_UNREGORBC;
cecFrame.args[0] = 'e'; /* [language code see iso/fdis 639-2]*/
cecFrame.args[1] = 'n'; /* [language code see iso/fdis 639-2]*/
cecFrame.args[2] = 'g'; /* [language code see iso/fdis 639-2]*/
cecFrame.argcount = 3;
cec_send_frame(&cecFrame);
break;
case CECOP_GET_CEC_VERSION:
/* TV responds to this request with it's CEC version support. */
cecFrame.srcdestaddr = MAKE_SRCDEST(cec.address_logic, (pcpi->srcdestaddr & 0xF0) >> 4);
cecFrame.opcode = CECOP_CEC_VERSION;
cecFrame.args[0] = 0x04; /* Report CEC1.3a*/
cecFrame.argcount = 1;
cec_send_frame(&cecFrame);
break;
case CECOP_REPORT_POWER_STATUS: /* Someone sent us their power state.*/
/*l_sourcePowerStatus = pcpi->args[0];
Let NEW SOURCE task know about it.
if (l_cecTaskState.task == SI_CECTASK_NEWSOURCE) {
l_cecTaskState.cpiState = CPI_RESPONSE;
}*/
break;
/* Do not reply to directly addressed 'Broadcast' msgs. */
case CECOP_ACTIVE_SOURCE:
case CECOP_REPORT_PHYSICAL_ADDRESS: /*A physical address was broadcast -- ignore it.*/
case CECOP_REQUEST_ACTIVE_SOURCE: /*We are not a source, so ignore this one.*/
case CECOP_ROUTING_CHANGE: /*We are not a downstream switch, so ignore this one.*/
case CECOP_ROUTING_INFORMATION: /*We are not a downstream switch, so ignore this one.*/
case CECOP_SET_STREAM_PATH: /*We are not a source, so ignore this one.*/
case CECOP_SET_MENU_LANGUAGE: /*As a TV, we can ignore this message*/
case CECOP_DEVICE_VENDOR_ID:
break;
case CECOP_ABORT: /*Send Feature Abort for all unsupported features.*/
default:
cec_send_feature_abort(pcpi, CECAR_UNRECOG_OPCODE);
break;
}
} else {
/* Respond to broadcast messages. */
switch (pcpi->opcode) {
case CECOP_STANDBY:
/* Setting this here will let the main task know */
/* (via SI_CecGetPowerState) and at the same time */
/* prevent us from broadcasting a STANDBY message */
/* of our own when the main task responds by */
/* calling SI_CecSetPowerState( STANDBY ); */
cec.powerstatus = CEC_POWERSTATUS_STANDBY;
break;
case CECOP_ACTIVE_SOURCE:
/*CecHandleActiveSource( pcpi );*/
break;
case CECOP_REPORT_PHYSICAL_ADDRESS:
/*CecHandleReportPhysicalAddress( pcpi );*/
break;
/* Do not reply to 'Broadcast' msgs that we don't need. */
case CECOP_REQUEST_ACTIVE_SOURCE: /*We are not a source, so ignore this one.*/
/*SI_StartActiveSource(0,0);//2011.08.03*/
break;
case CECOP_ROUTING_CHANGE: /*We are not a downstream switch, so ignore this one.*/
case CECOP_ROUTING_INFORMATION: /*We are not a downstream switch, so ignore this one.*/
case CECOP_SET_STREAM_PATH: /*We are not a source, so ignore this one.*/
case CECOP_SET_MENU_LANGUAGE: /*As a TV, we can ignore this message*/
break;
}
}
}
return 0;
}
static void cec_work_func(struct work_struct *work)
{
struct cec_delayed_work *cec_w =
container_of(work, struct cec_delayed_work, work.work);
cec_framedata_t cecFrame;
CECDBG(KERN_WARNING "CEC: cec_work_func,event=%d\n",cec_w->event);
switch(cec_w->event)
{
case EVENT_ENUMERATE:
break;
case EVENT_RX_FRAME:
memset(&cecFrame, 0, sizeof(cec_framedata_t));
cec_read_frame(&cecFrame);
cec_rx_msg_handler_last(&cecFrame);
break;
default:
break;
}
if(cec_w->data)
kfree(cec_w->data);
kfree(cec_w);
}
static void cec_submit_work(int event, int delay, void *data)
{
struct cec_delayed_work *work;
CECDBG("%s event %04x delay %d", __func__, event, delay);
work = kmalloc(sizeof(struct cec_delayed_work), GFP_ATOMIC);
if (work) {
INIT_DELAYED_WORK(&work->work, cec_work_func);
work->event = event;
work->data = data;
queue_delayed_work(cec.workqueue,
&work->work,
msecs_to_jiffies(delay));
} else {
CECDBG(KERN_WARNING "CEC: GPIO CEC: Cannot allocate memory to "
"create work\n");;
}
}
int cec_enumerate(void)
{
/*for(i = 0; i < 3; i++) {
if(Cec_Ping(la_player[i]) == 1) {
cec.address_logic = la_player[i];
break;
}
}
if(i == 3)
return -1;
//Broadcast our physical address.
GPIO_CecSendMessage(CECOP_GET_MENU_LANGUAGE,CEC_LOGADDR_TV);
msleep(100);*/
CECDBG("CEC: %s\n", __func__);
cec.address_logic = la_player[0];
hdmi_writel(hdmi_dev, CEC_LOGICADDR, cec.address_logic);
start_active_source();
return 0;
}
void cec_set_device_pa(int addr)
{
CECDBG("CEC: Physical Address is %02x", addr);
cec.address_phy = addr;
}
void rk3036_cec_isr(void)
{
int tx_isr = 0, rx_isr = 0;
hdmi_readl(hdmi_dev, CEC_TX_INT, &tx_isr);
hdmi_readl(hdmi_dev, CEC_RX_INT, &rx_isr);
CECDBG("CEC: rk3036_cec_isr:tx_isr %02x rx_isr %02x\n\n", tx_isr, rx_isr);
hdmi_writel(hdmi_dev, CEC_TX_INT, tx_isr);
hdmi_writel(hdmi_dev, CEC_RX_INT, rx_isr);
if (tx_isr & m_TX_BUSNOTFREE) {
cec.busfree = 0;
CECDBG("CEC: m_TX_BUSNOTFREE,busfree=%d\n",cec.busfree);
} else if (tx_isr & m_TX_DONE) {
cec.tx_done = 1;
CECDBG("CEC: m_TX_DONE,busfree=%d\n",cec.tx_done);
} else {
cec.tx_done = -1;
CECDBG("CEC: else:busfree=%d\n",cec.tx_done);
}
wake_up_interruptible_all(&cec.wait);
if(rx_isr & m_RX_DONE)
cec_submit_work(EVENT_RX_FRAME, 0, NULL);
}
static int __init rk3036_cec_init(void)
{
CECDBG(KERN_ERR "CEC: rk3036_cec_init start.\n");
memset(&cec, 0, sizeof(cec_t));
cec.workqueue = create_singlethread_workqueue("cec");
if (cec.workqueue == NULL) {
CECDBG(KERN_ERR "CEC: GPIO CEC: create workqueue failed.\n");
return -1;
}
init_waitqueue_head(&cec.wait);
/*Fref = Fsys / ((register 0xd4 + 1)*(register 0xd5 + 1))*/
/*Fref = 0.5M, Fsys = 74.25M*/
hdmi_writel(hdmi_dev, CEC_CLK_H, 11);
hdmi_writel(hdmi_dev, CEC_CLK_L, 11);
/*Set bus free time to 16.8ms*/
hdmi_writel(hdmi_dev, CEC_BUSFREETIME_L, 0xd0);
hdmi_writel(hdmi_dev, CEC_BUSFREETIME_H, 0x20);
/*Enable TX/RX INT*/
hdmi_writel(hdmi_dev, CEC_TX_INT, 0xFF);
hdmi_writel(hdmi_dev, CEC_RX_INT, 0xFF);
rk3036_hdmi_register_cec_callbacks(rk3036_cec_isr,
cec_set_device_pa,
cec_enumerate);
CECDBG(KERN_ERR "CEC: rk3036_cec_init sucess\n");
return 0;
}
static void __exit rk3036_cec_exit(void)
{
kfree(&cec);
}
late_initcall_sync(rk3036_cec_init);
module_exit(rk3036_cec_exit);

View File

@@ -0,0 +1,161 @@
#ifndef __RK3036_CEC_H__
#define __RK3036_CEC_H__
typedef enum
{
CEC_LOGADDR_TV = 0x00,
CEC_LOGADDR_RECDEV1 = 0x01,
CEC_LOGADDR_RECDEV2 = 0x02,
CEC_LOGADDR_TUNER1 = 0x03, /* STB1 in Spev v1.3*/
CEC_LOGADDR_PLAYBACK1 = 0x04, /* DVD1 in Spev v1.3*/
CEC_LOGADDR_AUDSYS = 0x05,
CEC_LOGADDR_TUNER2 = 0x06, /* STB2 in Spec v1.3*/
CEC_LOGADDR_TUNER3 = 0x07, /* STB3 in Spec v1.3*/
CEC_LOGADDR_PLAYBACK2 = 0x08, /* DVD2 in Spec v1.3*/
CEC_LOGADDR_RECDEV3 = 0x09,
CEC_LOGADDR_TUNER4 = 0x0A, /* RES1 in Spec v1.3*/
CEC_LOGADDR_PLAYBACK3 = 0x0B, /* RES2 in Spec v1.3*/
CEC_LOGADDR_RES3 = 0x0C,
CEC_LOGADDR_RES4 = 0x0D,
CEC_LOGADDR_FREEUSE = 0x0E,
CEC_LOGADDR_UNREGORBC = 0x0F
} cec_log_addr_t;
typedef enum /* CEC Messages*/
{
CECOP_FEATURE_ABORT = 0x00,
CECOP_IMAGE_VIEW_ON = 0x04,
CECOP_TUNER_STEP_INCREMENT = 0x05, /* N/A*/
CECOP_TUNER_STEP_DECREMENT = 0x06, /* N/A*/
CECOP_TUNER_DEVICE_STATUS = 0x07, /* N/A*/
CECOP_GIVE_TUNER_DEVICE_STATUS = 0x08, /* N/A*/
CECOP_RECORD_ON = 0x09, /* N/A*/
CECOP_RECORD_STATUS = 0x0A, /* N/A*/
CECOP_RECORD_OFF = 0x0B, /* N/A*/
CECOP_TEXT_VIEW_ON = 0x0D,
CECOP_RECORD_TV_SCREEN = 0x0F, /* N/A*/
CECOP_GIVE_DECK_STATUS = 0x1A,
CECOP_DECK_STATUS = 0x1B,
CECOP_SET_MENU_LANGUAGE = 0x32,
CECOP_CLEAR_ANALOGUE_TIMER = 0x33, /* Spec 1.3A*/
CECOP_SET_ANALOGUE_TIMER = 0x34, /* Spec 1.3A*/
CECOP_TIMER_STATUS = 0x35, /* Spec 1.3A*/
CECOP_STANDBY = 0x36,
CECOP_PLAY = 0x41,
CECOP_DECK_CONTROL = 0x42,
CECOP_TIMER_CLEARED_STATUS = 0x43, /* Spec 1.3A*/
CECOP_USER_CONTROL_PRESSED = 0x44,
CECOP_USER_CONTROL_RELEASED = 0x45,
CECOP_GIVE_OSD_NAME = 0x46,
CECOP_SET_OSD_NAME = 0x47,
CECOP_SET_OSD_STRING = 0x64,
CECOP_SET_TIMER_PROGRAM_TITLE = 0x67, /* Spec 1.3A*/
CECOP_SYSTEM_AUDIO_MODE_REQUEST = 0x70, /* Spec 1.3A*/
CECOP_GIVE_AUDIO_STATUS = 0x71, /* Spec 1.3A*/
CECOP_SET_SYSTEM_AUDIO_MODE = 0x72, /* Spec 1.3A*/
CECOP_REPORT_AUDIO_STATUS = 0x7A, /* Spec 1.3A*/
CECOP_GIVE_SYSTEM_AUDIO_MODE_STATUS = 0x7D, /* Spec 1.3A*/
CECOP_SYSTEM_AUDIO_MODE_STATUS = 0x7E, /* Spec 1.3A*/
CECOP_ROUTING_CHANGE = 0x80,
CECOP_ROUTING_INFORMATION = 0x81,
CECOP_ACTIVE_SOURCE = 0x82,
CECOP_GIVE_PHYSICAL_ADDRESS = 0x83,
CECOP_REPORT_PHYSICAL_ADDRESS = 0x84,
CECOP_REQUEST_ACTIVE_SOURCE = 0x85,
CECOP_SET_STREAM_PATH = 0x86,
CECOP_DEVICE_VENDOR_ID = 0x87,
CECOP_VENDOR_COMMAND = 0x89,
CECOP_VENDOR_REMOTE_BUTTON_DOWN = 0x8A,
CECOP_VENDOR_REMOTE_BUTTON_UP = 0x8B,
CECOP_GIVE_DEVICE_VENDOR_ID = 0x8C,
CECOP_MENU_REQUEST = 0x8D,
CECOP_MENU_STATUS = 0x8E,
CECOP_GIVE_DEVICE_POWER_STATUS = 0x8F,
CECOP_REPORT_POWER_STATUS = 0x90,
CECOP_GET_MENU_LANGUAGE = 0x91,
CECOP_SELECT_ANALOGUE_SERVICE = 0x92, /* Spec 1.3A N/A*/
CECOP_SELECT_DIGITAL_SERVICE = 0x93, /* N/A*/
CECOP_SET_DIGITAL_TIMER = 0x97, /* Spec 1.3A*/
CECOP_CLEAR_DIGITAL_TIMER = 0x99, /* Spec 1.3A*/
CECOP_SET_AUDIO_RATE = 0x9A, /* Spec 1.3A*/
CECOP_INACTIVE_SOURCE = 0x9D, /* Spec 1.3A*/
CECOP_CEC_VERSION = 0x9E, /* Spec 1.3A*/
CECOP_GET_CEC_VERSION = 0x9F, /* Spec 1.3A*/
CECOP_VENDOR_COMMAND_WITH_ID = 0xA0, /* Spec 1.3A*/
CECOP_CLEAR_EXTERNAL_TIMER = 0xA1, /* Spec 1.3A*/
CECOP_SET_EXTERNAL_TIMER = 0xA2, /* Spec 1.3A*/
CDCOP_HEADER = 0xF8,
CECOP_ABORT = 0xFF,
CECOP_REPORT_SHORT_AUDIO = 0xA3, /* Spec 1.4*/
CECOP_REQUEST_SHORT_AUDIO = 0xA4, /* Spec 1.4*/
CECOP_ARC_INITIATE = 0xC0,
CECOP_ARC_REPORT_INITIATED = 0xC1,
CECOP_ARC_REPORT_TERMINATED = 0xC2,
CECOP_ARC_REQUEST_INITIATION = 0xC3,
CECOP_ARC_REQUEST_TERMINATION = 0xC4,
CECOP_ARC_TERMINATE = 0xC5,
} cec_opcode_t;
typedef enum /* Operands for <Feature Abort> Opcode*/
{
CECAR_UNRECOG_OPCODE = 0x00,
CECAR_NOT_CORRECT_MODE,
CECAR_CANT_PROVIDE_SOURCE,
CECAR_INVALID_OPERAND,
CECAR_REFUSED
} cec_abor_reason_t;
enum /* Operands for <Power Status> Opcode*/
{
CEC_POWERSTATUS_ON = 0x00,
CEC_POWERSTATUS_STANDBY = 0x01,
CEC_POWERSTATUS_STANDBY_TO_ON = 0x02,
CEC_POWERSTATUS_ON_TO_STANDBY = 0x03,
};
enum {
EVENT_RX_FRAME,
EVENT_ENUMERATE,
};
#define MAKE_SRCDEST( src, dest) (( src << 4) | dest )
#define MAX_CMD_SIZE 16
typedef struct
{
char srcdestaddr; /*Source in upper nybble, dest in lower nybble*/
char opcode;
char args[ MAX_CMD_SIZE ];
char argcount;
char nextframeargcount;
} cec_framedata_t;
struct cec_delayed_work {
struct delayed_work work;
int event;
void *data;
};
typedef struct
{
struct workqueue_struct *workqueue;
wait_queue_head_t wait;
int busfree;
int tx_done;
int address_phy;
int address_logic;
int powerstatus;
} cec_t;
//#define DEBUG
#ifdef DEBUG
#define CECDBG(format, ...) \
printk(KERN_INFO format, ## __VA_ARGS__)
#else
#define CECDBG(format, ...)
#endif
#endif

View File

@@ -532,6 +532,8 @@ void rk3036_hdmi_irq(struct hdmi *hdmi_drv)
if (hdmi_drv->hdcp_irq_cb)
hdmi_drv->hdcp_irq_cb(0);
if (hdmi_drv->cec_irq)
hdmi_drv->cec_irq();
}
static void rk3036_hdmi_reset(struct hdmi *hdmi_drv)

View File

@@ -2,6 +2,8 @@
#define _RK3036_HDMI_HW_H
#include <linux/rockchip/iomap.h>
#include <linux/delay.h>
enum PWR_MODE {
NORMAL,
LOWER_PWR,
@@ -282,6 +284,41 @@ enum {
#define PHY_PRE_DIV_RATIO 0xed
#define v_PRE_DIV_RATIO(n) (n & 0x1f)
/*-----START----- HDMI CEC CTRL------START------*/
#define CEC_CTRL 0xd0
#define m_ADJUST_FOR_HISENSE (1 << 6)
#define m_REJECT_RX_BROADCAST (1 << 5)
#define m_BUSFREETIME_ENABLE (1 << 2)
#define m_REJECT_RX (1 << 1)
#define m_START_TX (1 << 0)
#define CEC_DATA 0xd1
#define CEC_TX_OFFSET 0xd2
#define CEC_RX_OFFSET 0xd3
#define CEC_CLK_H 0xd4
#define CEC_CLK_L 0xd5
#define CEC_TX_LENGTH 0xd6
#define CEC_RX_LENGTH 0xd7
#define CEC_TX_INT_MASK 0xd8
#define m_TX_DONE (1 << 3)
#define m_TX_NOACK (1 << 2)
#define m_TX_BROADCAST_REJ (1 << 1)
#define m_TX_BUSNOTFREE (1 << 0)
#define CEC_RX_INT_MASK 0xd9
#define m_RX_LA_ERR (1 << 4)
#define m_RX_GLITCH (1 << 3)
#define m_RX_DONE (1 << 0)
#define CEC_TX_INT 0xda
#define CEC_RX_INT 0xdb
#define CEC_BUSFREETIME_L 0xdc
#define CEC_BUSFREETIME_H 0xdd
#define CEC_LOGICADDR 0xde
/*------END------ HDMI CEC CTRL------END-------*/
static inline int hdmi_readl(struct rk_hdmi_device *hdmi_dev, u16 offset,
u32 *val)
{

View File

@@ -284,6 +284,7 @@ struct hdmi_edid {
struct hdmi_audio *audio; /* Device supported audio info */
int audio_num; /* Device supported audio type number */
int base_audio_support; /* Device supported base audio */
unsigned int cecaddress; //CEC physical address
};
/* RK HDMI Video Configure Parameters */
@@ -361,11 +362,16 @@ struct hdmi {
int (*set_vif) (struct hdmi *hdmi, struct rk_screen *screen,
bool connect);
/* call back for hdcp operatoion */
/* call back for hdcp operation */
void (*hdcp_cb) (void);
void (*hdcp_irq_cb) (int);
int (*hdcp_power_on_cb) (void);
void (*hdcp_power_off_cb) (void);
/*call back for cec operation*/
void (*cec_irq) (void);
void (*cec_set_device_pa) (int);
int (*cec_enumerate) (void);
};
#define hdmi_err(dev, format, arg...) \

View File

@@ -223,6 +223,10 @@ static int hdmi_edid_parse_cea_sdb(unsigned char *buf, struct hdmi_edid *pedid)
if (IEEEOUI == 0x0c03)
pedid->sink_hdmi = 1;
pedid->cecaddress = buf[cur_offset + 5];
pedid->cecaddress |= buf[cur_offset + 4] << 8;
hdmi_edid_debug("[EDID-CEA] CEC Physical addres is 0x%08x.\n", pedid->cecaddress);
if (count > 5) {
pedid->deepcolor = (buf[6] >> 3) & 0x0F;
supports_ai = buf[6] >> 7;

View File

@@ -217,6 +217,10 @@ void hdmi_work(struct work_struct *work)
case READ_PARSE_EDID:
rc = hdmi_sys_parse_edid(hdmi);
if (rc == HDMI_ERROR_SUCESS) {
if(hdmi->cec_set_device_pa)
hdmi->cec_set_device_pa(hdmi->edid.cecaddress);
if(hdmi->cec_enumerate)
hdmi->cec_enumerate();
hdmi->state = SYSTEM_CONFIG;
kobject_uevent_env(&hdmi->dev->kobj, KOBJ_ADD,
envp);