blob: 7e468127e03188a794b175d79cfea01776d9d6c5 [file] [log] [blame]
/**************************************************
* HDMI CEC uboot code *
* *
**************************************************/
#ifdef CONFIG_CEC_WAKEUP
#include "secure_apb.h"
#include "cec_tx_reg.h"
#ifndef NULL
#define NULL ((void *)0)
#endif
#define CEC_DBG_PRINT
#ifdef CEC_DBG_PRINT
static void cec_dbg_print(char *s, int v)
{
uart_puts(s);
uart_put_hex(v,8);
_udelay(100);
}
static void cec_dbg_prints(char *s)
{
uart_puts(s);
_udelay(100);
}
#else
#define cec_dbg_print(s,v)
#define cec_dbg_prints(s)
#endif
static void cec_reset_addr(void);
struct cec_tx_msg_t {
unsigned char buf[16];
unsigned char retry;
unsigned char len;
};
#define CEX_TX_MSG_BUF_NUM 4
#define CEC_TX_MSG_BUF_MASK (CEX_TX_MSG_BUF_NUM - 1)
struct cec_tx_msg {
struct cec_tx_msg_t msg[CEX_TX_MSG_BUF_NUM];
unsigned char send_idx;
unsigned char queue_idx;
};
struct cec_tx_msg cec_tx_msgs = {};
static int cec_strlen(char *p)
{
int i=0;
while (*p++)
i++;
return i;
}
static void *cec_memcpy(void *memto, const void *memfrom, unsigned int size)
{
char *tempfrom = (char *)memfrom;
char *tempto = (char *)memto;
if ((memto == NULL) || (memfrom == NULL))
return NULL;
while (size -- > 0)
*tempto++ = *tempfrom++;
return memto;
}
static void waiting_aocec_free(void) {
do {
unsigned long cnt = 0;
while (readl(P_AO_CEC_RW_REG) & (1<<23))
{
if (5000 == cnt++)
{
break;
}
}
} while(0);
}
static unsigned long cec_rd_reg(unsigned long addr)
{
unsigned long data32;
waiting_aocec_free();
data32 = 0;
data32 |= 0 << 16; // [16] cec_reg_wr
data32 |= 0 << 8; // [15:8] cec_reg_wrdata
data32 |= addr << 0; // [7:0] cec_reg_addr
writel(data32, P_AO_CEC_RW_REG);
waiting_aocec_free();
data32 = ((readl(P_AO_CEC_RW_REG)) >> 24) & 0xff;
return (data32);
} /* cec_rd_reg */
static void cec_wr_reg (unsigned long addr, unsigned long data)
{
unsigned long data32;
waiting_aocec_free();
data32 = 0;
data32 |= 1 << 16; // [16] cec_reg_wr
data32 |= data << 8; // [15:8] cec_reg_wrdata
data32 |= addr << 0; // [7:0] cec_reg_addr
writel(data32, P_AO_CEC_RW_REG);
} /* aocec_wr_only_reg */
static void cec_rx_read_pos_plus(void)
{
(cec_msg.rx_read_pos == cec_msg.rx_buf_size - 1) ?
(cec_msg.rx_read_pos = 0) :
(cec_msg.rx_read_pos++);
}
static void cec_arbit_bit_time_set(unsigned bit_set, unsigned time_set)
{
//11bit:bit[10:0]
switch (bit_set) {
case 3:
//3 bit
cec_wr_reg(AO_CEC_TXTIME_4BIT_BIT7_0, time_set & 0xff);
cec_wr_reg(AO_CEC_TXTIME_4BIT_BIT10_8, (time_set >> 8) & 0x7);
break;
//5 bit
case 5:
cec_wr_reg(AO_CEC_TXTIME_2BIT_BIT7_0, time_set & 0xff);
cec_wr_reg(AO_CEC_TXTIME_2BIT_BIT10_8, (time_set >> 8) & 0x7);
//7 bit
case 7:
cec_wr_reg(AO_CEC_TXTIME_17MS_BIT7_0, time_set & 0xff);
cec_wr_reg(AO_CEC_TXTIME_17MS_BIT10_8, (time_set >> 8) & 0x7);
break;
default:
break;
}
}
static void cec_hw_buf_clear(void)
{
cec_wr_reg(CEC_RX_MSG_CMD, RX_DISABLE);
cec_wr_reg(CEC_TX_MSG_CMD, TX_ABORT);
cec_wr_reg(CEC_RX_CLEAR_BUF, 1);
cec_wr_reg(CEC_TX_CLEAR_BUF, 1);
_udelay(100);
cec_wr_reg(CEC_RX_CLEAR_BUF, 0);
cec_wr_reg(CEC_TX_CLEAR_BUF, 0);
_udelay(100);
cec_wr_reg(CEC_RX_MSG_CMD, RX_NO_OP);
cec_wr_reg(CEC_TX_MSG_CMD, TX_NO_OP);
}
void remote_cec_hw_reset(void)
{
unsigned int reg;
cec_dbg_prints("cec reset\n");
reg = readl(P_AO_CRT_CLK_CNTL1);
/* 24MHz/ (731 + 1) = 32786.885Hz */
reg &= ~(0x7ff << 16);
reg |= (731 << 16); /* divider from 24MHz */
reg |= (0x1 << 26);
reg &= ~(0x800 << 16); /* select divider */
writel(reg, P_AO_CRT_CLK_CNTL1);
/* set up pinmux */
writel(readl(P_AO_RTI_PIN_MUX_REG) & (~(1 << 18 | 1 << 17)), P_AO_RTI_PIN_MUX_REG);
writel(readl(P_AO_RTI_PULL_UP_REG) & (~(1 << 9)), P_AO_RTI_PULL_UP_REG);
writel(readl(P_AO_RTI_PIN_MUX_REG) | (1 << 16), P_AO_RTI_PIN_MUX_REG);
// Assert SW reset AO_CEC
writel(0x1, P_AO_CEC_GEN_CNTL);
// Enable gated clock (Normal mode).
writel(readl(P_AO_CEC_GEN_CNTL) | (1<<1), P_AO_CEC_GEN_CNTL);
_udelay(100);
// Release SW reset
writel(readl(P_AO_CEC_GEN_CNTL) & ~(1<<0), P_AO_CEC_GEN_CNTL);
writel(readl(P_AO_CEC_INTR_MASKN) | (0x03 << 1), P_AO_CEC_INTR_MASKN);
cec_arbit_bit_time_set(3, 0x118);
cec_arbit_bit_time_set(5, 0x000);
cec_arbit_bit_time_set(7, 0x2aa);
}
static unsigned char remote_cec_ll_rx(void)
{
int i;
int print = 1;
unsigned char rx_msg_length = cec_rd_reg(CEC_RX_MSG_LENGTH) + 1;
cec_dbg_prints("cec R:");
for (i = 0; i < rx_msg_length; i++) {
cec_msg.buf[cec_msg.rx_write_pos].msg[i] = cec_rd_reg(CEC_RX_MSG_0_HEADER + i);
if (print) {
cec_dbg_print(" ", cec_msg.buf[cec_msg.rx_write_pos].msg[i]);
}
if (i == 1 && cec_msg.buf[cec_msg.rx_write_pos].msg[i] == CEC_OC_VENDOR_COMMAND_WITH_ID) {
/* do not print command with ID */
print = 0;
}
}
cec_msg.buf[cec_msg.rx_write_pos].msg_len = rx_msg_length;
cec_dbg_prints("\n");
return 0;
}
static void cec_buf_clear(void)
{
int i;
for (i = 0; i < 16; i++)
cec_msg.buf[cec_msg.rx_read_pos].msg[i] = 0;
}
static void cec_tx_buf_init(void)
{
int i, j;
for (j = 0; j < CEX_TX_MSG_BUF_NUM; j++) {
for (i = 0; i < 16; i++) {
cec_tx_msgs.msg[j].buf[i] = 0;
}
cec_tx_msgs.msg[j].retry = 0;
cec_tx_msgs.msg[j].len = 0;
}
}
static int cec_queue_tx_msg(unsigned char *msg, unsigned char len)
{
int s_idx, q_idx;
s_idx = cec_tx_msgs.send_idx;
q_idx = cec_tx_msgs.queue_idx;
if (((q_idx + 1) & CEC_TX_MSG_BUF_MASK) == s_idx) {
cec_dbg_prints("tx buffer full, abort msg\n");
cec_reset_addr();
return -1;
}
if (len && msg) {
cec_memcpy(cec_tx_msgs.msg[q_idx].buf, msg, len);
cec_tx_msgs.msg[q_idx].len = len;
cec_tx_msgs.queue_idx = (q_idx + 1) & CEC_TX_MSG_BUF_MASK;
}
return 0;
}
static int cec_triggle_tx(unsigned char *msg, unsigned char len)
{
int i;
if ((TX_IDLE == cec_rd_reg(CEC_TX_MSG_STATUS)) ||
(TX_DONE == cec_rd_reg(CEC_TX_MSG_STATUS))) {
cec_dbg_prints("cec T:");
for (i = 0; i < len; i++) {
cec_wr_reg(CEC_TX_MSG_0_HEADER + i, msg[i]);
cec_dbg_print(" ", msg[i]);
}
cec_dbg_prints("\n");
cec_wr_reg(CEC_TX_MSG_LENGTH, len-1);
cec_wr_reg(CEC_TX_MSG_CMD, TX_REQ_CURRENT); //TX_REQ_NEXT
return 0;
}
return -1;
}
static int remote_cec_ll_tx(unsigned char *msg, unsigned char len)
{
cec_queue_tx_msg(msg, len);
cec_triggle_tx(msg, len);
return 0;
}
static int ping_cec_ll_tx(unsigned char *msg, unsigned char len)
{
int i;
int ret = 0;
unsigned int n = 900;
unsigned int reg;
ret = cec_rd_reg(CEC_RX_MSG_STATUS);
cec_dbg_print("rx stat:", ret);
ret = cec_rd_reg(CEC_TX_MSG_STATUS);
cec_dbg_print(", tx stat:", ret);
cec_dbg_prints("\n");
while (cec_rd_reg(CEC_TX_MSG_STATUS) == TX_BUSY) {
/*
* waiting tx to idle if it is busy, other device may in tx state
*/
}
if (cec_rd_reg(CEC_TX_MSG_STATUS) == TX_ERROR)
cec_wr_reg(CEC_TX_MSG_CMD, TX_NO_OP);
for (i = 0; i < len; i++) {
cec_wr_reg(CEC_TX_MSG_0_HEADER + i, msg[i]);
}
cec_wr_reg(CEC_TX_MSG_LENGTH, len-1);
cec_wr_reg(CEC_TX_MSG_CMD, TX_REQ_CURRENT); //TX_REQ_NEXT
ret = cec_rd_reg(CEC_RX_MSG_STATUS);
cec_dbg_print("rx stat:", ret);
ret = cec_rd_reg(CEC_TX_MSG_STATUS);
cec_dbg_print(", tx stat:", ret);
cec_dbg_prints("\n");
while (1) {
reg = cec_rd_reg(CEC_TX_MSG_STATUS);
if ( reg == TX_DONE ) {
ret = TX_DONE;
cec_wr_reg(CEC_TX_MSG_CMD, TX_NO_OP);
cec_dbg_prints("ping_cec_ll_tx:TX_DONE\n");
break;
}
if (reg == TX_ERROR) {
ret = TX_ERROR;
cec_wr_reg(CEC_TX_MSG_CMD, TX_NO_OP);
cec_dbg_prints("ping_cec_ll_tx:TX_ERROR\n");
break;
}
if (!(n--)) {
cec_dbg_prints("ping_cec_ll_tx:TX_BUSY\n");
ret = TX_BUSY;
cec_wr_reg(CEC_TX_MSG_CMD, TX_NO_OP);
break;
}
if (reg != TX_BUSY) {
break;
}
_udelay(500);
}
return ret;
}
#define DEVICE_TV 0
#define DEVICE_RECORDER 1
#define DEVICE_RESERVED 2
#define DEVICE_TUNER 3
#define DEVICE_PLAYBACK 4
#define DEVICE_AUDIO_SYSTEM 5
#define DEVICE_PURE_CEC_SWITCH 6
#define DEVICE_VIDEO_PROCESSOR 7
static unsigned char log_addr_to_devtye(unsigned int addr)
{
static unsigned char addr_map[] = {
DEVICE_TV,
DEVICE_RECORDER,
DEVICE_RECORDER,
DEVICE_TUNER,
DEVICE_PLAYBACK,
DEVICE_AUDIO_SYSTEM,
DEVICE_TUNER,
DEVICE_TUNER,
DEVICE_PLAYBACK,
DEVICE_RECORDER,
DEVICE_TUNER,
DEVICE_PLAYBACK,
DEVICE_RESERVED,
DEVICE_RESERVED,
DEVICE_TV
};
return addr_map[addr & 0xf];
}
static void cec_report_physical_address(void)
{
unsigned char msg[5];
unsigned char phy_addr_ab = (readl(P_AO_DEBUG_REG1) >> 8) & 0xff;
unsigned char phy_addr_cd = readl(P_AO_DEBUG_REG1) & 0xff;
msg[0] = ((cec_msg.log_addr & 0xf) << 4)| CEC_BROADCAST_ADDR;
msg[1] = CEC_OC_REPORT_PHYSICAL_ADDRESS;
msg[2] = phy_addr_ab;
msg[3] = phy_addr_cd;
msg[4] = log_addr_to_devtye(cec_msg.log_addr);
remote_cec_ll_tx(msg, 5);
}
static void cec_report_device_power_status(int dst)
{
unsigned char msg[3];
msg[0] = ((cec_msg.log_addr & 0xf) << 4)| (dst & 0xf);
msg[1] = CEC_OC_REPORT_POWER_STATUS;
msg[2] = cec_msg.power_status;
remote_cec_ll_tx(msg, 3);
}
static void cec_set_stream_path(void)
{
unsigned char phy_addr_ab = (readl(P_AO_DEBUG_REG1) >> 8) & 0xff;
unsigned char phy_addr_cd = readl(P_AO_DEBUG_REG1) & 0xff;
if ((hdmi_cec_func_config >> CEC_FUNC_MASK) & 0x1) {
if ((hdmi_cec_func_config >> AUTO_POWER_ON_MASK) & 0x1) {
if ((phy_addr_ab == cec_msg.buf[cec_msg.rx_read_pos].msg[2]) &&
(phy_addr_cd == cec_msg.buf[cec_msg.rx_read_pos].msg[3])) {
cec_msg.cec_power = 0x1;
}
}
}
}
static void cec_device_vendor_id(void)
{
unsigned char msg[5];
msg[0] = ((cec_msg.log_addr & 0xf) << 4)| CEC_BROADCAST_ADDR;
msg[1] = CEC_OC_DEVICE_VENDOR_ID;
msg[2] = 0x00;
msg[3] = 0x00;
msg[4] = 0x00;
remote_cec_ll_tx(msg, 5);
}
static void cec_menu_status_smp(int menu_status, int dst)
{
unsigned char msg[3];
msg[0] = ((cec_msg.log_addr & 0xf) << 4)| (dst & 0xf);
msg[1] = CEC_OC_MENU_STATUS;
msg[2] = menu_status;
remote_cec_ll_tx(msg, 3);
}
static void cec_give_deck_status(int dst)
{
unsigned char msg[3];
msg[0] = ((cec_msg.log_addr & 0xf) << 4) | (dst & 0xf);
msg[1] = CEC_OC_DECK_STATUS;
msg[2] = 0x1a;
remote_cec_ll_tx(msg, 3);
}
/*static void cec_standby(void)
{
unsigned char msg[2];
msg[0] = ((cec_msg.log_addr & 0xf) << 4) | CEC_BROADCAST_ADDR;
msg[1] = CEC_OC_STANDBY;
remote_cec_ll_tx(msg, 2);
}*/
static void cec_set_osd_name(int dst)
{
unsigned char msg[16];
unsigned char osd_len = cec_strlen(CONFIG_CEC_OSD_NAME);
msg[0] = ((cec_msg.log_addr & 0xf) << 4) | (dst & 0xf);
msg[1] = CEC_OC_SET_OSD_NAME;
cec_memcpy(&msg[2], CONFIG_CEC_OSD_NAME, osd_len);
remote_cec_ll_tx(msg, osd_len + 2);
}
static void cec_get_version(int dst)
{
unsigned char dest_log_addr = cec_msg.log_addr & 0xf;
unsigned char msg[3];
if (0xf != dest_log_addr) {
msg[0] = ((cec_msg.log_addr & 0xf) << 4) | (dst & 0xf);
msg[1] = CEC_OC_CEC_VERSION;
msg[2] = CEC_VERSION_14A;
remote_cec_ll_tx(msg, 3);
}
}
static int check_addr(int phy_addr)
{
unsigned int local_addr = (readl(P_AO_DEBUG_REG1)) & 0xffff;
unsigned int i, mask = 0xf000, a, b;
for (i = 0; i < 4; i++) {
if (!(local_addr & mask)) {
break;
}
a = local_addr & mask;
b = phy_addr & mask;
if (a != b) {// node is not same
cec_dbg_prints("addr fail 1\n");
return 0;
}
mask >>= 4;
}
cec_dbg_prints("addr ok\n");
return 1;
}
static int is_playback_dev(int addr)
{
if (addr != CEC_PLAYBACK_DEVICE_1_ADDR &&
addr != CEC_PLAYBACK_DEVICE_2_ADDR &&
addr != CEC_PLAYBACK_DEVICE_3_ADDR) {
return 0;
}
return 1;
}
static unsigned int cec_handle_message(void)
{
unsigned char opcode;
unsigned char source;
unsigned int phy_addr, wake;
source = (cec_msg.buf[cec_msg.rx_read_pos].msg[0] >> 4) & 0xf;
if (((hdmi_cec_func_config>>CEC_FUNC_MASK) & 0x1) &&
(cec_msg.buf[cec_msg.rx_read_pos].msg_len > 1)) {
opcode = cec_msg.buf[cec_msg.rx_read_pos].msg[1];
switch (opcode) {
case CEC_OC_GET_CEC_VERSION:
cec_get_version(source);
break;
case CEC_OC_GIVE_DECK_STATUS:
cec_give_deck_status(source);
break;
case CEC_OC_GIVE_PHYSICAL_ADDRESS:
cec_report_physical_address();
break;
case CEC_OC_GIVE_DEVICE_VENDOR_ID:
cec_device_vendor_id();
break;
case CEC_OC_GIVE_OSD_NAME:
cec_set_osd_name(source);
break;
case CEC_OC_SET_STREAM_PATH:
cec_set_stream_path();
break;
case CEC_OC_GIVE_DEVICE_POWER_STATUS:
cec_report_device_power_status(source);
break;
case CEC_OC_USER_CONTROL_PRESSED:
if (((hdmi_cec_func_config >> CEC_FUNC_MASK) & 0x1) &&
((hdmi_cec_func_config >> AUTO_POWER_ON_MASK) & 0x1) &&
(cec_msg.buf[cec_msg.rx_read_pos].msg_len == 3) &&
((0x40 == cec_msg.buf[cec_msg.rx_read_pos].msg[2]) ||
(0x6d == cec_msg.buf[cec_msg.rx_read_pos].msg[2]) ||
(0x09 == cec_msg.buf[cec_msg.rx_read_pos].msg[2]) )) {
cec_msg.cec_power = 0x1;
}
break;
case CEC_OC_MENU_REQUEST:
cec_menu_status_smp(DEVICE_MENU_INACTIVE, source);
break;
/* TV Wake up by image/text view on */
case CEC_OC_IMAGE_VIEW_ON:
case CEC_OC_TEXT_VIEW_ON:
if (((hdmi_cec_func_config >> CEC_FUNC_MASK) & 0x1) &&
((hdmi_cec_func_config >> AUTO_POWER_ON_MASK) & 0x1) &&
(!is_playback_dev(cec_msg.log_addr))) {
/* request active source needed */
phy_addr = 0xffff;
cec_msg.cec_power = 0x1;
wake = (phy_addr << 0) |
(source << 16);
writel(wake, P_AO_RTI_STATUS_REG1);
}
break;
/* TV Wake up by active source*/
case CEC_OC_ACTIVE_SOURCE:
phy_addr = (cec_msg.buf[cec_msg.rx_read_pos].msg[2] << 8) |
(cec_msg.buf[cec_msg.rx_read_pos].msg[3] << 0);
if (((hdmi_cec_func_config >> CEC_FUNC_MASK) & 0x1) &&
((hdmi_cec_func_config >> AUTO_POWER_ON_MASK) & 0x1) &&
(!is_playback_dev(cec_msg.log_addr) && check_addr(phy_addr))) {
cec_msg.cec_power = 0x1;
wake = (phy_addr << 0) |
(source << 16);
writel(wake, P_AO_RTI_STATUS_REG1);
}
break;
default:
break;
}
}
cec_rx_read_pos_plus();
return 0;
}
static void cec_reset_addr(void)
{
remote_cec_hw_reset();
cec_wr_reg(CEC_LOGICAL_ADDR0, 0);
cec_hw_buf_clear();
cec_wr_reg(CEC_LOGICAL_ADDR0, cec_msg.log_addr & 0x0f);
_udelay(100);
cec_wr_reg(CEC_LOGICAL_ADDR0, (0x1 << 4) | (cec_msg.log_addr & 0x0f));
}
unsigned int cec_handler(void)
{
unsigned char s_idx;
static int busy_count = 0;
if (0xf == cec_rd_reg(CEC_RX_NUM_MSG)) {
cec_wr_reg(CEC_RX_CLEAR_BUF, 0x1);
cec_wr_reg(CEC_RX_CLEAR_BUF, 0x0);
cec_wr_reg(CEC_RX_MSG_CMD, RX_ACK_CURRENT);
cec_wr_reg(CEC_RX_MSG_CMD, RX_NO_OP);
cec_dbg_prints("error:hw_buf overflow\n");
}
switch (cec_rd_reg(CEC_RX_MSG_STATUS)) {
case RX_DONE:
if (1 == cec_rd_reg(CEC_RX_NUM_MSG)) {
remote_cec_ll_rx();
(cec_msg.rx_write_pos == cec_msg.rx_buf_size - 1) ? (cec_msg.rx_write_pos = 0) : (cec_msg.rx_write_pos++);
}
cec_wr_reg(CEC_RX_MSG_CMD, RX_ACK_CURRENT);
cec_wr_reg(CEC_RX_MSG_CMD, RX_NO_OP);
cec_dbg_prints("RX_OK\n");
break;
case RX_ERROR:
cec_dbg_prints("RX_ERROR\n");
if (TX_ERROR == cec_rd_reg(CEC_TX_MSG_STATUS)) {
cec_dbg_prints("TX_ERROR\n");
cec_reset_addr();
} else {
cec_dbg_prints("TX_other\n");
cec_wr_reg(CEC_RX_MSG_CMD, RX_ACK_CURRENT);
cec_wr_reg(CEC_RX_MSG_CMD, RX_NO_OP);
}
break;
default:
break;
}
switch (cec_rd_reg(CEC_TX_MSG_STATUS)) {
case TX_DONE:
cec_wr_reg(CEC_TX_MSG_CMD, TX_NO_OP);
cec_tx_msgs.send_idx = (cec_tx_msgs.send_idx + 1) & CEC_TX_MSG_BUF_MASK;
s_idx = cec_tx_msgs.send_idx;
if (cec_tx_msgs.send_idx != cec_tx_msgs.queue_idx) {
cec_dbg_prints("TX_OK\n");
cec_triggle_tx(cec_tx_msgs.msg[s_idx].buf,
cec_tx_msgs.msg[s_idx].len);
} else {
cec_dbg_prints("TX_END\n");
}
busy_count = 0;
break;
case TX_ERROR:
cec_dbg_prints("@TX_ERROR\n");
if (RX_ERROR == cec_rd_reg(CEC_RX_MSG_STATUS)) {
cec_dbg_prints("@RX_ERROR\n");
cec_reset_addr();
} else {
cec_wr_reg(CEC_TX_MSG_CMD, TX_NO_OP);
s_idx = cec_tx_msgs.send_idx;
if (cec_tx_msgs.msg[s_idx].retry < 5) {
cec_tx_msgs.msg[s_idx].retry++;
cec_triggle_tx(cec_tx_msgs.msg[s_idx].buf,
cec_tx_msgs.msg[s_idx].len);
} else {
cec_dbg_prints("TX retry too much, abort msg\n");
cec_tx_msgs.send_idx = (cec_tx_msgs.send_idx + 1) & CEC_TX_MSG_BUF_MASK;
}
}
busy_count = 0;
break;
case TX_IDLE:
s_idx = cec_tx_msgs.send_idx;
if (cec_tx_msgs.send_idx != cec_tx_msgs.queue_idx) { // triggle tx if idle
cec_triggle_tx(cec_tx_msgs.msg[s_idx].buf,
cec_tx_msgs.msg[s_idx].len);
}
busy_count = 0;
break;
case TX_BUSY:
busy_count++;
if (busy_count >= 2000) {
uart_puts("busy too long, reset hw\n");
cec_reset_addr();
busy_count = 0;
}
break;
default:
break;
}
if (cec_msg.rx_read_pos != cec_msg.rx_write_pos) {
cec_handle_message();
}
return 0;
}
/*static void check_standby(void)
{
if (((cec_msg.log_addr & 0xf) == 0) &&
((hdmi_cec_func_config >> CEC_FUNC_MASK) & 0x1) &&
((hdmi_cec_func_config >> ONE_TOUCH_STANDBY_MASK) & 0x1)) {
cec_standby();
}
}*/
void cec_node_init(void)
{
static int i = 0;
static unsigned int retry = 0;
static int regist_devs = 0;
static enum _cec_log_dev_addr_e *probe = NULL;
int tx_stat;
unsigned char msg[1];
unsigned int kern_log_addr = (readl(P_AO_DEBUG_REG1) >> 16) & 0xf;
enum _cec_log_dev_addr_e player_dev[3][3] =
{{CEC_PLAYBACK_DEVICE_1_ADDR, CEC_PLAYBACK_DEVICE_2_ADDR, CEC_PLAYBACK_DEVICE_3_ADDR},
{CEC_PLAYBACK_DEVICE_2_ADDR, CEC_PLAYBACK_DEVICE_3_ADDR, CEC_PLAYBACK_DEVICE_1_ADDR},
{CEC_PLAYBACK_DEVICE_3_ADDR, CEC_PLAYBACK_DEVICE_1_ADDR, CEC_PLAYBACK_DEVICE_2_ADDR}};
if (retry >= 12) { // retry all device addr
cec_msg.log_addr = 0x0f;
uart_puts("failed on retried all possible address\n");
return ;
}
writel(0, P_AO_RTI_STATUS_REG1);
if (probe == NULL) {
cec_msg.rx_read_pos = 0;
cec_msg.rx_write_pos = 0;
cec_msg.rx_buf_size = 2;
cec_msg.power_status = 1;
cec_msg.cec_power = 0;
cec_tx_msgs.send_idx = 0;
cec_tx_msgs.queue_idx = 0;
cec_tx_buf_init();
cec_buf_clear();
cec_wr_reg(CEC_LOGICAL_ADDR0, 0);
cec_hw_buf_clear();
cec_wr_reg(CEC_LOGICAL_ADDR0, 0xf);
_udelay(100);
cec_wr_reg(CEC_LOGICAL_ADDR0, (0x1 << 4) | 0xf);
/*
* use kernel cec logic address to detect which logic address is the
* started one to allocate.
*/
cec_dbg_print("kern log_addr:0x", kern_log_addr);
uart_puts("\n");
/* we don't need probe TV address */
if (!is_playback_dev(kern_log_addr)) {
msg[0] = (kern_log_addr << 4) | kern_log_addr;
ping_cec_ll_tx(msg, 1);
cec_msg.log_addr = 0x10 | kern_log_addr;
cec_wr_reg(CEC_LOGICAL_ADDR0, 0);
cec_hw_buf_clear();
cec_wr_reg(CEC_LOGICAL_ADDR0, kern_log_addr);
_udelay(100);
cec_wr_reg(CEC_LOGICAL_ADDR0, (0x1 << 4) | kern_log_addr);
cec_dbg_print("Set cec log_addr:0x", cec_msg.log_addr);
cec_dbg_print(",ADDR0:", cec_rd_reg(CEC_LOGICAL_ADDR0));
uart_puts("\n");
probe = NULL;
regist_devs = 0;
i = 0;
retry = 0;
/*check_standby();*/
return ;
}
for (i = 0; i < 3; i++) {
if (kern_log_addr == player_dev[i][0]) {
probe = player_dev[i];
break;
}
}
if (probe == NULL) {
probe = player_dev[0];
}
i = 0;
}
msg[0] = (probe[i]<<4) | probe[i];
tx_stat = ping_cec_ll_tx(msg, 1);
if (tx_stat == TX_BUSY) { // can't get cec bus
retry++;
remote_cec_hw_reset();
if (!(retry & 0x03)) {
cec_dbg_print("retry too much, log_addr:0x", probe[i]);
uart_puts("\n");
} else {
i -= 1;
}
} else if (tx_stat == TX_ERROR) {
cec_wr_reg(CEC_LOGICAL_ADDR0, 0);
cec_hw_buf_clear();
cec_wr_reg(CEC_LOGICAL_ADDR0, probe[i]);
_udelay(100);
cec_wr_reg(CEC_LOGICAL_ADDR0, (0x1 << 4) | probe[i]);
cec_msg.log_addr = probe[i];
cec_dbg_print("Set cec log_addr:0x", cec_msg.log_addr);
cec_dbg_print(", ADDR0:", cec_rd_reg(CEC_LOGICAL_ADDR0));
uart_puts("\n");
probe = NULL;
regist_devs = 0;
i = 0;
retry = 0;
return ;
} else if (tx_stat == TX_DONE) {
cec_dbg_print("sombody takes cec log_addr:0x", probe[i]);
uart_puts("\n");
regist_devs |= (1 << i);
retry += (4 - (retry & 0x03));
if (regist_devs == 0x07) {
// No avilable logical address
cec_msg.log_addr = 0x0f;
cec_wr_reg(CEC_LOGICAL_ADDR0, (0x1 << 4) | 0xf);
uart_puts("CEC allocate logic address failed\n");
}
}
i++;
if (i == 3) {
i = 0;
}
}
#endif