#include "drv_types.h" #include #include #include #include #include #include #include #include #include #include #include // drv_vmalloc() drv_vfree() #include #include #include #include #include "cec.h" #include "sysreg.h" #include "hdmi_cfg.h" #include "hdmi.h" #include "hdmi_hw.h" #include "hdmi_mapping.h" #include "hdmi_hpd.h" #include "hdmi_dbg.h" #include "debounce.h" #include "hdmi_processing.h" #include "hdmi_time.h" #include "drv_gpio.h" #define CDEV_CEC_MAJOR CEC_DEV_MAJOR #define CECSTATE_ADDR 0xbe0f0510 static INT32 cecd_pid = -1; static struct work_struct wq_cec_signal; static pCEC_MSG_t pMsg = NULL; #ifdef HDMI_DDC5V_WORKAROUND static DEBOUNCE PortA_5V_debounce; static DEBOUNCE PortB_5V_debounce; static DEBOUNCE PortC_5V_debounce; static DRV_5V_LEVEL_e ePortA_SW_5V = DRV_5V_LEVEL_LOW, ePortB_SW_5V = DRV_5V_LEVEL_LOW, ePortC_SW_5V = DRV_5V_LEVEL_LOW; #else static DEBOUNCE Src_5V_debounce; #endif //for ARC 5V GPIO GPIOPin_t geGpio5V_Pin = GPIO_NOT_USE; HDMI_PORT_T geARCPort = HDMI_PORT_NULL; extern BOOL MHL_CABLE_IN; static BOOL MHL_CABLE_A_STATUS = false; static BOOL MHL_CABLE_B_STATUS = false; static DEBOUNCE ArcGpio_5V_debounce; static UINT8 BootUpLog = 0x0; static struct work_struct wq_SetHPD_A_TOGGLE; static struct work_struct wq_SetHPD_B_TOGGLE; static struct work_struct wq_SetHPD_C_TOGGLE; void SetHPD_A_TOGGLE(void) { hdmi_apply_hpd(DRV_HDMI_PORT_A, DRV_HPD_LEVEL_LOW); /* Wait HDMI_HPD_L2H_DELAY ms for hot plug pulse width */ HDMI_DelayMs(HDMI_HPD_L2H_DELAY); hdmi_apply_hpd(DRV_HDMI_PORT_A, DRV_HPD_LEVEL_HIGH); } void SetHPD_B_TOGGLE(void) { hdmi_apply_hpd(DRV_HDMI_PORT_B, DRV_HPD_LEVEL_LOW); /* Wait HDMI_HPD_L2H_DELAY ms for hot plug pulse width */ HDMI_DelayMs(HDMI_HPD_L2H_DELAY); hdmi_apply_hpd(DRV_HDMI_PORT_B, DRV_HPD_LEVEL_HIGH); } void SetHPD_C_TOGGLE(void) { hdmi_apply_hpd(DRV_HDMI_PORT_C, DRV_HPD_LEVEL_LOW); /* Wait HDMI_HPD_L2H_DELAY ms for hot plug pulse width */ HDMI_DelayMs(HDMI_HPD_L2H_DELAY); hdmi_apply_hpd(DRV_HDMI_PORT_C, DRV_HPD_LEVEL_HIGH); } #ifdef INIT_BY_KMF static void TxFIFO_handler(pCEC_MSG_t pm); static INT32 QueryLink(UINT32 pAddr) { return (pAddr & 0xf000) >> 12; } static UINT32 query_physical_address(UINT8 logAddr) { UINT32 paddr = 0; INT32 timeout = 20, count; UINT8 cec_interrupt_flag = false; pFRAME_t m = &(pMsg->txMsg); m->block_num = 2; m->src = 0x0; m->dst = logAddr; m->opcode = 0x83; TxFIFO_handler(pMsg); HDMI_DelayMs(55); for (; timeout > 0; timeout--) { if (m->status == CEC_STATUS_TXNOACK) { cecdbg("CEC_STATUS_TXNOACK\n"); return 0; } if (m->status == CEC_STATUS_TXEOMDONE) { cecdbg("CEC_STATUS_TXEOMDONE\n"); break; } HDMI_DelayMs(25); } timeout = 30; for(;;) { HDMI_DelayMs(100); //cecdbg("HDMI_RegisterRead(CEC_rxData_EOM) = %d, (pMsg->bMsg = %d) , (timeout = %d) , (pMsg->bMessageDone = %d)",HDMI_RegisterRead(CEC_rxData_EOM), (pMsg->bMsg) , (timeout) , (pMsg->bMessageDone)); if(pMsg->bMessageDone == true) { break; } if((timeout--) <= 0) { break; } } cecdbg("timeout = %d\n",timeout); for(timeout = 0;timeout<20 && (cec_interrupt_flag == false);timeout++) { pFRAME_t f; INT32 i = pMsg->Rp; count = pMsg->Wp; for (; i != count ; i = (i + 1) % FRAMEMAX) { f = &pMsg->rxMsg[i]; if (f->opcode == 0x84 && f->src == logAddr && f->block_num == 5) { cec_interrupt_flag = true; paddr = ((f->param[0] << 8) | f->param[1]); break; } } HDMI_DelayMs(50); } return paddr; } /* return hdmi link no to umf: 1-hdmi1, 2-hdmi2 */ INT32 CEC_Query(UINT8 logAddr) { INT32 ret; BootUpLog = logAddr; ret = QueryLink(query_physical_address(logAddr)); cecdbg("%s: logAddr 0x%x in hdmi %d\n", __FUNCTION__, logAddr, ret); return ret; } #endif #ifdef HDMI_DDC5V_WORKAROUND static void Check_PortA_5V_change(void *data) { BOOL fIs_5V_Changed = FALSE; //DRV_5V_LEVEL_e ePortA_5V_Level = *((DRV_5V_LEVEL_e*)data); DRV_5V_LEVEL_e ePortA_5V_Level = (DRV_5V_LEVEL_e)data; //UINT8 fInRange = HDMI_RegisterRead(HDMIRX_IN_RANGE); if(ePortA_5V_Level == DRV_5V_LEVEL_HIGH) { if(ePortA_SW_5V == DRV_5V_LEVEL_LOW) { cecdbg("A in\n"); fIs_5V_Changed = TRUE; ePortA_SW_5V = DRV_5V_LEVEL_HIGH; //if(hdmi_get_cur_port() == HDMI_PORT_A) // hdmi_set_termination(DRV_HDMI_PORT_A, DRV_HPD_LEVEL_HIGH); #ifdef CONFIG_CHANGE_HOT_PLUG_ACTION #ifndef CONFIG_SUPPORT_CEC_TV if(hdmi_get_cur_port() == HDMI_PORT_A) #endif #endif hdmi_apply_hpd(DRV_HDMI_PORT_A, DRV_HPD_LEVEL_HIGH); } //turn on SW 5V prevent HDMI auto reset when DDC 5V low if(hdmi_get_cur_port() == HDMI_PORT_A) sysset_HDMI_SW5V(HDMI_PORT_A, TRUE); } else if(ePortA_5V_Level == DRV_5V_LEVEL_LOW && ePortA_SW_5V == DRV_5V_LEVEL_HIGH) { if((hdmi_get_cur_port() == HDMI_PORT_A) && (hdmi_flag_check(HAS_ACTIVE_DATA))) { return; } else { cecdbg("A out\n"); fIs_5V_Changed = TRUE; ePortA_SW_5V = DRV_5V_LEVEL_LOW; hdmi_apply_hpd(DRV_HDMI_PORT_A, DRV_HPD_LEVEL_LOW); //hdmi_set_termination(DRV_HDMI_PORT_A, DRV_HPD_LEVEL_LOW); } } if( fIs_5V_Changed == TRUE) { hdmi_hpd_update(); if((hdmi_get_cur_port() == HDMI_PORT_A) &&(ePortA_SW_5V ==DRV_5V_LEVEL_HIGH)) { schedule_work(&wq_SetHPD_A_TOGGLE); } } } static void Check_PortB_5V_change(void *data) { BOOL fIs_5V_Changed = FALSE; //DRV_5V_LEVEL_e ePortB_5V_Level = *((DRV_5V_LEVEL_e*)data); DRV_5V_LEVEL_e ePortB_5V_Level = (DRV_5V_LEVEL_e)data; //UINT8 fInRange = HDMI_RegisterRead(HDMIRX_IN_RANGE); if(ePortB_5V_Level == DRV_5V_LEVEL_HIGH) { if(ePortB_SW_5V == DRV_5V_LEVEL_LOW) { cecdbg("B in\n"); fIs_5V_Changed = TRUE; ePortB_SW_5V = DRV_5V_LEVEL_HIGH; //if(hdmi_get_cur_port() == HDMI_PORT_B) // hdmi_set_termination(DRV_HDMI_PORT_B, DRV_HPD_LEVEL_HIGH); #ifdef CONFIG_CHANGE_HOT_PLUG_ACTION #ifndef CONFIG_SUPPORT_CEC_TV if(hdmi_get_cur_port() == HDMI_PORT_B) #endif #endif hdmi_apply_hpd(DRV_HDMI_PORT_B, DRV_HPD_LEVEL_HIGH); } //turn on SW 5V prevent HDMI auto reset when DDC 5V low if(hdmi_get_cur_port() == HDMI_PORT_B) sysset_HDMI_SW5V(HDMI_PORT_B, TRUE); } else if(ePortB_5V_Level == DRV_5V_LEVEL_LOW && ePortB_SW_5V == DRV_5V_LEVEL_HIGH) { if((hdmi_get_cur_port() == HDMI_PORT_B) && (hdmi_flag_check(HAS_ACTIVE_DATA))) { return; } else { cecdbg("B out\n"); fIs_5V_Changed = TRUE; ePortB_SW_5V = DRV_5V_LEVEL_LOW; hdmi_apply_hpd(DRV_HDMI_PORT_B, DRV_HPD_LEVEL_LOW); //hdmi_set_termination(DRV_HDMI_PORT_B, DRV_HPD_LEVEL_LOW); } } if( fIs_5V_Changed == TRUE) { hdmi_hpd_update(); if((hdmi_get_cur_port() == HDMI_PORT_B) &&(ePortB_SW_5V ==DRV_5V_LEVEL_HIGH)) { schedule_work(&wq_SetHPD_B_TOGGLE); } } } static void Check_PortC_5V_change(void *data) { BOOL fIs_5V_Changed = FALSE; //DRV_5V_LEVEL_e ePortC_5V_Level = *((DRV_5V_LEVEL_e*)data); DRV_5V_LEVEL_e ePortC_5V_Level = (DRV_5V_LEVEL_e)data; //UINT8 fInRange = HDMI_RegisterRead(HDMIRX_IN_RANGE); if(ePortC_5V_Level == DRV_5V_LEVEL_HIGH) { if(ePortC_SW_5V == DRV_5V_LEVEL_LOW) { cecdbg("C in\n"); fIs_5V_Changed = TRUE; ePortC_SW_5V = DRV_5V_LEVEL_HIGH; //if(hdmi_get_cur_port() == HDMI_PORT_C) // hdmi_set_termination(DRV_HDMI_PORT_C, DRV_HPD_LEVEL_HIGH); #ifdef CONFIG_CHANGE_HOT_PLUG_ACTION #ifndef CONFIG_SUPPORT_CEC_TV if(hdmi_get_cur_port() == HDMI_PORT_C) #endif #endif hdmi_apply_hpd(DRV_HDMI_PORT_C, DRV_HPD_LEVEL_HIGH); } //turn on SW 5V prevent HDMI auto reset when DDC 5V low if(hdmi_get_cur_port() == HDMI_PORT_C) sysset_HDMI_SW5V(HDMI_PORT_C, TRUE); } else if(ePortC_5V_Level == DRV_5V_LEVEL_LOW && ePortC_SW_5V == DRV_5V_LEVEL_HIGH) { if((hdmi_get_cur_port() == HDMI_PORT_C) && (hdmi_flag_check(HAS_ACTIVE_DATA))) { return; } else { cecdbg("C out\n"); fIs_5V_Changed = TRUE; ePortC_SW_5V = DRV_5V_LEVEL_LOW; hdmi_apply_hpd(DRV_HDMI_PORT_C, DRV_HPD_LEVEL_LOW); //hdmi_set_termination(DRV_HDMI_PORT_C, DRV_HPD_LEVEL_LOW); } } if( fIs_5V_Changed == TRUE) { hdmi_hpd_update(); if((hdmi_get_cur_port() == HDMI_PORT_C) &&(ePortC_SW_5V ==DRV_5V_LEVEL_HIGH)) { schedule_work(&wq_SetHPD_C_TOGGLE); } } } DRV_5V_LEVEL_e CEC_Get_SW5V(HDMI_PORT_T ePort) { DRV_5V_LEVEL_e eLevel; switch(ePort) { case HDMI_PORT_A: eLevel = ePortA_SW_5V; break; case HDMI_PORT_B: eLevel = ePortB_SW_5V; break; case HDMI_PORT_C: eLevel = ePortC_SW_5V; break; default: eLevel = DRV_5V_LEVEL_LOW; break; } return eLevel; } #else static void CheckSrc5V_change(void *dummy) { hdmi_hpd_update(); } #endif static void CEC_parser_wq(void *dummy) { struct task_struct *p; p = find_task_by_vpid(cecd_pid); if (p) { send_sig_info(CEC_SIG, (void*)1, p); } } static void CEC_parser(void) { schedule_work(&wq_cec_signal); } static void RxFIFO_handler(pCEC_MSG_t pm) { if (!pm->rxFifo_cnt) { return; } HDMI_RegisterWrite(CEC_Rx_FIFO_POP, 0); pm->rxFifo_cnt--; if (HDMI_RegisterRead(CEC_rxData_sbit)) { pm->rxBlock_ptr = 0; pm->rxMsg[pm->Wp].block_num = 0; pm->bMsg = true; } if (pm->bMsg) { pm->rxMsg[pm->Wp].block[pm->rxBlock_ptr] = HDMI_RegisterRead(CEC_rxData); //hdmidbg("\trxMsg[%d].block[%d]=0x%02x\tA%dE%d\n",pm->Wp,pm->rxBlock_ptr,HDMI_RegisterRead(CEC_rxData), HDMI_RegisterRead(CEC_rxData_ACK), HDMI_RegisterRead(CEC_rxData_EOM)); if (pm->rxBlock_ptr < 17) { pm->rxBlock_ptr++; } } if (HDMI_RegisterRead(CEC_rxData_EOM) && pm->bMsg) { #ifdef CONFIG_SUPPORT_DEBUG_MESSAGE INT32 c,n; INT8 str[40]; #endif pm->rxMsg[pm->Wp].block_num = pm->rxBlock_ptr; pm->rxBlock_ptr = 0; pm->rxFifo_cnt = 0; pm->bMsg = false; #ifdef CONFIG_SUPPORT_DEBUG_MESSAGE //print recieved CEC message for(c=0,n=0;crxMsg[pm->Wp].block_num;c++) { if (n >= 0) { n += sprintf(&str[n], "%02x%s", pm->rxMsg[pm->Wp].block[c], c != (pm->rxMsg[pm->Wp].block_num - 1) ? "" : "\n"); } } cecdbg("Msg recv %s",str); #endif pm->Wp = (pm->Wp + 1) % FRAMEMAX; if (pm->Wp == pm->Rp) { pm->bOverwrite = true; } pm->bMessageDone = true; CEC_parser(); } HDMI_RegisterWrite(CEC_Rx_FIFO_POP, 1); } static void TxFIFO_handler(pCEC_MSG_t pm) { pFRAME_t ptxMsg = &(pm->txMsg); INT32 i = 0; #ifdef CONFIG_SUPPORT_DEBUG_MESSAGE INT32 c,n; INT8 str[40]; #endif if (ptxMsg->status == CEC_STATUS_TXSENDING) { return; } if (ptxMsg->block_num <= 0) { return; } ptxMsg->status = CEC_STATUS_TXSENDING; HDMI_RegisterWrite(CEC_HEADER_follower, ptxMsg->block[0] & 0xf); HDMI_RegisterWrite(CEC_HEADER_initiator, ptxMsg->block[0] >> 4); for (i = 1; i < ptxMsg->block_num; i++) { HDMI_RegisterWrite(CEC_DataBlock00 + (i - 1), ptxMsg->block[i]); } #ifdef CONFIG_SUPPORT_DEBUG_MESSAGE //print send CEC message for(c=0,n=0;cblock_num;c++) { if (n >= 0) { n += sprintf(&str[n], "%02x%s", ptxMsg->block[c], c != (ptxMsg->block_num - 1) ? "" : "\n"); } } cecdbg("Msg sent: %s",str); #endif if (ptxMsg->block_num > 1) { HDMI_RegisterWrite(CEC_Header_EOM, 0); HDMI_RegisterWrite(CEC_Block_XX_EOM, ptxMsg->block_num - 2); HDMI_RegisterWrite(CEC_R_retry_th, 3); } else { HDMI_RegisterWrite(CEC_Header_EOM, 1); HDMI_RegisterWrite(CEC_Block_XX_EOM, 0); HDMI_RegisterWrite(CEC_R_retry_th, 1); } HDMI_RegisterWrite(CEC_TransmissionStart, 0); HDMI_RegisterWrite(CEC_TransmissionStart, 1); } #ifdef CONFIG_DEBUG_DRV_CEC_WRITE_EN static UINT8 char2Hex(INT8 ch) { if (ch >= '0' && ch <= '9') { return ch - '0'; } else if (ch >= 'a' && ch <= 'f') { return ch - 'a' + 10; } else if (ch >= 'A' && ch <= 'F') { return ch - 'A' + 10; } return 0x0; } static void msg_parser(const INT8 *str) { UINT8 ch = '\0'; UINT32 i = 0; while (*str != ' ' && *str != '\0' && *(str + 1) != '\0') { ch = char2Hex(*str) * 16 + char2Hex(*(str + 1)); str = str + 2; pMsg->txMsg.block[i] = ch; i++; if (i >= 17) { break; } } pMsg->txMsg.block_num = i; } #endif static void BootUpPatch(void) { cecdbg("%s:\n", __FUNCTION__); /* Patch: Change source to related hdmi source */ if (BootUpLog) { UINT32 addr; FRAME_t m; pMsg->bMessageDone = false; addr = query_physical_address(BootUpLog); m.block[0] = 0xf | ((BootUpLog & 0xf) << 4); m.block[1] = 0x82; // Active Source m.block[2] = ((addr >> 8) & 0xff); m.block[3] = ((addr) & 0xff); m.block_num = 4; if (pMsg->bOverwrite) { cecdbg("Msg overwrite\n"); pMsg->Rp++; } memcpy(&pMsg->rxMsg[pMsg->Wp], &m, sizeof(FRAME_t)); pMsg->txMsg.status = CEC_STATUS_TXEOMDONE; pMsg->txMsg.block_num = 0; pMsg->Wp = (pMsg->Wp + 1) % FRAMEMAX; if (pMsg->Wp == pMsg->Rp) { cecdbg("Msg Full\n"); pMsg->bOverwrite = true; } pMsg->bMessageDone = true; cecdbg("Patch: Send log 0x%01x addr 0x%04x\n", BootUpLog, addr); BootUpLog = 0; } } #ifdef CONFIG_DEBUG_DRV_CEC_WRITE_EN INT32 CEC_write(struct file *file, const INT8 __user * buf, size_t size, loff_t * ppos) { switch (buf[0]) { case 'm': case 'M': msg_parser(buf + 1); TxFIFO_handler(pMsg); break; case 't': case 'T': msg_parser(buf + 1); memcpy(&pMsg->rxMsg[pMsg->Wp], &pMsg->txMsg, sizeof(FRAME_t)); pMsg->txMsg.status = CEC_STATUS_TXEOMDONE; pMsg->txMsg.block_num = 0; pMsg->Wp = (pMsg->Wp + 1) % FRAMEMAX; if (pMsg->Wp == pMsg->Rp) { pMsg->bOverwrite = true; } pMsg->bMessageDone = true; CEC_parser(); break; default: break; } return size; } #endif void DRV_CEC_Transmit_ARC(UINT8 ucEnable, UINT8 hw_port, INT8 sw_port) { sysset_cec_arc(ucEnable ? true : false); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,8) long CEC_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) #else ssize_t CEC_ioctl(struct inode *inode, struct file *filp, UINT32 cmd, ULONG arg) #endif { BOOL bDebugLog = bCECDBG; UINT32 uiPhyAddr; UINT8 hw_port, ucEnable = 0; INT8 sw_port; switch (cmd) { case CEC_CECEnable: cecdbg("IOC CECEnable:%ld\n",arg); if (arg==0) { ucEnable = 0; cecdbg("Set ARC OFF\n"); DRV_CEC_Transmit_ARC(ucEnable , hw_port, sw_port); } break; case CEC_REGISTER: cecdbg("IOC REGISTER:PID%d\n", cecd_pid); /* Patch: boot up by cec, resend active source to cecd */ BootUpPatch(); /* Report hpd status again when cecd is started. */ hdmi_report_cur_hpd(); /* Assign cecd pid for driver. Put this at the last one. */ cecd_pid = arg; break; case CEC_DEBUGLOG: copy_to_user((void*)arg, (void*)&bDebugLog, sizeof(BOOL)); cecdbg("IOC DEBUGLOG:%d\n", bCECDBG); break; case CEC_SENDMSG: cecdbg("IOC SENDMSG\n"); TxFIFO_handler(pMsg); break; case CEC_DEBUG://For AP debug only. memcpy(&pMsg->rxMsg[pMsg->Wp], &pMsg->txMsg, sizeof(FRAME_t)); pMsg->txMsg.status = CEC_STATUS_TXEOMDONE; pMsg->txMsg.block_num = 0; pMsg->Wp = (pMsg->Wp + 1) % FRAMEMAX; if (pMsg->Wp == pMsg->Rp) { pMsg->bOverwrite = true; } pMsg->bMessageDone = true; CEC_parser(); break; case CEC_ReportARCInitiation: uiPhyAddr = arg; cecdbg("IOC ReportARCInitiationn:0x%x\n", uiPhyAddr); hw_port = (UINT8) hdmi_hw_port(((uiPhyAddr >> 12) & 0x0f) - 1); sw_port = hdmi_sw_port(((uiPhyAddr >> 12) & 0x0f) - 1); //switch port ucEnable = 1; DRV_CEC_Transmit_ARC(ucEnable, hw_port, sw_port); break; case CEC_ReportARCTermination: uiPhyAddr = arg; cecdbg("IOC ReportARCTermination:0x%x\n", uiPhyAddr); hw_port = (UINT8) hdmi_hw_port(((uiPhyAddr >> 12) & 0x0f) - 1); sw_port = hdmi_sw_port(((uiPhyAddr >> 12) & 0x0f) - 1); //switch port ucEnable = 0; DRV_CEC_Transmit_ARC(ucEnable, hw_port, sw_port); break; case CEC_ARC_PORT_HPD_TOGGLE: printk("IOC CEC_ARC_PORT_HPD_TOGGLE:%ld\n",arg); if (arg==1) { printk("CEC_ARC_PORT_HPD_TOGGLE\n"); hdmi_apply_hpd((1 << geARCPort), DRV_HPD_LEVEL_LOW); HDMI_DelayMs(HDMI_HPD_L2H_DELAY); hdmi_apply_hpd((1 << geARCPort), DRV_HPD_LEVEL_HIGH); } break; } return 0; } INT32 CEC_mmap(struct file *filp, struct vm_area_struct *vma) { if (vma->vm_end - vma->vm_start > 4096) { return -ENOMEM; } if (remap_pfn_range(vma, vma->vm_start, vmalloc_to_pfn(pMsg), vma->vm_end - vma->vm_start, vma->vm_page_prot)) { return -EAGAIN; } return 0; } irqreturn_t CEC_ISR(INT32 irq, void* dev_id) { //static DRV_5V_LEVEL_e ePortA_5V_Level = DRV_5V_LEVEL_LOW, ePortB_5V_Level = DRV_5V_LEVEL_LOW, ePortC_5V_Level = DRV_5V_LEVEL_LOW; //volatile CEC_REG0000_t *tCEC_INT = (CEC_REG0000_t *)0xbe1e0000; CEC_REG0000_t tCEC_INT; CEC_REG0000_t dCEC_INT_tmp; tCEC_INT.dCECReg0000_0003 = HDMI_RegisterRead(CEC_0000_DW_0000); dCEC_INT_tmp.dCECReg0000_0003 = tCEC_INT.dCECReg0000_0003 & 0xF00017FF; //mask interrupt bit if(geARCPort != HDMI_PORT_A) { if (tCEC_INT.ists_ddc5v_0_rise) { cecdbg("INT A 5V in\n"); dCEC_INT_tmp.ists_ddc5v_0_rise = 1; //for clear //ePortA_5V_Level = DRV_5V_LEVEL_HIGH; if(((true != MHL_CABLE_IN) && (MHL_CABLE_A_STATUS == false)) || (true == MHL_CABLE_B_STATUS)) { #ifdef HDMI_DDC5V_WORKAROUND debounce_notice(&PortA_5V_debounce, (void *)DRV_5V_LEVEL_HIGH, 0); #else debounce_notice(&Src_5V_debounce, NULL, 30); #endif } else { MHL_CABLE_A_STATUS = true; } } if (tCEC_INT.ists_ddc5v_0_fall) { cecdbg("INT A 5V out\n"); dCEC_INT_tmp.ists_ddc5v_0_fall = 1; //for clear //ePortA_5V_Level = DRV_5V_LEVEL_LOW; if(true != MHL_CABLE_A_STATUS) { #ifdef HDMI_DDC5V_WORKAROUND debounce_notice(&PortA_5V_debounce, (void *)DRV_5V_LEVEL_LOW, 500); #else debounce_notice(&Src_5V_debounce, NULL, 30); #endif } else { MHL_CABLE_A_STATUS = false; } } } if(geARCPort != HDMI_PORT_B) { if (tCEC_INT.ists_ddc5v_1_rise) { cecdbg("INT B 5V in\n"); dCEC_INT_tmp.ists_ddc5v_1_rise = 1; //for clear //ePortB_5V_Level = DRV_5V_LEVEL_HIGH; if(((true != MHL_CABLE_IN) && (MHL_CABLE_B_STATUS == false)) || (true == MHL_CABLE_A_STATUS)) { #ifdef HDMI_DDC5V_WORKAROUND debounce_notice(&PortB_5V_debounce, (void *)DRV_5V_LEVEL_HIGH, 0); #else debounce_notice(&Src_5V_debounce, NULL, 30); #endif } else { MHL_CABLE_B_STATUS = true; } } if (tCEC_INT.ists_ddc5v_1_fall) { cecdbg("INT B 5V out\n"); dCEC_INT_tmp.ists_ddc5v_1_fall = 1; //for clear //ePortB_5V_Level = DRV_5V_LEVEL_LOW; if(true != MHL_CABLE_B_STATUS) { #ifdef HDMI_DDC5V_WORKAROUND debounce_notice(&PortB_5V_debounce, (void *)DRV_5V_LEVEL_LOW, 500); #else debounce_notice(&Src_5V_debounce, NULL, 30); #endif } else { MHL_CABLE_B_STATUS = false; } } } if(geARCPort != HDMI_PORT_C) { if (tCEC_INT.R_INTR_Status_rx_ddc5v_2_rise) { cecdbg("INT C 5V in\n"); dCEC_INT_tmp.R_INTR_Status_rx_ddc5v_2_rise = 1; //for clear //ePortC_5V_Level = DRV_5V_LEVEL_HIGH; #ifdef HDMI_DDC5V_WORKAROUND debounce_notice(&PortC_5V_debounce, (void *)DRV_5V_LEVEL_HIGH, 0); #else debounce_notice(&Src_5V_debounce, NULL, 30); #endif } if (tCEC_INT.R_INTR_Status_rx_ddc5v_2_fall) { cecdbg("INT C 5V out\n"); dCEC_INT_tmp.R_INTR_Status_rx_ddc5v_2_fall = 1; //for clear //ePortC_5V_Level = DRV_5V_LEVEL_LOW; #ifdef HDMI_DDC5V_WORKAROUND debounce_notice(&PortC_5V_debounce, (void *)DRV_5V_LEVEL_LOW, 500); #else debounce_notice(&Src_5V_debounce, NULL, 30); #endif } } if (tCEC_INT.ists_tx_noarbit) { cecdbg("tx_noarbit\n"); dCEC_INT_tmp.ists_tx_noarbit = 1; //for clear pMsg->txMsg.status = CEC_STATUS_TXNOARBIT; pMsg->txMsg.block_num = 0; } else if (tCEC_INT.ists_tx_noack) { cecdbg("x_noack\n"); dCEC_INT_tmp.ists_tx_noack = 1; //for clear pMsg->txMsg.status = CEC_STATUS_TXNOACK; pMsg->txMsg.block_num = 0; } else if (tCEC_INT.ists_tx_eom_done) { pFRAME_t ptxMsg = &(pMsg->txMsg); cecdbg("tx_eom_done\n"); dCEC_INT_tmp.ists_tx_eom_done = 1; //for clear ptxMsg->status = CEC_STATUS_TXEOMDONE; ptxMsg->block_num = 0; } else if (tCEC_INT.ists_rfifo_ready) { cecdbg("rfifo_ready\n"); dCEC_INT_tmp.ists_rfifo_ready = 1; //for clear pMsg->rxFifo_cnt = HDMI_RegisterRead(CEC_R_rfifo_th) + 1; RxFIFO_handler(pMsg); } else if (tCEC_INT.ists_rx_pop_done) { cecdbg("rx_pop_done\n"); dCEC_INT_tmp.ists_rx_pop_done = 1; //for clear RxFIFO_handler(pMsg); } else if (tCEC_INT.ists_rx_discon) { cecdbg("rx_discon\n"); dCEC_INT_tmp.ists_rx_discon = 1; //for clear } else if (tCEC_INT.ists_rx_eom) { cecdbg("rx_eom\n"); dCEC_INT_tmp.ists_rx_eom = 1; //for clear pMsg->rxFifo_cnt = HDMI_RegisterRead(CEC_R_rfifo_th); RxFIFO_handler(pMsg); } HDMI_RegisterWrite(CEC_0000_DW_0000, dCEC_INT_tmp.dCECReg0000_0003); //clear interrupt status return IRQ_HANDLED; } static void CheckArcGpio5V_change(void *dummy) { UINT32 Delay_ms; DRV_5V_LEVEL_e eGpio5VLevel = (GPIOTryRead(geGpio5V_Pin) == 1?DRV_5V_LEVEL_HIGH:DRV_5V_LEVEL_LOW); cecdbg("%s Gpio5VLevel=%d\n", __FUNCTION__, eGpio5VLevel); if(eGpio5VLevel == DRV_5V_LEVEL_HIGH) { EnableGPIOInterrupt(geGpio5V_Pin, 0x0); // 00 low_level trigger, 01 high_level trigger, 10 falling edge, 11 rising edge } else { EnableGPIOInterrupt(geGpio5V_Pin, 0x1); // 00 low_level trigger, 01 high_level trigger, 10 falling edge, 11 rising edge } if((eGpio5VLevel == DRV_5V_LEVEL_LOW) && (hdmi_flag_check(HAS_ACTIVE_DATA))) { Delay_ms = 2000; } else { Delay_ms = 0; } #ifdef HDMI_DDC5V_WORKAROUND if(geARCPort == HDMI_PORT_A) { debounce_notice(&PortA_5V_debounce, (void *)eGpio5VLevel, Delay_ms); } else if(geARCPort == HDMI_PORT_B) { debounce_notice(&PortB_5V_debounce, (void *)eGpio5VLevel, Delay_ms); } else if(geARCPort == HDMI_PORT_C) { debounce_notice(&PortC_5V_debounce, (void *)eGpio5VLevel, Delay_ms); } #else debounce_notice(&Src_5V_debounce, NULL, 0); #endif } void DRV_GPIO5V_ISR_Callback(void) { DisableGPIOInterrupt(geGpio5V_Pin); //disable interrupt when interrupt happend cecdbg("%s\n", __FUNCTION__); debounce_notice(&ArcGpio_5V_debounce, (void *)NULL, 250); //debounce GPIO 5V value for 250ms and enable interrupt } char check_mhl_det(void) { int i; for( i=0 ; i<5 ; i++ ) { if(GPIOGetValueByPinFunc(GPIO_PIN_MHL_CD_SENSE_DETECT)==1) { cecdbg("in MHL mode ignore set hpd %d \n",i); return 1; } } return 0; } void DRV_Set_GPIO5V_ARCPort(GPIOPin_t eGpio5V_Pin, HDMI_PORT_T eARCPort) { UINT8 bIntrEn = 0; geGpio5V_Pin = eGpio5V_Pin; geARCPort = eARCPort; cecdbg("%s GPIO:%d ARCPort:%d\n", __FUNCTION__, geGpio5V_Pin, geARCPort); //setup DDC 5V interrupt bIntrEn = (geARCPort == HDMI_PORT_A)?0: (hdmi_get_channel_at_hw_port(HDMI_PORT_A) == HDMI_SRC_NULL)?0:1; HDMI_RegisterWrite(CEC_ien_ddc5v_0_rise, bIntrEn); HDMI_RegisterWrite(CEC_ien_ddc5v_0_fall, bIntrEn); sysset_DDC_PortA_Det5V_En(bIntrEn); bIntrEn = (geARCPort == HDMI_PORT_B)?0: (hdmi_get_channel_at_hw_port(HDMI_PORT_B) == HDMI_SRC_NULL)?0:1; HDMI_RegisterWrite(CEC_ien_ddc5v_1_rise, bIntrEn); HDMI_RegisterWrite(CEC_ien_ddc5v_1_fall, bIntrEn); sysset_DDC_PortB_Det5V_En(bIntrEn); bIntrEn = (geARCPort == HDMI_PORT_C)?0: (hdmi_get_channel_at_hw_port(HDMI_PORT_C) == HDMI_SRC_NULL)?0:1; HDMI_RegisterWrite(CEC_R_INTR_en_rx_ddc5v_2_rise, bIntrEn); HDMI_RegisterWrite(CEC_R_INTR_en_rx_ddc5v_2_fall, bIntrEn); sysset_DDC_PortC_Det5V_En(bIntrEn); if(geGpio5V_Pin != GPIO_NOT_USE && geARCPort != HDMI_PORT_NULL) { RegGPIOCallBackFun(geGpio5V_Pin, DRV_GPIO5V_ISR_Callback); //dabounce GPIO 5V value for 250ms and enable interrupt debounce_notice(&ArcGpio_5V_debounce, (void *)NULL, 250); } #ifdef CONFIG_HDMI_SUPPORT_MHL #if CONFIG_HDMI_MHL_PORT == 0 if(check_mhl_det()== 0) #endif #endif { #ifdef CONFIG_HDMI_GPIO_DETECT_PORT_A #else sysset_HDMI_SW5V(HDMI_PORT_A,false); #endif } #ifdef CONFIG_HDMI_SUPPORT_MHL #if CONFIG_HDMI_MHL_PORT == 1 if(check_mhl_det()== 0) #endif #endif { #ifdef CONFIG_HDMI_GPIO_DETECT_PORT_B #else sysset_HDMI_SW5V(HDMI_PORT_B,false); #endif } { #ifdef CONFIG_HDMI_GPIO_DETECT_PORT_C #else sysset_HDMI_SW5V(HDMI_PORT_C,false); #endif } } HDMI_PORT_T DRV_Get_GPIO5V_ARCPort(void) { return geARCPort; } #define IRQ_CEC 34 static void CEC_dispatch(struct pt_regs *regs) { do_IRQ(IRQ_CEC); } static struct file_operations CEC_fops = { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,8)) .unlocked_ioctl = CEC_ioctl, #else .ioctl = CEC_ioctl, #endif #ifdef CONFIG_DEBUG_DRV_CEC_WRITE_EN .write = CEC_write, #endif .mmap = CEC_mmap, .owner = THIS_MODULE, }; #define RXFIFO_TH 2 #define CECDRV_DEVNAME "/dev/cec" /* Driver Device Name */ static INT32 cec_devno; static struct cdev cec_cdev; INT32 DRV_CEC_Init(void) { INT32 status; UINT8 *pCECstate = (UINT8*)CECSTATE_ADDR; cecdbg("%s\n", __FUNCTION__); pMsg = (pCEC_MSG_t)drv_vmalloc(sizeof(CEC_MSG_t), MODULEID_CEC); memset(pMsg, 0x0, sizeof(CEC_MSG_t)); sysset_hdmi_hpd_detection(); HDMI_RegisterWrite(CEC_ien_ddc5v_0_rise, 0); HDMI_RegisterWrite(CEC_ien_ddc5v_0_fall, 0); HDMI_RegisterWrite(CEC_ien_ddc5v_1_rise, 0); HDMI_RegisterWrite(CEC_ien_ddc5v_1_fall, 0); HDMI_RegisterWrite(CEC_R_INTR_en_rx_ddc5v_2_rise, 0); HDMI_RegisterWrite(CEC_R_INTR_en_rx_ddc5v_2_fall, 0); if(HDMI_RegisterRead(CEC_ists_ddc5v_0_rise) == 1){cecdbg("CEC_ists_ddc5v_0_rise clr");HDMI_RegisterWrite(CEC_ists_ddc5v_0_rise,0);} if(HDMI_RegisterRead(CEC_ists_ddc5v_0_fall) == 1){cecdbg("CEC_ists_ddc5v_0_fall clr");HDMI_RegisterWrite(CEC_ists_ddc5v_0_fall,0);} if(HDMI_RegisterRead(CEC_ists_ddc5v_1_rise) == 1){cecdbg("CEC_ists_ddc5v_1_rise clr");HDMI_RegisterWrite(CEC_ists_ddc5v_1_rise,0);} if(HDMI_RegisterRead(CEC_ists_ddc5v_1_fall) == 1){cecdbg("CEC_ists_ddc5v_1_fall clr");HDMI_RegisterWrite(CEC_ists_ddc5v_1_fall,0);} if(HDMI_RegisterRead(CEC_R_INTR_Status_rx_ddc5v_2_rise) == 1){cecdbg("CEC_R_INTR_Status_rx_ddc5v_2_rise clr");HDMI_RegisterWrite(CEC_R_INTR_Status_rx_ddc5v_2_rise,0);} if(HDMI_RegisterRead(CEC_R_INTR_Status_rx_ddc5v_2_fall) == 1){cecdbg("CEC_R_INTR_Status_rx_ddc5v_2_fall clr");HDMI_RegisterWrite(CEC_R_INTR_Status_rx_ddc5v_2_fall,0);} if (!pMsg) { cecdbg("%s: Can not allocate memory\n", __FUNCTION__); return -ENOMEM; } if (*pCECstate) { cecdbg("onetouch play"); hdmi_apply_hpd(DRV_HDMI_PORT_ALL , DRV_HPD_LEVEL_LOW); HDMI_DelayMs(HDMI_HPD_L2H_DELAY); hdmi_apply_hpd_by5V(DRV_HDMI_PORT_ALL); } INIT_WORK(&wq_cec_signal, (work_func_t) CEC_parser_wq); #ifdef HDMI_DDC5V_WORKAROUND debounce_init(&PortA_5V_debounce, Check_PortA_5V_change); debounce_init(&PortB_5V_debounce, Check_PortB_5V_change); debounce_init(&PortC_5V_debounce, Check_PortC_5V_change); #else debounce_init(&Src_5V_debounce, CheckSrc5V_change); #endif debounce_init(&ArcGpio_5V_debounce, CheckArcGpio5V_change); INIT_WORK(&wq_SetHPD_A_TOGGLE, (work_func_t) SetHPD_A_TOGGLE); INIT_WORK(&wq_SetHPD_B_TOGGLE, (work_func_t) SetHPD_B_TOGGLE); INIT_WORK(&wq_SetHPD_C_TOGGLE, (work_func_t) SetHPD_C_TOGGLE); set_vi_handler(IRQ_CEC, (vi_handler_t) CEC_dispatch); status = request_irq(IRQ_CEC, &CEC_ISR, IRQF_DISABLED, "cec", NULL); if (status) { return -EIO; } cec_devno = MKDEV(CDEV_CEC_MAJOR, 0); status = register_chrdev_region(cec_devno, 0, CECDRV_DEVNAME); if (status) { return -EIO; } cdev_init(&cec_cdev, &CEC_fops); cec_cdev.owner = THIS_MODULE; cec_cdev.ops = &CEC_fops; if (cdev_add(&cec_cdev, cec_devno, 1)) { return -EIO; } sysset_hdmi_hpd_detection(); HDMI_RegisterWrite(CEC_R_cec_en, 0); //Disable CEC HDMI_RegisterWrite(CEC_R_cec_en, 1); //Enable CEC HDMI_RegisterWrite(CEC_R_rfifo_th, RXFIFO_TH); HDMI_RegisterWrite(CEC_ien_tx_noarbit, 1); HDMI_RegisterWrite(CEC_ien_tx_noack, 1); HDMI_RegisterWrite(CEC_ien_tx_eom_done, 1); HDMI_RegisterWrite(CEC_ien_rfifo_ready, 1); HDMI_RegisterWrite(CEC_ien_rx_pop_done, 1); HDMI_RegisterWrite(CEC_ien_rx_discon, 1); HDMI_RegisterWrite(CEC_ien_rx_eom, 1); //HDMI_RegisterWrite(CEC_timer_retryframe_period, 0xca); //HDMI_RegisterWrite(CEC_timer_newframe_preiod, 0x12); //HDMI_RegisterWrite(CEC_timer_conframe_period, 0x1d); HDMI_RegisterWrite(CEC_timer_bit_tran_period, 0x37); HDMI_RegisterWrite(CEC_timer_conframe_period, 0x1E); HDMI_RegisterWrite(CEC_timer_dbit_end_max_period, 0x55); HDMI_RegisterWrite(CEC_timer_dbit_end_min_peiord, 0x12); HDMI_RegisterWrite(CEC_timer_dbit_high_min_period, 0x1F); HDMI_RegisterWrite(CEC_timer_dbit_low_min_period, 0x21); HDMI_RegisterWrite(CEC_timer_newframe_preiod, 0x12); HDMI_RegisterWrite(CEC_timer_retryframe_period, 0xCF); HDMI_RegisterWrite(CEC_timer_rx_dbit_end_max_period, 0x5A); HDMI_RegisterWrite(CEC_timer_rx_dbit_end_min_period, 0x5A); HDMI_RegisterWrite(CEC_timer_rx_dbit_latch_period, 0x6B); HDMI_RegisterWrite(CEC_timer_sbit_end_min_period, 0x16); HDMI_RegisterWrite(CEC_timer_sbit_tran_min_period, 0x3A); HDMI_RegisterWrite(CEC_timer_tx_dbit_end_period, 0x2C); HDMI_RegisterWrite(CEC_timer_tx_dbit_high_period, 0x1D); HDMI_RegisterWrite(CEC_timer_tx_dbit_low_period, 0x2C); HDMI_RegisterWrite(CEC_timer_tx_sbit_end_period, 0x2B); HDMI_RegisterWrite(CEC_timer_tx_sbit_tran_period, 0x3D); //enable 5V interrupt move to DRV_Set_GPIO5V_ARCPort //HDMI_RegisterWrite(CEC_ien_ddc5v_0_rise, 1); //HDMI_RegisterWrite(CEC_ien_ddc5v_0_fall, 1); //HDMI_RegisterWrite(CEC_ien_ddc5v_1_rise, 1); //HDMI_RegisterWrite(CEC_ien_ddc5v_1_fall, 1); //HDMI_RegisterWrite(CEC_R_INTR_en_rx_ddc5v_2_rise, 1); //HDMI_RegisterWrite(CEC_R_INTR_en_rx_ddc5v_2_fall, 1); HDMI_RegisterWrite(CEC_R_hpd_man, 1); /* enable */ pMsg->txMsg.status = CEC_STATUS_TXEOMDONE; pMsg->txMsg.block_num = 0; pMsg->Wp = 0; pMsg->Rp = 0; pMsg->bMessageDone = false; return 0; } void DRV_CEC_Exit(void) { //Disable all interrupts. HDMI_RegisterWrite(CEC_0008_DW_0008, HDMI_RegisterRead(CEC_0008_DW_0008) & 0xfffff000); HDMI_RegisterWrite(CEC_0020_DW_0020, HDMI_RegisterRead(CEC_0020_DW_0020) & 0xf0ffffff); HDMI_RegisterWrite(CEC_R_cec_en, 0);//Disable CEC free_irq(IRQ_CEC, NULL); cdev_del(&cec_cdev); unregister_chrdev_region(cec_devno, 0); #ifdef HDMI_DDC5V_WORKAROUND debounce_exit(&PortA_5V_debounce); debounce_exit(&PortB_5V_debounce); debounce_exit(&PortC_5V_debounce); #else debounce_exit(&Src_5V_debounce); #endif if (pMsg) { vfree(pMsg); } }