| /* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ |
| /* |
| * arch/arm/cpu/armv8/tl1/firmware/scp_task/hdmi_cec_arc.c |
| * |
| * Copyright (C) 2020 Amlogic, Inc. All rights reserved. |
| * |
| */ |
| |
| /************************************************** |
| * HDMI CEC uboot code * |
| * * |
| **************************************************/ |
| #ifdef CONFIG_CEC_WAKEUP |
| #include <asm/arch/cec_tx_reg.h> |
| #include <amlogic/aml_cec.h> |
| #include "hdmi_cec_arc.h" |
| #include "task_apis.h" |
| |
| #ifndef NULL |
| #define NULL ((void *)0) |
| #endif |
| |
| #define CEC_DBG_PRINT |
| #define CEC_REG_DEBUG 0 |
| |
| cec_msg_t cec_msg; |
| unsigned char hdmi_cec_func_config; |
| cec_wakeup_t cec_wakup; |
| |
| #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); |
| } |
| #if CEC_REG_DEBUG |
| static void cec_print_reg(char *s, int v, int l) |
| { |
| uart_puts(s); |
| uart_put_hex(v, l); |
| uart_puts("\n"); |
| _udelay(1000); |
| } |
| #endif |
| #else |
| #define cec_dbg_print(s,v) |
| #define cec_dbg_prints(s) |
| #define cec_print_reg(s, v, l) |
| #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 unsigned long cec_rd_reg(unsigned long addr) |
| { |
| unsigned long data32; |
| |
| 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, AO_CECB_RW_REG); |
| data32 = ((readl(AO_CECB_RW_REG)) >> 24) & 0xff; |
| return (data32); |
| } /* cec_rd_reg */ |
| |
| static void cec_wr_reg (unsigned long addr, unsigned long data) |
| { |
| unsigned long data32; |
| |
| 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, AO_CECB_RW_REG); |
| } /* aocec_wr_only_reg */ |
| |
| static inline void cec_set_bits_dwc(uint32_t reg, uint32_t bits, |
| uint32_t start, uint32_t len) |
| { |
| unsigned int tmp; |
| tmp = cec_rd_reg(reg); |
| tmp &= ~(((1 << len) - 1) << start); |
| tmp |= (bits << start); |
| cec_wr_reg(reg, tmp); |
| } |
| |
| static void cec_set_reg_bits(unsigned long addr, unsigned int value, |
| unsigned int offset, unsigned int len) |
| { |
| unsigned int data32 = 0; |
| |
| data32 = readl(addr); |
| data32 &= ~(((1 << len) - 1) << offset); |
| data32 |= (value & ((1 << len) - 1)) << offset; |
| writel(data32, addr); |
| } |
| |
| 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++); |
| } |
| |
| #if CEC_REG_DEBUG |
| static void dump_cecb_reg(void) |
| { |
| int i = 0; |
| unsigned char reg; |
| unsigned int reg32; |
| |
| reg32 = readl(AO_CECB_CLK_CNTL_REG0); |
| cec_print_reg("CLK_CNTL0:", reg32, 32); |
| reg32 = readl(AO_CECB_CLK_CNTL_REG1); |
| cec_print_reg("CLK_CNTL1:", reg32, 32); |
| reg32 = readl(AO_CECB_GEN_CNTL); |
| cec_print_reg("GEN_CNTL:", reg32, 32); |
| reg32 = readl(AO_CECB_RW_REG); |
| cec_print_reg("RW:", reg32, 32); |
| reg32 = readl(AO_CECB_INTR_MASKN); |
| cec_print_reg("INT_MASKN:", reg32, 32); |
| reg32 = readl(AO_CECB_INTR_STAT); |
| cec_print_reg("INT_STAT:", reg32, 32); |
| |
| cec_print_reg("CEC_CTRL:", cec_rd_reg(DWC_CECB_CTRL), 8); |
| cec_print_reg("CEC_MASK:", cec_rd_reg(DWC_CECB_INTR_MASK), 8); |
| cec_print_reg("CEC_ADDR_L:", cec_rd_reg(DWC_CECB_LADD_LOW), 8); |
| cec_print_reg("CEC_ADDR_H:", cec_rd_reg(DWC_CECB_LADD_HIGH), 8); |
| cec_print_reg("CEC_TX_CNT:", cec_rd_reg(DWC_CECB_TX_CNT), 8); |
| cec_print_reg("CEC_RX_CNT:", cec_rd_reg(DWC_CECB_RX_CNT), 8); |
| cec_print_reg("CEC_LOCK:", cec_rd_reg(DWC_CECB_LOCK_BUF), 8); |
| cec_print_reg("CEC_WKUPCTRL:", cec_rd_reg(DWC_CECB_WAKEUPCTRL), 8); |
| |
| cec_dbg_prints("RX buffer:"); |
| for (i = 0; i < 16; i++) { |
| reg = cec_rd_reg(DWC_CECB_RX_DATA00 + i); |
| cec_dbg_print(" ", reg); |
| } |
| cec_dbg_prints("\n"); |
| |
| cec_dbg_prints("TX buffer:"); |
| for (i = 0; i < 16; i++) { |
| reg = cec_rd_reg(DWC_CECB_TX_DATA00 + i); |
| cec_dbg_print(" ", reg); |
| } |
| cec_dbg_prints("\n"); |
| } |
| #else |
| static inline void dump_cecb_reg(void) {} |
| #endif |
| |
| void cec_hw_reset(void) |
| { |
| unsigned int reg; |
| unsigned int data32; |
| cec_dbg_prints("cec reset\n"); |
| |
| reg = (0 << 31) | |
| (0 << 30) | |
| (1 << 28) | /* clk_div0/clk_div1 in turn */ |
| ((732-1) << 12) | /* Div_tcnt1 */ |
| ((733-1) << 0); /* Div_tcnt0 */ |
| writel(reg, AO_CECB_CLK_CNTL_REG0); |
| reg = (0 << 13) | |
| ((11-1) << 12) | |
| ((8-1) << 0); |
| writel(reg, AO_CECB_CLK_CNTL_REG1); |
| |
| reg = readl(AO_CECB_CLK_CNTL_REG0); |
| reg |= (1 << 31); |
| writel(reg, AO_CECB_CLK_CNTL_REG0); |
| |
| _udelay(200); |
| reg |= (1 << 30); |
| writel(reg, AO_CECB_CLK_CNTL_REG0); |
| |
| reg = readl(AO_RTI_PWR_CNTL_REG0); |
| reg |= (0x01 << 14); /* xtal gate */ |
| writel(reg, AO_RTI_PWR_CNTL_REG0); |
| |
| data32 = 0; |
| data32 |= (7 << 12); /* filter_del */ |
| data32 |= (1 << 8); /* filter_tick: 1us */ |
| data32 |= (1 << 3); /* enable system clock */ |
| data32 |= 0 << 1; /* [2:1] cntl_clk: */ |
| /* 0=Disable clk (Power-off mode); */ |
| /* 1=Enable gated clock (Normal mode); */ |
| /* 2=Enable free-run clk (Debug mode). */ |
| data32 |= 1 << 0; /* [0] sw_reset: 1=Reset */ |
| writel(data32, AO_CECB_GEN_CNTL); |
| /* Enable gated clock (Normal mode). */ |
| cec_set_reg_bits(AO_CECB_GEN_CNTL, 1, 1, 1); |
| /* Release SW reset */ |
| cec_set_reg_bits(AO_CECB_GEN_CNTL, 0, 0, 1); |
| /* set up pinmux */ |
| writel(((readl(AO_RTI_PINMUX_REG1) & (~(0xF << 8))) | (2 << 8)), |
| AO_RTI_PINMUX_REG1); |
| /*enable the interrupt*/ |
| writel(CECB_IRQ_EN_MASK, AO_CECB_INTR_MASKN); |
| cec_wr_reg(DWC_CECB_WAKEUPCTRL, 0); |
| cec_dbg_print("Set cec pinmux:0x", (readl(AO_RTI_PINMUX_REG1) >> 8)); |
| cec_dbg_prints("\n"); |
| } |
| |
| static unsigned char remote_cec_ll_rx(void) |
| { |
| int i; |
| int len; |
| |
| len = cec_rd_reg(DWC_CECB_RX_CNT); |
| cec_dbg_prints("cec R:"); |
| for (i = 0; i < len; i++) { |
| cec_msg.buf[cec_msg.rx_write_pos].msg[i] = cec_rd_reg(DWC_CECB_RX_DATA00 + i); |
| cec_dbg_print(" ", cec_msg.buf[cec_msg.rx_write_pos].msg[i]); |
| } |
| /* clr CEC lock bit */ |
| cec_wr_reg(DWC_CECB_LOCK_BUF, 0); |
| cec_msg.buf[cec_msg.rx_write_pos].msg_len = len; |
| 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 = 0, lock; |
| |
| while (1) { |
| /* send is in process */ |
| lock = cec_rd_reg(DWC_CECB_LOCK_BUF); |
| if (lock) { |
| cec_dbg_prints("rx msg in tx\n"); |
| return -1; |
| } |
| if (cec_rd_reg(DWC_CECB_CTRL) & 0x01) |
| i++; |
| else |
| break; |
| if (i > 25) { |
| cec_dbg_prints("wait busy timeout\n"); |
| return -1; |
| } |
| } |
| |
| cec_dbg_prints("cec T:"); |
| for (i = 0; i < len; i++) { |
| cec_wr_reg(DWC_CECB_TX_DATA00 + i, msg[i]); |
| cec_dbg_print(" ", msg[i]); |
| } |
| cec_dbg_prints("\n"); |
| |
| /* start send */ |
| cec_wr_reg(DWC_CECB_TX_CNT, len); |
| cec_set_bits_dwc(DWC_CECB_CTRL, 3, 0, 3); |
| return 0; |
| } |
| |
| 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) |
| { |
| unsigned int reg, ret = 0; |
| unsigned int cnt = 0; |
| |
| remote_cec_ll_tx(msg, len); |
| |
| while (cec_tx_msgs.queue_idx != cec_tx_msgs.send_idx) { |
| reg = readl(AO_CECB_INTR_STAT); |
| writel(reg, AO_CECB_INTR_CLR); |
| if (reg & CECB_IRQ_TX_DONE) { |
| ret = TX_DONE; |
| cec_tx_msgs.send_idx = (cec_tx_msgs.send_idx + 1) & CEC_TX_MSG_BUF_MASK; |
| cec_dbg_prints("ping_cec_tx:TX_DONE\n"); |
| break; |
| } |
| |
| if (reg & CECB_IRQ_TX_NACK) { |
| ret = TX_ERROR; |
| cec_tx_msgs.send_idx = (cec_tx_msgs.send_idx + 1) & CEC_TX_MSG_BUF_MASK; |
| cec_dbg_prints("ping_cec_tx:TX_ERROR\n"); |
| break; |
| } |
| if (reg & CECB_IRQ_TX_ARB_LOST) { |
| ret = TX_BUSY; |
| cec_tx_msgs.send_idx = (cec_tx_msgs.send_idx + 1) & CEC_TX_MSG_BUF_MASK; |
| cec_dbg_prints("ping_cec_tx:TX_ABT_LOST\n"); |
| break; |
| } |
| if (reg & CECB_IRQ_TX_ERR_INITIATOR) { |
| ret = TX_BUSY; |
| cec_tx_msgs.send_idx = (cec_tx_msgs.send_idx + 1) & CEC_TX_MSG_BUF_MASK; |
| cec_dbg_prints("ping_cec_tx:TX_ERR_INIT\n"); |
| break; |
| } |
| _udelay(500); |
| if (cnt++ > 2000) { |
| uart_puts("err: tx not finish flag\n"); |
| break; |
| } |
| } |
| |
| 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(AO_DEBUG_REG1) >> 8) & 0xff; |
| unsigned char phy_addr_cd = readl(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(AO_DEBUG_REG1) >> 8) & 0xff; |
| unsigned char phy_addr_cd = readl(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; |
| } |
| } |
| } |
| } |
| |
| void cec_routing_change(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) { |
| /* wake up if routing destination is self */ |
| if ((phy_addr_ab == cec_msg.buf[cec_msg.rx_read_pos].msg[4]) && |
| (phy_addr_cd == cec_msg.buf[cec_msg.rx_read_pos].msg[5])) |
| 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(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; |
| } |
| |
| int is_phy_addr_ready(cec_msg_t *msg) |
| { |
| /*unsigned int source_addr = readl(AO_RTI_STATUS_REG1);*/ |
| |
| if (msg == NULL) |
| return 0; |
| if (((hdmi_cec_func_config >> CEC_FUNC_MASK) & 0x1) && |
| ((hdmi_cec_func_config >> AUTO_POWER_ON_MASK) & 0x1) && |
| msg->cec_power) { |
| if (cec_wakup.wk_phy_addr && |
| cec_wakup.wk_logic_addr && |
| (cec_wakup.wk_phy_addr != 0xFFFF)) { |
| uart_puts("is_phy_addr_ready 0x"); |
| uart_put_hex(cec_wakup.wk_phy_addr, 16); |
| uart_puts("\n"); |
| return 1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| void cec_save_port_id(void) |
| { |
| unsigned int phy_addr; |
| int i, port_id; |
| |
| phy_addr = cec_wakup.wk_phy_addr; |
| if ((phy_addr == 0xFFFF) || ((phy_addr & 0xF000) == 0)) { |
| cec_wakup.wk_port_id = 0xFF; |
| set_cec_val1(*((unsigned int *)&cec_wakup)); |
| return; |
| } |
| |
| for (i = 0; i < PHY_ADDR_LEN; i++) { |
| port_id = (phy_addr >> (PHY_ADDR_LEN - i - 1)*4) & 0xF; |
| if (port_id == 0) { |
| port_id = (phy_addr >> (PHY_ADDR_LEN - i)*4) & 0xF; |
| break; |
| } |
| } |
| cec_wakup.wk_port_id = port_id; |
| set_cec_val1(*((unsigned int *)&cec_wakup)); |
| } |
| |
| 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_ROUTING_CHANGE: |
| cec_routing_change(); |
| 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, AO_RTI_STATUS_REG1);*/ |
| cec_wakup.wk_logic_addr = source; |
| cec_wakup.wk_phy_addr = phy_addr; |
| set_cec_val1(*((unsigned int *)&cec_wakup)); |
| } |
| 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; |
| cec_msg.active_source = 1; |
| /*wake = (phy_addr << 0) |*/ |
| /* (source << 16);*/ |
| uart_puts("source : 0x"); |
| uart_put_hex(source, 8); |
| uart_puts("\n"); |
| /*writel(wake, AO_RTI_STATUS_REG1);*/ |
| cec_wakup.wk_logic_addr = source; |
| cec_wakup.wk_phy_addr = phy_addr; |
| set_cec_val1(*((unsigned int *)&cec_wakup)); |
| } |
| break; |
| |
| default: |
| break; |
| } |
| } |
| cec_rx_read_pos_plus(); |
| return 0; |
| } |
| |
| static void cec_set_log_addr(int addr) |
| { |
| cec_wr_reg(DWC_CECB_LADD_LOW, 0); |
| cec_wr_reg(DWC_CECB_LADD_HIGH, 0x80); |
| if (addr > 15) |
| return; |
| |
| /*save logic addr for kernel*/ |
| cec_set_reg_bits(AO_DEBUG_REG1, addr, 16, 4); |
| /*write the logic addr*/ |
| if ((addr & 0x0f) < 8) |
| cec_wr_reg(DWC_CECB_LADD_LOW, 1 << addr); |
| else |
| cec_wr_reg(DWC_CECB_LADD_HIGH, (1 << (addr - 8)) | 0x80); |
| _udelay(100); |
| } |
| |
| static void cec_reset_addr(void) |
| { |
| int addr = cec_msg.log_addr; |
| |
| cec_hw_reset(); |
| cec_set_log_addr(addr); |
| } |
| |
| static unsigned char cec_get_log_addr(void) |
| { |
| int i, reg; |
| |
| reg = cec_rd_reg(DWC_CECB_LADD_LOW); |
| reg = (cec_rd_reg(DWC_CECB_LADD_HIGH) << 8) | reg; |
| for (i = 0; i < 16; i++) { |
| if (reg & (1 << i)) |
| break; |
| } |
| if (reg & 0x8000 && i < 16) |
| return i + 16; |
| else if (i < 16) |
| return i; |
| return 0xff; |
| } |
| |
| unsigned int cec_handler(void) |
| { |
| unsigned char s_idx; |
| static int busy_count = 0; |
| int irq; |
| |
| /*dump_cecb_reg();*/ |
| irq = readl(AO_CECB_INTR_STAT); |
| writel(irq, AO_CECB_INTR_CLR); |
| if (irq & CECB_IRQ_RX_EOM) { |
| 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_dbg_prints("RX_OK\n"); |
| } |
| if (irq & CECB_IRQ_RX_ERR_FOLLOWER) { |
| cec_dbg_prints("RX_ERROR\n"); |
| cec_wr_reg(DWC_CECB_LOCK_BUF, 0); |
| } |
| if (irq & CECB_IRQ_RX_WAKEUP) { |
| cec_dbg_prints("rx wake up\n"); |
| cec_wr_reg(DWC_CECB_WAKEUPCTRL, 0); |
| /* TODO: wake up system if needed */ |
| } |
| |
| if (irq & CECB_IRQ_TX_DONE) { |
| 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; |
| } |
| |
| if (irq & CECB_IRQ_TX_NACK) { |
| cec_dbg_prints("@TX_NACK\n"); |
| 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; |
| } |
| |
| if (irq & CECB_IRQ_TX_ERR_INITIATOR) { |
| cec_dbg_prints("@TX_ERR_INIT\n"); |
| 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; |
| } |
| |
| if (irq & CECB_IRQ_TX_ARB_LOST) { |
| busy_count++; |
| if (busy_count >= 2000) { |
| uart_puts("busy too long, reset hw\n"); |
| cec_reset_addr(); |
| busy_count = 0; |
| } |
| } |
| if (cec_msg.rx_read_pos != cec_msg.rx_write_pos) { |
| cec_handle_message(); |
| dump_cecb_reg(); |
| } |
| |
| 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 int *probe = NULL; |
| |
| int tx_stat; |
| unsigned char msg[1]; |
| unsigned int kern_log_addr = (readl(AO_DEBUG_REG1) >> 16) & 0xf; |
| int 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, AO_RTI_STATUS_REG1); |
| memset(&cec_wakup, 0, sizeof(cec_wakup)); |
| 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.active_source = 0; |
| cec_msg.cec_power = 0; |
| cec_tx_msgs.send_idx = 0; |
| cec_tx_msgs.queue_idx = 0; |
| cec_tx_buf_init(); |
| cec_buf_clear(); |
| _udelay(100); |
| /* |
| * 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)) { |
| cec_set_log_addr(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; |
| _udelay(100); |
| cec_dbg_print("Set cec log_addr:0x", cec_msg.log_addr); |
| cec_dbg_print(",ADDR0:", cec_get_log_addr()); |
| 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; |
| } |
| |
| cec_set_log_addr(probe[i]); |
| 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++; |
| 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) { |
| _udelay(100); |
| cec_msg.log_addr = probe[i]; |
| cec_set_log_addr(cec_msg.log_addr); |
| cec_dbg_print("Set cec log_addr:0x", cec_msg.log_addr); |
| cec_dbg_print(", ADDR0:", cec_get_log_addr()); |
| uart_puts("\n"); |
| probe = NULL; |
| regist_devs = 0; |
| i = 0; |
| retry = 0; |
| dump_cecb_reg(); |
| 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_set_log_addr(15); |
| uart_puts("CEC allocate logic address failed\n"); |
| } |
| } |
| i++; |
| if (i == 3) { |
| i = 0; |
| } |
| } |
| |
| int cec_suspend_wakeup_chk(void) |
| { |
| static unsigned int cec_wait_addr = 0; |
| int exit_reason = 0; |
| |
| if ((cec_msg.cec_power == 0x1) && |
| (hdmi_cec_func_config & 0x1)) { |
| if (cec_wait_addr++ < 100) { |
| if (cec_msg.active_source) { |
| cec_save_port_id(); |
| exit_reason = CEC_WAKEUP; |
| uart_puts("check wakeup\n"); |
| } |
| uart_puts("."); |
| } else { |
| exit_reason = CEC_WAKEUP; |
| uart_puts("timeout wakeup\n"); |
| } |
| } |
| |
| if (exit_reason == CEC_WAKEUP) |
| return 1; |
| else |
| return 0; |
| } |
| |
| int cec_suspend_handle(void) |
| { |
| int exit_reason = 0; |
| |
| if (cec_msg.log_addr) { |
| if (hdmi_cec_func_config & 0x1) { |
| cec_handler(); |
| if (cec_msg.cec_power == 0x1) { |
| if (cec_msg.active_source) { |
| cec_save_port_id(); |
| /*cec power key*/ |
| exit_reason = CEC_WAKEUP; |
| uart_puts("message wakeup\n"); |
| } |
| } |
| } |
| } else if (hdmi_cec_func_config & 0x1) |
| cec_node_init(); |
| |
| if (exit_reason == CEC_WAKEUP) { |
| uart_puts("active source:"); |
| uart_put_hex(cec_msg.active_source, 8); |
| uart_puts("\n"); |
| |
| uart_puts("wk_logic_addr:"); |
| uart_put_hex(cec_wakup.wk_logic_addr, 8); |
| uart_puts("\n"); |
| |
| uart_puts("wk_phy_addr:0x"); |
| uart_put_hex(cec_wakup.wk_phy_addr, 16); |
| uart_puts("\n"); |
| |
| uart_puts("wk_port_id:0x"); |
| uart_put_hex(cec_wakup.wk_port_id, 8); |
| uart_puts("\n"); |
| return 1; |
| } else |
| return 0; |
| } |
| #endif |
| |