/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation version 2. * This program is distributed AS-IS WITHOUT ANY WARRANTY of any * kind, whether express or implied; INCLUDING without the implied warranty * of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE or NON-INFRINGEMENT. * See the GNU General Public License for more details at * http://www.gnu.org/licenses/gpl-2.0.html. */ #include #include #include #include #include #include #include #include #include "si_fw_macros.h" #include "si_app_devcap.h" #include "si_infoframe.h" #include "si_edid.h" #include "si_mhl_defs.h" #include "si_mhl2_edid_3d_api.h" #include "si_8620_internal_api.h" #include "si_mhl_tx_hw_drv_api.h" #ifdef MEDIA_DATA_TUNNEL_SUPPORT #include "si_mdt_inputdev.h" #endif #include "si_mhl_callback_api.h" #include "si_8620_drv.h" #include "mhl_linux_tx.h" #include "si_8620_regs.h" #include "mhl_supp.h" #include "platform.h" #include "mhl_device_cfg.h" #define SLEEP_10MS 10 #define RFIFO_FILL_DELAY 10 #define MHL_STREAM_ID 0 #define CONTENT_TYPE_DEFAULT 0 /* Local functions */ #ifdef USE_HW_TIMER static int int_1_isr(struct drv_hw_context *hw_context, uint8_t int_1_status); #endif static int int_2_isr(struct drv_hw_context *hw_context, uint8_t int_2_status); static int int_3_isr(struct drv_hw_context *hw_context, uint8_t int_3_status); static int int_4_isr(struct drv_hw_context *hw_context, uint8_t int_4_status); static int int_5_isr(struct drv_hw_context *hw_context, uint8_t int_5_status); static int int_8_isr(struct drv_hw_context *hw_context, uint8_t intr_8_status); static int int_9_isr(struct drv_hw_context *hw_context, uint8_t int_9_status); static int hdcp_isr(struct drv_hw_context *hw_context, uint8_t intr_status); static int hdcp2_isr(struct drv_hw_context *hw_context, uint8_t intr_status); static int g2wb_err_isr(struct drv_hw_context *hw_context, uint8_t intr_stat); static int g2wb_isr(struct drv_hw_context *hw_context, uint8_t intr_stat); static int mhl_cbus_isr(struct drv_hw_context *hw_context, uint8_t cbus_int); static int mhl_cbus_err_isr(struct drv_hw_context *hw_context, uint8_t cbus_err_int); static int mhl3_block_isr(struct drv_hw_context *hw_context, uint8_t status); static int coc_isr(struct drv_hw_context *hw_context, uint8_t coc_int_status); static int tdm_isr(struct drv_hw_context *hw_context, uint8_t intr_status); static int int_link_trn_isr(struct drv_hw_context *hw_context, uint8_t intr_status); static void enable_intr(struct drv_hw_context *hw_context, uint8_t intr_num, uint8_t intr_mask); static void switch_to_idle(struct drv_hw_context *hw_context, bool do_interrupt_clear); static void disconnect_mhl(struct drv_hw_context *hw_context, bool do_interrupt_clear); static void start_hdcp(struct drv_hw_context *hw_context); static void start_hdcp_content_type(struct drv_hw_context *hw_context); static void stop_video(struct drv_hw_context *hw_context); static int get_device_rev(struct drv_hw_context *hw_context); static void unmute_video(struct drv_hw_context *hw_context); static int set_hdmi_params(struct mhl_dev_context *dev_context); static void hsic_init(struct drv_hw_context *hw_context); static void si_mhl_tx_drv_enable_emsc_block(struct drv_hw_context *hw_context); static void disable_gen2_write_burst_rcv(struct drv_hw_context *hw_context); static void disable_gen2_write_burst_xmit(struct drv_hw_context *hw_context); static void si_mhl_tx_drv_start_gen2_write_burst( struct drv_hw_context *hw_context); static void si_mhl_tx_drv_set_lowest_tmds_link_speed( struct mhl_dev_context *dev_context, uint32_t pixel_clock_frequency, uint8_t bits_per_pixel); static int start_video(struct drv_hw_context *hw_context); /* Local data */ #define FIELD_RATE_MEASUREMENT_INTERVAL 50 #define DDC_ABORT_THRESHOLD 10 static int ddc_abort_count; #define MSC_ABORT_THRESHOLD 10 static int msc_abort_count; struct intr_tbl { uint8_t aggr_stat_index; uint8_t aggr_stat_id_bit; uint8_t mask; uint16_t mask_addr; uint16_t stat_addr; int (*isr) (struct drv_hw_context *, uint8_t status); char *name; }; enum l1_intr_stat_enums_t { FAST_INTR_STAT, L1_INTR_STAT_0, L1_INTR_STAT_1, L1_INTR_STAT_2, L1_INTR_STAT_3, L1_INTR_STAT_4, L1_INTR_STAT_5, /* this one MUST be last */ NUM_AGGREGATED_INTR_REGS }; struct intr_tbl g_intr_tbl[] = { {L1_INTR_STAT_2, 0x10, 0, REG_CBUS_DISC_INTR0_MASK, REG_CBUS_DISC_INTR0, int_4_isr, "DISC"}, {L1_INTR_STAT_1, 0x08, 0, REG_MDT_INT_1_MASK, REG_MDT_INT_1, g2wb_err_isr, "G2WB"}, {L1_INTR_STAT_1, 0x04, 0, REG_MDT_INT_0_MASK, REG_MDT_INT_0, g2wb_isr, "G2WB"}, {L1_INTR_STAT_5, 0x08, 0, REG_COC_INTR_MASK, REG_COC_INTR, coc_isr, "COC"}, {L1_INTR_STAT_4, 0x04, 0, REG_TRXINTMH, REG_TRXINTH, tdm_isr, "TDM"}, {L1_INTR_STAT_1, 0x01, 0, REG_CBUS_INT_0_MASK, REG_CBUS_INT_0, mhl_cbus_isr, "MSC"}, {L1_INTR_STAT_1, 0x02, 0, REG_CBUS_INT_1_MASK, REG_CBUS_INT_1, mhl_cbus_err_isr, "MERR"}, {L1_INTR_STAT_2, 0x40, 0, REG_EMSCINTRMASK, REG_EMSCINTR, mhl3_block_isr, "BLOCK"}, {L1_INTR_STAT_2, 0x80, 0, REG_EMSCINTRMASK1, REG_EMSCINTR1, int_link_trn_isr, "LTRN"}, {L1_INTR_STAT_0, 0x20, 0, REG_INTR8_MASK, REG_INTR8, int_8_isr, "INFR"}, {L1_INTR_STAT_0, 0x80, 0, REG_TPI_INTR_EN, REG_TPI_INTR_ST0, hdcp_isr, "HDCP"}, {L1_INTR_STAT_3, 0x01, 0, REG_HDCP2X_INTR0_MASK, REG_HDCP2X_INTR0, hdcp2_isr, "HDCP2"}, {L1_INTR_STAT_0, 0x40, 0, REG_INTR9_MASK, REG_INTR9, int_9_isr, "EDID"}, {L1_INTR_STAT_0, 0x04, 0, REG_INTR3_MASK, REG_INTR3, int_3_isr, "DDC"}, {L1_INTR_STAT_0, 0x08, 0, REG_INTR5_MASK, REG_INTR5, int_5_isr, "SCDT"}, {L1_INTR_STAT_0, 0x02, 0, REG_INTR2_MASK, REG_INTR2, int_2_isr, "INT2"}, #ifdef USE_HW_TIMER {L1_INTR_STAT_0, 0x01, 0, REG_INTR1_MASK, REG_INTR1, int_1_isr, "TIMR"}, #endif }; enum intr_nums_t { INTR_DISC, INTR_G2WB_ERR, INTR_G2WB, INTR_COC, INTR_TDM, INTR_MSC, INTR_MERR, INTR_BLOCK, INTR_LINK_TRAINING, INTR_INFR, INTR_HDCP, INTR_HDCP2, INTR_EDID, INTR_DDC, INTR_SCDT, INTR_USER, #ifdef USE_HW_TIMER INTR_TIMR, #endif MAX_INTR }; #define BIT_RGND_READY_INT \ BIT_CBUS_DISC_INTR0_CBUS_DISC_INTR0_STAT6 #define BIT_CBUS_MHL12_DISCON_INT \ BIT_CBUS_DISC_INTR0_CBUS_DISC_INTR0_STAT5 #define BIT_CBUS_MHL3_DISCON_INT \ BIT_CBUS_DISC_INTR0_CBUS_DISC_INTR0_STAT4 #define BIT_NOT_MHL_EST_INT \ BIT_CBUS_DISC_INTR0_CBUS_DISC_INTR0_STAT3 #define BIT_MHL_EST_INT \ BIT_CBUS_DISC_INTR0_CBUS_DISC_INTR0_STAT2 #define BIT_MHL3_EST_INT \ BIT_CBUS_DISC_INTR0_CBUS_DISC_INTR0_STAT1 #define BIT_RGND_READY_INT_MASK \ BIT_CBUS_DISC_INTR0_MASK_CBUS_DISC_INTR0_MASK6 #define BIT_CBUS_MHL12_DISCON_INT_MASK \ BIT_CBUS_DISC_INTR0_MASK_CBUS_DISC_INTR0_MASK5 #define BIT_CBUS_MHL3_DISCON_INT_MASK \ BIT_CBUS_DISC_INTR0_MASK_CBUS_DISC_INTR0_MASK4 #define BIT_NOT_MHL_EST_INT_MASK \ BIT_CBUS_DISC_INTR0_MASK_CBUS_DISC_INTR0_MASK3 #define BIT_MHL_EST_INT_MASK \ BIT_CBUS_DISC_INTR0_MASK_CBUS_DISC_INTR0_MASK2 #define BIT_MHL3_EST_INT_MASK \ BIT_CBUS_DISC_INTR0_MASK_CBUS_DISC_INTR0_MASK1 #ifdef USE_HW_TIMER #define BIT_HW_TIMER_POP BIT_INTR1_STAT7 #endif #define BIT_DDC_CMD_DONE BIT_INTR3_STAT3 #define BIT_INTR_SCDT_CHANGE BIT_INTR5_STAT0 #define BIT_CEA_NEW_VSI BIT_INTR8_MASK2 #define BIT_CEA_NEW_AVI BIT_INTR8_MASK1 #define BIT_VID_OVRRD_ENABLE_AUTO_LINK_MODE_UPDATE 0x08 #define BIT_MDT_RFIFO_DATA_RDY BIT_MDT_INT_0_MDT_INT_0_0 #define BIT_MDT_IDLE_AFTER_HAWB_DISABLE BIT_MDT_INT_0_MDT_INT_0_2 #define BIT_MDT_XFIFO_EMPTY BIT_MDT_INT_0_MDT_INT_0_3 #define BIT_MDT_RCV_TIMEOUT BIT_MDT_INT_1_MDT_INT_1_0 #define BIT_MDT_RCV_SM_ABORT_PKT_RCVD BIT_MDT_INT_1_MDT_INT_1_1 #define BIT_MDT_RCV_SM_ERROR BIT_MDT_INT_1_MDT_INT_1_2 #define BIT_MDT_XMIT_TIMEOUT BIT_MDT_INT_1_MDT_INT_1_5 #define BIT_MDT_XMIT_SM_ABORT_PKT_RCVD BIT_MDT_INT_1_MDT_INT_1_6 #define BIT_MDT_XMIT_SM_ERROR BIT_MDT_INT_1_MDT_INT_1_7 #define BIT_CBUS_DDC_PEER_ABORT BIT_DDC_ABORT_INT_DDC_ABORT_INT_STAT7 #define BIT_CBUS_MSC_MT_DONE_NACK \ BIT_CBUS_INT_0_MASK_CBUS_INT_0_MASK7 #define BIT_CBUS_MSC_MR_SET_INT \ BIT_CBUS_INT_0_MASK_CBUS_INT_0_MASK6 #define BIT_CBUS_MSC_MR_WRITE_BURST \ BIT_CBUS_INT_0_MASK_CBUS_INT_0_MASK5 #define BIT_CBUS_MSC_MR_MSC_MSG \ BIT_CBUS_INT_0_MASK_CBUS_INT_0_MASK4 #define BIT_CBUS_MSC_MR_WRITE_STAT \ BIT_CBUS_INT_0_MASK_CBUS_INT_0_MASK3 #define BIT_CBUS_HPD_CHG \ BIT_CBUS_INT_0_MASK_CBUS_INT_0_MASK2 #define BIT_CBUS_MSC_MT_DONE \ BIT_CBUS_INT_0_MASK_CBUS_INT_0_MASK1 #define BIT_CBUS_CNX_CHG \ BIT_CBUS_INT_0_MASK_CBUS_INT_0_MASK0 #define BIT_CBUS_CMD_ABORT BIT_CBUS_INT_1_MASK_CBUS_INT_1_MASK6 #define BIT_CBUS_MSC_ABORT_RCVD BIT_CBUS_INT_1_MASK_CBUS_INT_1_MASK3 #define BIT_CBUS_DDC_ABORT BIT_CBUS_INT_1_MASK_CBUS_INT_1_MASK2 #define BIT_CBUS_CEC_ABORT BIT_CBUS_INT_1_MASK_CBUS_INT_1_MASK1 #define BIT_INTR9_EDID_ERROR BIT_INTR9_STAT6 #define BIT_INTR9_EDID_DONE BIT_INTR9_STAT5 #define BIT_INTR9_DEVCAP_DONE BIT_INTR9_STAT4 #define BIT_INTR9_EDID_ERROR_MASK BIT_INTR9_MASK6 #define BIT_INTR9_EDID_DONE_MASK BIT_INTR9_MASK5 #define BIT_INTR9_DEVCAP_DONE_MASK BIT_INTR9_MASK4 #define BIT_TDM_INTR_SYNC_DATA BIT_TRXINTH_TRX_INTR8 #define BIT_TDM_INTR_SYNC_WAIT BIT_TRXINTH_TRX_INTR9 #define BIT_TDM_INTR_SYNC_DATA_MASK BIT_TRXINTMH_TRX_INTRMASK8 #define BIT_TDM_INTR_SYNC_WAIT_MASK BIT_TRXINTMH_TRX_INTRMASK9 #define BIT_HDCP2_INTR_AUTH_DONE \ BIT_HDCP2X_INTR0_HDCP2X_INTR0_STAT0 #define BIT_HDCP2_INTR_AUTH_FAIL \ BIT_HDCP2X_INTR0_HDCP2X_INTR0_STAT1 #define BIT_HDCP2_INTR_RPTR_RCVID_CHANGE \ BIT_HDCP2X_INTR0_HDCP2X_INTR0_STAT4 #define BIT_TMDS_CSTAT_P3_DISABLE_AUTO_AVIF_CLEAR 0x04 #define BIT_TMDS_CSTAT_P3_AVIF_MANUAL_CLEAR_STROBE 0x08 #define BIT_CBUS_MSC_MT_ABORT_INT_MSC_MT_PEER_ABORT \ BIT_MSC_MT_ABORT_INT_MSC_MT_ABORT_INT_STAT7 #define BIT_PAGE_CBUS_REG_MSC_MT_ABORT_INT_STAT5 \ BIT_MSC_MT_ABORT_INT_MSC_MT_ABORT_INT_STAT5 #define BIT_CBUS_MSC_MT_ABORT_INT_UNDEF_CMD \ BIT_MSC_MT_ABORT_INT_MSC_MT_ABORT_INT_STAT3 #define BIT_CBUS_MSC_MT_ABORT_INT_TIMEOUT \ BIT_MSC_MT_ABORT_INT_MSC_MT_ABORT_INT_STAT2 #define BIT_CBUS_MSC_MT_ABORT_INT_PROTO_ERR \ BIT_MSC_MT_ABORT_INT_MSC_MT_ABORT_INT_STAT1 #define BIT_CBUS_MSC_MT_ABORT_INT_MAX_FAIL \ BIT_MSC_MT_ABORT_INT_MSC_MT_ABORT_INT_STAT0 #define REG_CBUS_MHL_SCRPAD_BASE 0x40 #define REG_RX_HDMI_CTRL0_DEFVAL_DVI 0x14 #define REG_RX_HDMI_CTRL0_DEFVAL_HDMI 0x1C #define REG_RX_HDMI_CTRL0_DEFVAL_HW_CTRL 0x10 #define REG_RX_HDMI_CTRL2_DEFVAL_DVI 0x30 #define REG_RX_HDMI_CTRL2_DEFVAL_HDMI 0x38 #define TX_HW_RESET_PERIOD 10 #define TX_HW_RESET_DELAY 100 #define MHL_FLOW_FLAG_NONE 0x0000 #define MHL_FLOW_FLAG_MHL_IMPEDANCE 0x0001 #define MHL_FLOW_FLAG_MHL_ESTABLISHED 0x0002 #define MHL_FLOW_FLAG_DCAP_READY 0x0004 #define MHL_FLOW_FLAG_DCAP_CHANGE 0x0008 #define MHL_FLOW_FLAG_PATH_ENABLE 0x0010 #define MHL_FLOW_FLAG_RAP_CONTENT_ON 0x0020 #define MHL_FLOW_FLAG_CBUS_SET_HPD 0x0040 #define MHL_FLOW_FLAG_EDID_RTP 0x0080 #define MHL_FLOW_FLAG_INPUT_CLOCK_STABLE 0x0100 #define MHL_FLOW_FLAG_INFOFRAME_RECEIVED 0x0200 #define MHL_FLOW_FLAG_CP_AVAILABLE 0x0400 #define MHL_FLOW_FLAG_CP_AUTHENTICATED 0x0800 #define BIT_COC_PLL_LOCK_STATUS_CHANGE 0x01 #define BIT_COC_CALIBRATION_DONE 0x02 #define MSK_TDM_SYNCHRONIZED 0xC0 #define VAL_TDM_SYNCHRONIZED 0x80 #define BIT_COC_STAT_6_CALIBRATION_DONE 0x80 #define BITS_ES0_0_COC_STAT_0_FSM_STATE_MASK 0x0F #define BITS_ES0_0_COC_STAT_F_CALIBRATION_STATE_2 0x02 #define BITS_ES1_0_COC_STAT_0_CALIBRATION_MASK 0x8F #define BITS_ES1_0_COC_STAT_0_CALIBRATION_STATE_2 0x02 #define BITS_ES1_0_COC_STAT_0_PLL_LOCKED 0x80 #define VAL_M3_CTRL_MHL1_2_VALUE (BIT_M3_CTRL_SW_MHL3_SEL | \ BIT_M3_CTRL_ENC_TMDS) #if defined(DEBUG) static char *rgnd_value_string[] = { "open", "2k", "1k", "short" }; #endif #define IN_MHL3_MODE(hw_context) \ (hw_context->cbus_mode > CM_oCBUS_PEER_IS_MHL1_2) static uint8_t colorSpaceTranslateInfoFrameToHw[] = { VAL_INPUT_FORMAT_RGB, VAL_INPUT_FORMAT_YCBCR422, VAL_INPUT_FORMAT_YCBCR444, VAL_INPUT_FORMAT_INTERNAL_RGB }; /* Based on module parameter "crystal_khz=xxxxx" program registers. */ static void program_ext_clock_regs(struct drv_hw_context *hw_context, int crystal_khz) { /* preset to default crystal on SK - 19.2 MHz */ int reg_fb_div_ctl_main = 0x04; int reg_hdcp2x_tp1 = 0x5E; switch (crystal_khz) { case 38400: reg_fb_div_ctl_main = 0x0C; reg_hdcp2x_tp1 = 0xBC; break; case 30000: reg_fb_div_ctl_main = 0x06; reg_hdcp2x_tp1 = 0x92; break; case 24000: reg_fb_div_ctl_main = 0x05; reg_hdcp2x_tp1 = 0x75; break; case 20000: reg_fb_div_ctl_main = 0x04; reg_hdcp2x_tp1 = 0x62; break; case 19200: default: /* reg_fb_div_ctl_main = 0x04; reg_hdcp2x_tp1 = 0x5E;*/ break; } mhl_tx_write_reg(hw_context, REG_DIV_CTL_MAIN, reg_fb_div_ctl_main); mhl_tx_write_reg(hw_context, REG_HDCP2X_TP1, reg_hdcp2x_tp1); MHL_TX_DBG_INFO("Set ext clock regs: 3F2 = %02X, 3B4 = %02X\n", reg_fb_div_ctl_main, reg_hdcp2x_tp1); } static uint8_t ok_to_proceed_with_ddc(struct drv_hw_context *hw_context) { int cbus_status = mhl_tx_read_reg(hw_context, REG_CBUS_STATUS); if (cbus_status < 0) { MHL_TX_DBG_INFO("I2C error: 0x%02x\n", cbus_status); return 0; } if (!(BIT_CBUS_STATUS_CBUS_HPD & cbus_status)) return 0; if (!(BIT_CBUS_STATUS_CBUS_CONNECTED & cbus_status)) return 0; return cbus_status & (BIT_CBUS_STATUS_CBUS_HPD | BIT_CBUS_STATUS_CBUS_CONNECTED); } uint8_t si_mhl_tx_drv_ecbus_connected(struct mhl_dev_context *dev_context) { struct drv_hw_context *hw_context = (struct drv_hw_context *)&dev_context->drv_context; int cbus_status = mhl_tx_read_reg(hw_context, REG_CBUS_STATUS); if (cbus_status < 0) { MHL_TX_DBG_INFO("I2C error: 0x%02x\n", cbus_status); return 0; } return cbus_status & BIT_CBUS_STATUS_CBUS_CONNECTED; } #if 0 static void enable_heartbeat(struct drv_hw_context *hw_context) { /* * turn on disconnection based on heartbeat (as well as RSEN) - * This is no longer the default behavior. * use_heartbeat=2 is required to enable disconnection. */ switch (platform_get_flags() & PLATFORM_FLAG_HEARTBEAT_MASK) { case PLATFORM_VALUE_DISCONN_HEARTBEAT: MHL_TX_DBG_INFO ("Disconnection on heartbeat failure is enabled\n"); mhl_tx_modify_reg(hw_context, REG_DISC_CTRL1, BIT_DISC_CTRL1_HB_ONLY, BIT_DISC_CTRL1_HB_ONLY); /* intentionally fall through */ case PLATFORM_VALUE_ISSUE_HEARTBEAT: /* Turn on heartbeat polling */ MHL_TX_DBG_WARN("Heartbeat polling is enabled\n"); mhl_tx_write_reg(hw_context, REG_MSC_HEARTBEAT_CONTROL, 0xA7); break; default: MHL_TX_DBG_INFO ("Disconnection on heartbeat failure is disabled\n"); MHL_TX_DBG_INFO("heartbeat entirely disabled for compliance\n"); mhl_tx_modify_reg(hw_context, REG_DISC_CTRL1, BIT_DISC_CTRL1_HB_ONLY, 0x00); mhl_tx_write_reg(hw_context, REG_MSC_HEARTBEAT_CONTROL, 0x27); break; } } #endif static void disable_heartbeat(struct drv_hw_context *hw_context) { /* Disable MSC heartbeat */ mhl_tx_write_reg(hw_context, REG_MSC_HEARTBEAT_CONTROL, 0x27); } /* For MHL 3 the auto_zone bits must be cleared. */ static void clear_auto_zone_for_mhl_3(struct drv_hw_context *hw_context) { /* Clear auto zone */ mhl_tx_write_reg(hw_context, REG_TX_ZONE_CTL1, 0x0); /* Program PLL for 1X and clock from HSIC */ mhl_tx_write_reg(hw_context, REG_MHL_PLL_CTL0, (VAL_MHL_PLL_CTL0_HDMI_CLK_RATIO_1X | BIT_MHL_PLL_CTL0_CRYSTAL_CLK_SEL | BIT_MHL_PLL_CTL0_ZONE_MASK_OE)); } /* For MHL 1/2 we should use auto_zone. */ static void set_auto_zone_for_mhl_1_2(struct drv_hw_context *hw_context) { /* Enable AUTO ZONE for MHL1/2 */ mhl_tx_write_reg(hw_context, REG_TX_ZONE_CTL1, VAL_TX_ZONE_CTL1_TX_ZONE_CTRL_MODE); /* Program PLL for 1X and clock from HDMI */ mhl_tx_write_reg(hw_context, REG_MHL_PLL_CTL0, (VAL_MHL_PLL_CTL0_HDMI_CLK_RATIO_1X | BIT_MHL_PLL_CTL0_ZONE_MASK_OE)); } int si_mhl_tx_drv_set_tdm_slot_allocation(struct drv_hw_context *hw_context, uint8_t *vc_slot_counts, bool program) { int status = -EINVAL; uint16_t slot_total = 0; uint8_t idx; /* To the extent we can sanity check the slot allocation request */ if (vc_slot_counts[VC_CBUS1] != 1) goto done; for (idx = 0; idx < VC_MAX; idx++) { slot_total += vc_slot_counts[idx]; if (vc_slot_counts[idx] == 0) goto done; } switch (hw_context->cbus_mode) { case CM_eCBUS_S: case CM_eCBUS_S_AV_BIST: if (slot_total != 25) goto done; break; case CM_eCBUS_D: case CM_eCBUS_D_AV_BIST: if (slot_total != 200) goto done; break; default: goto done; } status = 0; if (program) { mhl_tx_write_reg(hw_context, REG_TTXSPINUMS, vc_slot_counts[VC_E_MSC]); mhl_tx_write_reg(hw_context, REG_TTXHSICNUMS, vc_slot_counts[VC_T_CBUS]); mhl_tx_write_reg(hw_context, REG_TRXSPINUMS, vc_slot_counts[VC_E_MSC]); mhl_tx_write_reg(hw_context, REG_TRXHSICNUMS, vc_slot_counts[VC_T_CBUS]); memcpy(hw_context->tdm_virt_chan_slot_counts, vc_slot_counts, sizeof(hw_context->tdm_virt_chan_slot_counts)); } done: return status; } static int block_input_buffer_available(struct drv_hw_context *hw_context) { uint16_t head, tail; head = hw_context->block_protocol.head; tail = hw_context->block_protocol.tail; if (head == (tail + 1)) { /* full case */ return 0; } return 1; } static int alloc_block_input_buffer(struct drv_hw_context *hw_context, uint8_t **pbuffer) { uint16_t head, tail; int index; head = hw_context->block_protocol.head; tail = hw_context->block_protocol.tail; if (!block_input_buffer_available(hw_context)) { /* full case */ return -1; } index = tail; if (++tail >= NUM_BLOCK_INPUT_BUFFERS) tail = 0; hw_context->block_protocol.tail = tail; *pbuffer = &hw_context->block_protocol.input_buffers[index][0]; return index; } static void set_block_input_buffer_length(struct drv_hw_context *hw_context, int block, int length) { if ((block < 0) || (block >= NUM_BLOCK_INPUT_BUFFERS)) return; hw_context->block_protocol.input_buffer_lengths[block] = length; } /* call this from mhl_supp.c to during processing of * DRV_INTR_FLAG_EMSC_INCOMING */ int si_mhl_tx_drv_peek_block_input_buffer(struct mhl_dev_context *dev_context, uint8_t **buffer, int *length) { struct drv_hw_context *hw_context = (struct drv_hw_context *)&dev_context->drv_context; uint16_t head, tail, index; head = hw_context->block_protocol.head; tail = hw_context->block_protocol.tail; if (head == tail) return -1; index = head; *buffer = &hw_context->block_protocol.input_buffers[index][0]; *length = hw_context->block_protocol.input_buffer_lengths[index]; return 0; } /* Call this from mhl_supp.c during processing of * DRV_INTR_FLAG_EMSC_INCOMING */ void si_mhl_tx_drv_free_block_input_buffer(struct mhl_dev_context *dev_context) { struct drv_hw_context *hw_context = (struct drv_hw_context *)&dev_context->drv_context; uint16_t head; head = hw_context->block_protocol.head; if (++head >= NUM_BLOCK_INPUT_BUFFERS) head = 0; hw_context->block_protocol.head = head; } #ifdef DUMP_ALL_REGS void dump_all_registers(struct drv_hw_context *hw_context) { uint8_t pages[] = { SA_TX_PAGE_0, SA_TX_PAGE_1, SA_TX_PAGE_2, SA_TX_PAGE_3, SA_TX_PAGE_4, SA_TX_CBUS, SA_TX_PAGE_6, SA_TX_PAGE_7, SA_TX_PAGE_8 }; int i; for (i = 0; i < sizeof(pages); ++i) { int j; for (j = 0; j < 256; ++j) { int dummy; dummy = mhl_tx_read_reg(hw_context, pages[i], j); } } } #endif static int get_emsc_fifo_count(struct drv_hw_context *hw_context, uint16_t *fifo_count) { int status; uint8_t rfifo_size[2]; uint16_t count; status = mhl_tx_read_reg_block(hw_context, REG_EMSCRFIFOBCNTL, sizeof(rfifo_size), rfifo_size); if (status < 0) { MHL_TX_DBG_ERR("%sSPI Read error: %d%s\n", ANSI_ESC_RED_TEXT, status, ANSI_ESC_RESET_TEXT); return status; } count = ((uint16_t)rfifo_size[1] << 8) | (uint16_t)rfifo_size[0]; if (count > LOCAL_BLK_RCV_BUFFER_SIZE) { MHL_TX_DBG_ERR("%sFIFO count too large (%d)%s\n", ANSI_ESC_RED_TEXT, count, ANSI_ESC_RESET_TEXT); return -EINVAL; } *fifo_count = count; return 0; } #define EMSC_HEADER_SIZE (1 + STD_TRANSPORT_HDR_SIZE) static int mhl3_block_isr(struct drv_hw_context *hw_context, uint8_t int_status) { int unload_cnt, remaining, avail; int block_index, status; uint8_t emsc_int, req_size; uint16_t data_len; uint8_t *pbit_bucket; bool payload_encountered = false; /* If eMSC not in normal mode, all bets are off. */ status = mhl_tx_read_reg(hw_context, REG_SPIBURSTSTAT); if (0 == (BIT_SPIBURSTSTAT_EMSC_NORMAL_MODE & status)) return 0; if (BIT_EMSCINTR_EMSC_RFIFO_READ_ERR & int_status) { MHL_TX_DBG_ERR("%seMSC read error! status:%x%s\n", ANSI_ESC_RED_TEXT, int_status, ANSI_ESC_RESET_TEXT); mhl_tx_clear_emsc_read_err(hw_context); } if ((BIT_EMSCINTR_SPI_DVLD & int_status) == 0) return 0; do { uint8_t *buffer = NULL; uint8_t header[STD_TRANSPORT_HDR_SIZE]; emsc_int = mhl_tx_read_reg(hw_context, REG_EMSCINTR); if (emsc_int & BIT_EMSCINTR_EMSC_RFIFO_READ_ERR) { MHL_TX_DBG_WARN("%seMSC read error! status:%x%s\n", ANSI_ESC_RED_TEXT, emsc_int, ANSI_ESC_RESET_TEXT); mhl_tx_clear_emsc_read_err(hw_context); } if ((emsc_int & BIT_EMSCINTR_SPI_DVLD) == 0) break; if (!block_input_buffer_available(hw_context)) break; status = get_emsc_fifo_count(hw_context, &data_len); if (status < 0) break; MHL_TX_DBG_INFO("EMSCINTR_SPI_DVLD, payload length: %d\n", data_len); /* * Has to be at least enough for a standard header. If not, * it should be by the next time this interrupt is handled. */ if (data_len < EMSC_HEADER_SIZE) break; /* TODO: Possible bug handled here */ if (data_len == LOCAL_BLK_RCV_BUFFER_SIZE) goto drain_rfifo; /* * Get the request size. Even though the RFIFO size (data_len) * is big enough, the req_size may be shorter than the STD * header size, so we only get the one byte. */ if (use_spi) { mhl_tx_read_spi_emsc(hw_context, 1, &req_size); } else { mhl_tx_read_reg_block(hw_context, REG_EMSC_RCV_READ_PORT, 1, &req_size); } data_len--; /* Get the STD header if enough data, and go from there. */ if (req_size < 1) { MHL_TX_DBG_ERR("%sHeader Error: Command size less than " "STD Transaction Header Size: %d%s\n", ANSI_ESC_RED_TEXT, req_size, ANSI_ESC_RESET_TEXT); goto done_emsc; } if (use_spi) { mhl_tx_read_spi_emsc(hw_context, STD_TRANSPORT_HDR_SIZE, header); } else { mhl_tx_read_reg_block(hw_context, REG_EMSC_RCV_READ_PORT, STD_TRANSPORT_HDR_SIZE, header); } req_size--; /* Read two bytes, so correct count. */ data_len -= STD_TRANSPORT_HDR_SIZE; unload_cnt = header[0]; remaining = header[1]; /* Validate STD header */ if (req_size < remaining) { MHL_TX_DBG_ERR("%sHeader Error: " "RFIFO_LEN: %d REQ_LEN: %d REMAINING: %d%s\n", ANSI_ESC_YELLOW_TEXT, data_len, req_size, remaining, ANSI_ESC_RESET_TEXT); req_size = (req_size < data_len) ? req_size : data_len; goto drain_req; } if ((unload_cnt + remaining) == 0) { MHL_TX_DBG_ERR("%sHeader Error: Unload and Remaining " "counts are 0!%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); goto drain_req; } avail = hw_context->block_protocol.peer_blk_rx_buf_avail; if ((avail + unload_cnt) > hw_context->block_protocol.peer_blk_rx_buf_max) { MHL_TX_DBG_ERR( "%seMSC Header Error: unload count too large:" "%d + %d > %d%s\n", ANSI_ESC_YELLOW_TEXT, unload_cnt, avail, hw_context->block_protocol.peer_blk_rx_buf_max, ANSI_ESC_RESET_TEXT); avail = hw_context->block_protocol.peer_blk_rx_buf_max; } else { avail += unload_cnt; } hw_context->block_protocol.peer_blk_rx_buf_avail = avail; /* * FIFO data count MUST be at LEAST enough * for the current transport header remaining byte count. */ if (data_len < remaining) { MHL_TX_DBG_WARN("eMSC FIFO data count < remaining " "byte count: %d < %d\n", data_len, remaining); msleep(RFIFO_FILL_DELAY); status = get_emsc_fifo_count(hw_context, &data_len); if (status < 0) break; if (data_len < remaining) { status = -EINVAL; break; } } if (remaining > 0) { payload_encountered = true; block_index = alloc_block_input_buffer(hw_context, &buffer); if (use_spi) { mhl_tx_read_spi_emsc(hw_context, remaining, buffer); } else { mhl_tx_read_reg_block(hw_context, REG_EMSC_RCV_READ_PORT, remaining, buffer); } set_block_input_buffer_length(hw_context, block_index, remaining); } hw_context->block_protocol.received_byte_count += (remaining + STD_TRANSPORT_HDR_SIZE); data_len -= remaining; } while (data_len > 0); if (payload_encountered) hw_context->intr_info->flags |= DRV_INTR_EMSC_INCOMING; goto done_emsc; drain_req: if (req_size == 0) goto done_emsc; data_len = req_size; drain_rfifo: MHL_TX_DBG_ERR("Draining %d bytes from RFIFO\n", data_len); pbit_bucket = kmalloc(data_len, GFP_KERNEL); if (!pbit_bucket) { MHL_TX_DBG_ERR("Failed to allocate %d bytes for pbit bucket\n", data_len); goto done_emsc; } if (use_spi) { mhl_tx_read_spi_emsc(hw_context, data_len, pbit_bucket); } else { mhl_tx_read_reg_block(hw_context, REG_EMSC_RCV_READ_PORT, data_len, pbit_bucket); } get_emsc_fifo_count(hw_context, &data_len); MHL_TX_DBG_INFO("New RFIFO length: %d\n", data_len); kfree(pbit_bucket); done_emsc: return 0; } static int coc_isr(struct drv_hw_context *hw_context, uint8_t coc_int_status) { int ret_val = 0; struct mhl_dev_context *dev_context; dev_context = container_of((void *)hw_context, struct mhl_dev_context, drv_context); if (BIT_COC_PLL_LOCK_STATUS_CHANGE & coc_int_status) MHL_TX_DBG_INFO("COC PLL lock status change\n"); if (si_mhl_tx_drv_connection_is_mhl3(dev_context)) { if (BIT_COC_CALIBRATION_DONE & coc_int_status) { int calibration_stat; uint8_t calibrated_value; MHL_TX_DBG_INFO("Calibration done\n"); calibration_stat = mhl_tx_read_reg(hw_context, REG_COC_STAT_0); calibration_stat &= BITS_COC_STAT_0_CALIBRATION_MASK; calibrated_value = BITS_COC_STAT_0_PLL_LOCKED | BITS_COC_STAT_0_CALIBRATION_STATE_2; if (calibrated_value == calibration_stat) { /* disable timeout */ mhl_tx_write_reg(hw_context, REG_COC_CTLB, 0x00); MHL_TX_DBG_ERR("CoC in calibrated state\n"); switch (hw_context->cbus_mode) { case CM_TRANSITIONAL_TO_eCBUS_S: enable_intr(hw_context, INTR_TDM, BIT_TDM_INTR_SYNC_DATA_MASK | BIT_TDM_INTR_SYNC_WAIT_MASK); hw_context->cbus_mode = CM_TRANSITIONAL_TO_eCBUS_S_CALIBRATED; si_set_cbus_mode_leds( hw_context->cbus_mode); break; case CM_TRANSITIONAL_TO_eCBUS_S_BIST: si_mhl_tx_set_bist_timer(dev_context); if (!(BIST_TRIGGER_ECBUS_TX_RX_MASK & dev_context->bist_trigger_info)) enable_intr(hw_context, INTR_TDM, BIT_TDM_INTR_SYNC_DATA_MASK | BIT_TDM_INTR_SYNC_WAIT_MASK); hw_context->cbus_mode = CM_TRANSITIONAL_TO_eCBUS_S_CAL_BIST; si_set_cbus_mode_leds( hw_context->cbus_mode); break; default: ; } hw_context->intr_info->flags |= DRV_INTR_COC_CAL; } else { MHL_TX_DBG_ERR("calibration state: 0x%02X\n", calibration_stat); } } } return ret_val; } static int tdm_isr(struct drv_hw_context *hw_context, uint8_t intr_status) { int ret_val = 0; uint8_t tdm_status; struct mhl_dev_context *dev_context; dev_context = container_of((void *)hw_context, struct mhl_dev_context, drv_context); if (si_mhl_tx_drv_connection_is_mhl3(dev_context)) { if (BIT_TDM_INTR_SYNC_DATA & intr_status) { MHL_TX_DBG_INFO("TDM in SYNC_DATA state.\n"); tdm_status = mhl_tx_read_reg(hw_context, REG_TRXSTA2); if ((tdm_status & MSK_TDM_SYNCHRONIZED) == VAL_TDM_SYNCHRONIZED) { MHL_TX_DBG_ERR("TDM is synchronized %s\n", si_mhl_tx_drv_get_cbus_mode_str( hw_context->cbus_mode)); if (hw_context->cbus_mode < CM_eCBUS_S) { hw_context->intr_info->flags |= DRV_INTR_TDM_SYNC; hw_context->block_protocol. received_byte_count = 0; si_mhl_tx_send_blk_rcv_buf_info( dev_context); #ifdef EARLY_HSIC /* * todo hsic_init configures the * transmitter for USB host mode. So * really this call should be deferred * until the driver has negotiated with * the sink to take over the host role. * The call is placed here for test. */ hsic_init(hw_context); #endif } switch (hw_context->cbus_mode) { case CM_TRANSITIONAL_TO_eCBUS_S_CAL_BIST: hw_context-> cbus_mode = CM_eCBUS_S_AV_BIST; si_set_cbus_mode_leds( hw_context->cbus_mode); break; case CM_TRANSITIONAL_TO_eCBUS_S_CALIBRATED: hw_context->cbus_mode = CM_eCBUS_S; si_set_cbus_mode_leds( hw_context->cbus_mode); break; case CM_eCBUS_S: case CM_eCBUS_S_AV_BIST: /* do nothing in this case */ break; default: MHL_TX_DBG_ERR( "%sunexpected CBUS mode %s: %s\n", ANSI_ESC_RED_TEXT, si_mhl_tx_drv_get_cbus_mode_str( hw_context->cbus_mode), ANSI_ESC_RESET_TEXT) } } else { switch (hw_context->cbus_mode) { case CM_eCBUS_S_BIST: MHL_TX_DBG_ERR("TDM not " "synchronized," " NOT retrying\n"); break; case CM_TRANSITIONAL_TO_eCBUS_S_CAL_BIST: if (BIST_TRIGGER_ECBUS_TX_RX_MASK & dev_context->bist_trigger_info) { MHL_TX_DBG_ERR("TDM not " "synchronized," " NOT retrying\n"); break; } case CM_TRANSITIONAL_TO_eCBUS_S_CALIBRATED: MHL_TX_DBG_ERR("TDM not synchronized," " retrying\n"); mhl_tx_write_reg(hw_context, REG_MHL_PLL_CTL2, 0x00); mhl_tx_write_reg(hw_context, REG_MHL_PLL_CTL2, 0x80); break; default: ; } } } if (BIT_TDM_INTR_SYNC_WAIT & intr_status) MHL_TX_DBG_ERR("TDM in SYNC_WAIT state.\n"); } return ret_val; } static int int_link_trn_isr(struct drv_hw_context *hw_context, uint8_t intr_status) { if (IN_MHL3_MODE(hw_context)) { if (BIT_EMSCINTR1_EMSC_TRAINING_COMMA_ERR & intr_status) { MHL_TX_DBG_ERR("%sTraining comma " "error COC_STAT_0:0x%02x%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); } else { MHL_TX_DBG_ERR("%sCOC_STAT_0:0x%02x%s\n", ANSI_ESC_GREEN_TEXT, mhl_tx_read_reg(hw_context, REG_COC_STAT_0), ANSI_ESC_RESET_TEXT); } return 0; } else { return 0; } } static char *cbus_mode_strings[NUM_CM_MODES] = { "CM_NO_CONNECTION", "CM_NO_CONNECTION_BIST_SETUP", "CM_NO_CONNECTION_BIST_STAT", "CM_oCBUS_PEER_VERSION_PENDING", "CM_oCBUS_PEER_VERSION_PENDING_BIST_SETUP", "CM_oCBUS_PEER_VERSION_PENDING_BIST_STAT", "CM_oCBUS_PEER_IS_MHL1_2", "CM_oCBUS_PEER_IS_MHL3_BIST_SETUP", "CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_SENT", "CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY", "CM_oCBUS_PEER_IS_MHL3_BIST_STAT", "CM_oCBUS_PEER_IS_MHL3", "CM_TRANSITIONAL_TO_eCBUS_S_BIST", "CM_TRANSITIONAL_TO_eCBUS_D_BIST", "CM_TRANSITIONAL_TO_eCBUS_S", "CM_TRANSITIONAL_TO_eCBUS_D", "CM_TRANSITIONAL_TO_eCBUS_S_CAL_BIST", "CM_TRANSITIONAL_TO_eCBUS_D_CAL_BIST", "CM_TRANSITIONAL_TO_eCBUS_S_CALIBRATED", "CM_TRANSITIONAL_TO_eCBUS_D_CALIBRATED", "CM_eCBUS_S_BIST", "CM_eCBUS_D_BIST", "CM_BIST_DONE_PENDING_DISCONNECT", "CM_eCBUS_S", "CM_eCBUS_D", "CM_eCBUS_S_AV_BIST", "CM_eCBUS_D_AV_BIST", }; void si_set_cbus_mode_leds_impl(enum cbus_mode_e mode_sel, const char *func_name, int line_num) { if (mode_sel < NUM_CM_MODES) { MHL_TX_PROXY_DBG_PRINT(DBG_MSG_LEVEL_WARN, func_name, line_num, "CBUS MODE: %s%s%s\n", ANSI_ESC_CYAN_TEXT, cbus_mode_strings[mode_sel], ANSI_ESC_RESET_TEXT) } switch (mode_sel) { case CM_NO_CONNECTION: case CM_NO_CONNECTION_BIST_SETUP: case CM_NO_CONNECTION_BIST_STAT: set_pin(X02_USB_LED15_AMBER, 1); set_pin(X02_USB_LED15_GREEN, 1); set_pin(LED_USB_MODE, 0); break; case CM_oCBUS_PEER_VERSION_PENDING: case CM_oCBUS_PEER_VERSION_PENDING_BIST_SETUP: case CM_oCBUS_PEER_VERSION_PENDING_BIST_STAT: set_pin(X02_USB_LED15_AMBER, 0); set_pin(X02_USB_LED15_GREEN, 0); set_pin(LED_USB_MODE, 1); break; case CM_oCBUS_PEER_IS_MHL1_2: set_pin(X02_USB_LED15_AMBER, 0); set_pin(X02_USB_LED15_GREEN, 1); set_pin(LED_USB_MODE, 1); break; case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP: case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_SENT: case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY: case CM_oCBUS_PEER_IS_MHL3_BIST_STAT: case CM_oCBUS_PEER_IS_MHL3: case CM_TRANSITIONAL_TO_eCBUS_S_BIST: case CM_TRANSITIONAL_TO_eCBUS_S: case CM_TRANSITIONAL_TO_eCBUS_S_CAL_BIST: case CM_TRANSITIONAL_TO_eCBUS_S_CALIBRATED: case CM_TRANSITIONAL_TO_eCBUS_D: case CM_TRANSITIONAL_TO_eCBUS_D_CALIBRATED: case CM_eCBUS_S_BIST: case CM_eCBUS_D_BIST: case CM_BIST_DONE_PENDING_DISCONNECT: case CM_eCBUS_S: case CM_eCBUS_D: case CM_eCBUS_S_AV_BIST: case CM_eCBUS_D_AV_BIST: set_pin(X02_USB_LED15_AMBER, 1); set_pin(X02_USB_LED15_GREEN, 0); set_pin(LED_USB_MODE, 1); break; default: MHL_TX_DBG_INFO("CBUS MODE: %02X\n", mode_sel); break; } } static void leave_ecbus_s_mode(struct drv_hw_context *hw_context) { mhl_tx_write_reg(hw_context, REG_COC_CTL0, 0x40); mhl_tx_write_reg(hw_context, REG_MHL_COC_CTL1, 0x07); /* bugzilla 33456 */ mhl_tx_write_reg(hw_context, REG_COC_CTL7, 0x06); } static void force_cbus_high_z(struct drv_hw_context *hw_context) { int disc_ctrl5; disc_ctrl5 = mhl_tx_read_reg(hw_context, REG_DISC_CTRL5); mhl_tx_write_reg(hw_context, REG_DISC_CTRL5, disc_ctrl5 & ~MSK_DISC_CTRL5_CBUSMHL_PUP_SEL); msleep(50); mhl_tx_write_reg(hw_context, REG_DISC_CTRL5, disc_ctrl5); } static void ecbus_s_bist_prep(struct drv_hw_context *hw_context, struct bist_setup_info *test_info) { uint8_t start_cmd = 0x01; memset(hw_context->prev_bist_coc_status, 0, ARRAY_SIZE(hw_context->prev_bist_coc_status)); /* Clear eCBUS-S BIST error counter and status and remain in default CoC working mode. */ mhl_tx_write_reg(hw_context, REG_COC_CTL7, 0x66); /* Clear eCBUS-S BIST error counter and status */ mhl_tx_write_reg(hw_context, REG_COC_CTL7, 0x60); switch (test_info->e_cbus_pattern) { case BIST_ECBUS_PATTERN_UNSPECIFIED: case BIST_ECBUS_PATTERN_PRBS: start_cmd = 0x01; break; case BIST_ECBUS_PATTERN_FIXED_8: mhl_tx_write_reg(hw_context, REG_COC_CTL9, (uint8_t) test_info->e_cbus_fixed_pat); mhl_tx_write_reg(hw_context, REG_COC_CTLA, (uint8_t) test_info->e_cbus_fixed_pat); start_cmd = 0x02; break; case BIST_ECBUS_PATTERN_FIXED_10: /* Fixed10 is not supported by eCBUS-S * Verify this command option is properly refused. * For now just fall through to default. */ default: MHL_TX_DBG_ERR("Unrecognized test pattern detected!\n"); } mhl_tx_write_reg(hw_context, REG_COC_CTL7, 0x60 | start_cmd); mhl_tx_write_reg(hw_context, REG_TTXNUMB, 0x05); mhl_tx_modify_reg(hw_context, REG_COC_CTL0, BIT_COC_CTL0_COC_CONTROL0_2, 0x00); } static void ecbus_d_bist_prep(struct drv_hw_context *hw_context, struct bist_setup_info *test_info) { uint8_t start_cmd = 0x03; /* Clear eCBUS-D BIST error counter and status */ mhl_tx_write_reg(hw_context, REG_DOC_CTL7, 0x60); switch (test_info->e_cbus_pattern) { case BIST_ECBUS_PATTERN_UNSPECIFIED: case BIST_ECBUS_PATTERN_PRBS: start_cmd = 0x01; break; case BIST_ECBUS_PATTERN_FIXED_8: mhl_tx_write_reg(hw_context, REG_DOC_CTL9, (uint8_t) test_info->e_cbus_fixed_pat); mhl_tx_write_reg(hw_context, REG_DOC_CTLA, (uint8_t) test_info->e_cbus_fixed_pat); start_cmd = 0x02; break; case BIST_ECBUS_PATTERN_FIXED_10: mhl_tx_write_reg(hw_context, REG_DOC_CTL9, (uint8_t) test_info->e_cbus_fixed_pat); mhl_tx_write_reg(hw_context, REG_DOC_CTL8, ((uint8_t) (test_info->e_cbus_fixed_pat >> 8)) & 0x03); start_cmd = (uint8_t) ((test_info-> e_cbus_fixed_pat & 0x0300) >> 6); if (test_info->e_cbus_fixed_pat & 0x8000) { mhl_tx_write_reg(hw_context, REG_DOC_CTLA, ~(uint8_t) test_info->e_cbus_fixed_pat); mhl_tx_modify_reg(hw_context, REG_DOC_CTL8, 0x0C, ~start_cmd); } else { mhl_tx_write_reg(hw_context, REG_DOC_CTLA, (uint8_t) test_info->e_cbus_fixed_pat); mhl_tx_modify_reg(hw_context, REG_DOC_CTL8, 0x0C, start_cmd); } start_cmd = 0x03; break; default: MHL_TX_DBG_ERR("Unrecognized test pattern detected!\n"); start_cmd = 0x03; } mhl_tx_write_reg(hw_context, REG_DOC_CTL7, start_cmd); } /* * Configure the CBUS link for the requested operating mode. * Returns: * -1 = Requested mode not available. * 0 = Requested mode change is complete. * 1 = Requested mode change is in process. In this case completion * is indicated by the interrupt flag DRV_INTR_FLAG_TDM_SYNC. */ int si_mhl_tx_drv_switch_cbus_mode(struct drv_hw_context *hw_context, enum cbus_mode_e mode_sel) { int status = 1; uint8_t slot_total; int coc_stat_0; int i = 0; struct mhl_dev_context *dev_context; dev_context = container_of((void *)hw_context, struct mhl_dev_context, drv_context); MHL_TX_DBG_WARN("Switch cbus_mode from %x to %x\n", hw_context->cbus_mode, mode_sel); if (hw_context->cbus_mode < CM_oCBUS_PEER_VERSION_PENDING) return -1; switch (mode_sel) { case CM_NO_CONNECTION: case CM_NO_CONNECTION_BIST_SETUP: case CM_NO_CONNECTION_BIST_STAT: switch (hw_context->cbus_mode) { case CM_NO_CONNECTION: case CM_NO_CONNECTION_BIST_SETUP: case CM_NO_CONNECTION_BIST_STAT: /* already disconnected */ break; case CM_oCBUS_PEER_VERSION_PENDING: case CM_oCBUS_PEER_VERSION_PENDING_BIST_SETUP: case CM_oCBUS_PEER_VERSION_PENDING_BIST_STAT: case CM_oCBUS_PEER_IS_MHL1_2: case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP: case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_SENT: case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY: case CM_oCBUS_PEER_IS_MHL3_BIST_STAT: case CM_oCBUS_PEER_IS_MHL3: force_cbus_high_z(hw_context); break; case CM_eCBUS_D_BIST: case CM_TRANSITIONAL_TO_eCBUS_D_BIST: case CM_TRANSITIONAL_TO_eCBUS_D: case CM_TRANSITIONAL_TO_eCBUS_D_CAL_BIST: case CM_TRANSITIONAL_TO_eCBUS_D_CALIBRATED: case CM_eCBUS_D: case CM_eCBUS_D_AV_BIST: /* todo: either implement or remove eCBUS-D support */ break; case CM_eCBUS_S_BIST: case CM_eCBUS_S_AV_BIST: case CM_TRANSITIONAL_TO_eCBUS_S_BIST: case CM_TRANSITIONAL_TO_eCBUS_S_CAL_BIST: hw_context->cbus_mode = CM_BIST_DONE_PENDING_DISCONNECT; si_set_cbus_mode_leds(hw_context->cbus_mode); case CM_TRANSITIONAL_TO_eCBUS_S: case CM_TRANSITIONAL_TO_eCBUS_S_CALIBRATED: leave_ecbus_s_mode(hw_context); force_cbus_high_z(hw_context); /* let disconnect_mhl handle this transition */ mode_sel = hw_context->cbus_mode; break; case CM_eCBUS_S: leave_ecbus_s_mode(hw_context); force_cbus_high_z(hw_context); mode_sel = CM_NO_CONNECTION; break; default: MHL_TX_DBG_ERR("%sinvalid cbus mode%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); } hw_context->cbus_mode = mode_sel; si_set_cbus_mode_leds(hw_context->cbus_mode); break; case CM_oCBUS_PEER_IS_MHL1_2: MHL_TX_DBG_ERR("Switch to MHL1/2 oCBUS mode\n"); #define BIT_CBUS_MSC_COMPATIBILITY_CONTROL_ENABLE_XDEVCAP 0x80 mhl_tx_write_reg(hw_context, REG_CBUS_MSC_COMPATIBILITY_CONTROL, 0x02); mhl_tx_write_reg(hw_context, REG_M3_CTRL, VAL_M3_CTRL_MHL1_2_VALUE); /* * disable BIT_DPD_PWRON_HSIC */ mhl_tx_write_reg(hw_context, REG_DPD, BIT_DPD_PWRON_PLL | BIT_DPD_PDNTX12 | BIT_DPD_OSC_EN); enable_intr(hw_context, INTR_COC, 0); set_auto_zone_for_mhl_1_2(hw_context); mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL0, 0xBC); mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL1, 0xFE); mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL3, 0x48); mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL5, 0x39); mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL2, 0x2A); mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL6, 0x2A); mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL7, 0x08); hw_context->cbus_mode = CM_oCBUS_PEER_IS_MHL1_2; si_set_cbus_mode_leds(hw_context->cbus_mode); break; case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP: case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_SENT: case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY: case CM_oCBUS_PEER_IS_MHL3_BIST_STAT: case CM_oCBUS_PEER_IS_MHL3: switch (hw_context->cbus_mode) { case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP: case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_SENT: case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY: case CM_oCBUS_PEER_IS_MHL3_BIST_STAT: case CM_oCBUS_PEER_IS_MHL3: break; case CM_TRANSITIONAL_TO_eCBUS_S_BIST: case CM_TRANSITIONAL_TO_eCBUS_S_CAL_BIST: case CM_eCBUS_S_BIST: case CM_eCBUS_S_AV_BIST: case CM_BIST_DONE_PENDING_DISCONNECT: hw_context->cbus_mode = CM_BIST_DONE_PENDING_DISCONNECT; case CM_TRANSITIONAL_TO_eCBUS_S: case CM_TRANSITIONAL_TO_eCBUS_S_CALIBRATED: /* let disconnect_mhl handle this transition */ mode_sel = hw_context->cbus_mode; default: leave_ecbus_s_mode(hw_context); /* when a graceful transition to oCBUS mode can be tested, remove the following line */ force_cbus_high_z(hw_context); } hw_context->cbus_mode = mode_sel; si_set_cbus_mode_leds(hw_context->cbus_mode); break; case CM_eCBUS_S_BIST: case CM_eCBUS_S: #ifdef CoC_FSM_MONITORING #ifndef BIST_MONITORING mhl_tx_modify_reg(hw_context, REG_GPIO_CTRL1, BIT_CTRL1_GPIO_I_6, BIT_CTRL1_GPIO_I_6); #endif #endif si_mhl_tx_initialize_block_transport(dev_context); si_mhl_tx_drv_enable_emsc_block(hw_context); MHL_TX_DBG_WARN("hpd status: 0x%02x\n", mhl_tx_read_reg(hw_context, REG_CBUS_STATUS)); switch (mode_sel) { case CM_eCBUS_S_BIST: hw_context->cbus_mode = CM_TRANSITIONAL_TO_eCBUS_S_BIST; si_set_cbus_mode_leds(hw_context->cbus_mode); if (BIST_TRIGGER_ECBUS_TX_RX_MASK & dev_context->bist_trigger_info) ecbus_s_bist_prep(hw_context, &dev_context->bist_setup); break; case CM_eCBUS_S: hw_context->cbus_mode = CM_TRANSITIONAL_TO_eCBUS_S; break; default: ; } mhl_tx_write_reg(hw_context, REG_TTXSPINUMS, hw_context-> tdm_virt_chan_slot_counts[VC_E_MSC]); mhl_tx_write_reg(hw_context, REG_TRXSPINUMS, hw_context-> tdm_virt_chan_slot_counts[VC_E_MSC]); slot_total = hw_context->tdm_virt_chan_slot_counts[VC_E_MSC]; mhl_tx_write_reg(hw_context, REG_TTXHSICNUMS, hw_context-> tdm_virt_chan_slot_counts[VC_T_CBUS]); mhl_tx_write_reg(hw_context, REG_TRXHSICNUMS, hw_context-> tdm_virt_chan_slot_counts[VC_T_CBUS]); slot_total += hw_context->tdm_virt_chan_slot_counts[VC_T_CBUS]; mhl_tx_write_reg(hw_context, REG_TTXTOTNUMS, 24); mhl_tx_write_reg(hw_context, REG_TRXTOTNUMS, 24); /* begin reset */ mhl_tx_write_reg(hw_context, REG_PWD_SRST, 0xA0); mhl_tx_write_reg(hw_context, REG_MHL_COC_CTL1, 0xBC); /* release sw-reset */ mhl_tx_write_reg(hw_context, REG_PWD_SRST, 0x20); /* Enable timeout */ mhl_tx_write_reg(hw_context, REG_COC_CTLB, 0x01); mhl_tx_write_reg(hw_context, REG_COC_CTL0, 0x5C); mhl_tx_write_reg(hw_context, REG_COC_CTL14, 0x03); mhl_tx_write_reg(hw_context, REG_COC_CTL15, 0x80); #ifdef CoC_FSM_MONITORING #ifndef BIST_MONITORING mhl_tx_modify_reg(hw_context, REG_GPIO_CTRL1, BIT_CTRL1_GPIO_I_7, BIT_CTRL1_GPIO_I_7); #endif #endif #define STATE_3_TRAP_LIMIT 6 #define TRAP_WAIT_SLEEP 1 for (i = 0; i < STATE_3_TRAP_LIMIT; ++i) { int temp; temp = mhl_tx_read_reg(hw_context, REG_EMSCINTR1); coc_stat_0 = mhl_tx_read_reg(hw_context, REG_COC_STAT_0); if (0x03 == (BITS_ES0_0_COC_STAT_0_FSM_STATE_MASK & coc_stat_0)) { #ifdef CoC_FSM_MONITORING #ifndef BIST_MONITORING mhl_tx_modify_reg(hw_context, REG_GPIO_CTRL1, BIT_CTRL1_GPIO_I_7, 0); #endif #endif msleep(20); #ifdef CoC_FSM_MONITORING #ifndef BIST_MONITORING mhl_tx_modify_reg(hw_context, REG_GPIO_CTRL1, BIT_CTRL1_GPIO_I_7, BIT_CTRL1_GPIO_I_7); #endif #endif break; } if (0x02 == (BITS_ES0_0_COC_STAT_0_FSM_STATE_MASK & coc_stat_0)) { break; } if (!(BITS_ES1_0_COC_STAT_0_PLL_LOCKED & coc_stat_0)) { MHL_TX_DBG_ERR( "%slost PLL_LOCK coc_stat_0:0x%02x%s\n", ANSI_ESC_RED_TEXT, coc_stat_0, ANSI_ESC_RESET_TEXT); i = STATE_3_TRAP_LIMIT; break; } msleep(TRAP_WAIT_SLEEP); } mhl_tx_write_reg(hw_context, REG_COC_CTL14, 0x00); mhl_tx_write_reg(hw_context, REG_COC_CTL15, 0x80); if (i < STATE_3_TRAP_LIMIT) { mhl_tx_write_reg(hw_context, REG_CBUS3_CNVT, 0x85); } else { MHL_TX_DBG_ERR("%stimed out waiting for trap%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); status = -1; disconnect_mhl(hw_context, true); switch_to_idle(hw_context, false); mode_sel = CM_NO_CONNECTION; } #ifdef CoC_FSM_MONITORING #ifndef BIST_MONITORING mhl_tx_modify_reg(hw_context, REG_GPIO_CTRL1, BIT_CTRL1_GPIO_I_7 | BIT_CTRL1_GPIO_I_6, 0); #endif #endif break; case CM_eCBUS_D_BIST: case CM_eCBUS_D: hw_context->cbus_mode = CM_TRANSITIONAL_TO_eCBUS_D; si_mhl_tx_initialize_block_transport(dev_context); si_mhl_tx_drv_enable_emsc_block(hw_context); switch (mode_sel) { case CM_eCBUS_D_BIST: hw_context->cbus_mode = CM_TRANSITIONAL_TO_eCBUS_D_BIST; si_set_cbus_mode_leds(hw_context->cbus_mode); if (BIST_TRIGGER_ECBUS_TX_RX_MASK & dev_context->bist_trigger_info) ecbus_d_bist_prep(hw_context, &dev_context->bist_setup); break; case CM_eCBUS_D: hw_context->cbus_mode = CM_TRANSITIONAL_TO_eCBUS_D; si_set_cbus_mode_leds(hw_context->cbus_mode); break; default: ; } mhl_tx_write_reg(hw_context, REG_TTXSPINUMS, hw_context-> tdm_virt_chan_slot_counts[VC_E_MSC]); mhl_tx_write_reg(hw_context, REG_TRXSPINUMS, hw_context-> tdm_virt_chan_slot_counts[VC_E_MSC]); slot_total = hw_context->tdm_virt_chan_slot_counts[VC_E_MSC]; mhl_tx_write_reg(hw_context, REG_TTXHSICNUMS, hw_context-> tdm_virt_chan_slot_counts[VC_T_CBUS]); mhl_tx_write_reg(hw_context, REG_TRXHSICNUMS, hw_context-> tdm_virt_chan_slot_counts[VC_T_CBUS]); slot_total += hw_context->tdm_virt_chan_slot_counts[VC_T_CBUS]; mhl_tx_write_reg(hw_context, REG_TTXTOTNUMS, 199); mhl_tx_write_reg(hw_context, REG_TRXTOTNUMS, 199); break; default: MHL_TX_DBG_ERR("Invalid or unsupported CBUS mode specified\n"); status = -EINVAL; break; } return status; } enum cbus_mode_e si_mhl_tx_drv_get_cbus_mode(struct mhl_dev_context *dev_context) { struct drv_hw_context *hw_context = (struct drv_hw_context *)&dev_context->drv_context; return hw_context->cbus_mode; } char *si_mhl_tx_drv_get_cbus_mode_str(enum cbus_mode_e cbus_mode) { if (cbus_mode >= NUM_CM_MODES) return "invalid cbus mode"; return cbus_mode_strings[cbus_mode]; } static void disable_gen2_write_burst_rcv(struct drv_hw_context *hw_context) { if (hw_context->gen2_write_burst_rcv) { /* disable Gen2 Write Burst to allow normal CBUS traffic */ mhl_tx_write_reg(hw_context, REG_MDT_XMIT_CONTROL, 0); mhl_tx_write_reg(hw_context, REG_MDT_RCV_CONTROL, 0); /* BIT_MDT_RCV_CONTROL_MDT_RCV_EN); */ /* TODO: Review with John */ /* BIT_MDT_RCV_CONTROL_MDT_DISABLE); */ MHL_TX_DBG_INFO("%sdisabled GEN2%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); hw_context->gen2_write_burst_rcv = false; } } static void disable_gen2_write_burst_xmit(struct drv_hw_context *hw_context) { if (hw_context->gen2_write_burst_xmit) { /* disable Gen2 Write Burst to allow normal CBUS traffic */ mhl_tx_write_reg(hw_context, REG_MDT_XMIT_CONTROL, 0); MHL_TX_DBG_INFO("%sdisabled GEN2%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); hw_context->gen2_write_burst_xmit = false; } } static void enable_gen2_write_burst_rcv(struct drv_hw_context *hw_context) { /* enable Gen2 Write Burst interrupt, MSC and EDID interrupts. */ if (!hw_context->gen2_write_burst_rcv) { /* 2 second timeout */ mhl_tx_write_reg(hw_context, REG_MDT_RCV_TIMEOUT, 100); mhl_tx_write_reg(hw_context, REG_MDT_RCV_CONTROL, BIT_MDT_RCV_CONTROL_MDT_RCV_EN | hw_context->delayed_hawb_enable_reg_val); MHL_TX_DBG_INFO("%senabled GEN2%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); hw_context->gen2_write_burst_rcv = true; } } static void enable_gen2_write_burst_xmit(struct drv_hw_context *hw_context) { /* enable Gen2 Write Burst interrupt, MSC and EDID interrupts. */ if (!hw_context->gen2_write_burst_xmit) { mhl_tx_write_reg(hw_context, REG_MDT_XMIT_CONTROL, BIT_MDT_XMIT_CONTROL_MDT_XMIT_EN | BIT_MDT_XMIT_CONTROL_MDT_XMIT_FIXED_BURST_LEN); MHL_TX_DBG_INFO("%senabled GEN2%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); hw_context->gen2_write_burst_xmit = true; } } #ifndef MANUAL_EDID_FETCH static void freeze_MHL_connect(struct drv_hw_context *hw_context) { mhl_tx_write_reg(hw_context, REG_DISC_STAT1, 0x08); mhl_tx_modify_reg(hw_context, REG_DISC_CTRL5, BIT_DISC_CTRL5_DSM_OVRIDE, BIT_DISC_CTRL5_DSM_OVRIDE); } static void unfreeze_MHL_connect(struct drv_hw_context *hw_context) { mhl_tx_modify_reg(hw_context, REG_DISC_CTRL5, BIT_DISC_CTRL5_DSM_OVRIDE, 0); } #endif void si_mhl_tx_drv_shut_down_HDCP2(struct drv_hw_context *hw_context) { int ddcm_status; int count = 0; /* Disable HDCP 2.2 */ enable_intr(hw_context, INTR_HDCP2, 0x00); /* Disable HDCP2 DDC polling */ hw_context->hdcp2_started = false; mhl_tx_write_reg(hw_context, REG_HDCP2X_POLL_CS, 0x71); while (0 <= (ddcm_status = mhl_tx_read_reg(hw_context, REG_HDCP2X_DDCM_STS))) { if (0 == (MSK_HDCP2X_DDCM_STS_HDCP2X_DDCM_CTL_CS_3_0 & ddcm_status)) { break; } if (++count > 256) break; MHL_TX_DBG_WARN("shutting down HDCP\n"); } /* disable encryption */ mhl_tx_write_reg(hw_context, REG_HDCP2X_CTRL_0, 0x82); MHL_TX_DBG_WARN("HDCP2 Off; Last HDCP2X_DDCM Status %02X;\n", ddcm_status); mhl_tx_modify_reg(hw_context, REG_M3_P0CTRL, BIT_M3_P0CTRL_MHL3_P0_HDCP_EN, 0x00); /* clear any leftover hdcp2 interrupts */ mhl_tx_write_reg(hw_context, g_intr_tbl[INTR_HDCP2].stat_addr, 0xff); } static bool issue_edid_read_request(struct drv_hw_context *hw_context, uint8_t block_number) { if (ok_to_proceed_with_ddc(hw_context)) { int ddc_status; ddc_status = mhl_tx_read_reg(hw_context, REG_DDC_STATUS); if (BIT_DDC_STATUS_DDC_BUS_LOW & ddc_status) { int lm_ddc; lm_ddc = mhl_tx_read_reg(hw_context, REG_LM_DDC); /* disable TPI mode */ mhl_tx_write_reg(hw_context, REG_LM_DDC, lm_ddc | VAL_LM_DDC_SW_TPI_EN_DISABLED); /* clear out the ddc bus low bit */ mhl_tx_write_reg(hw_context, REG_DDC_STATUS, ddc_status & ~BIT_DDC_STATUS_DDC_BUS_LOW); /* restore TPI mode state */ mhl_tx_write_reg(hw_context, REG_LM_DDC, lm_ddc); } MHL_TX_DBG_INFO("\n\tRequesting EDID block:%d\n" "\tcurrentEdidRequestBlock:%d\n" "\tedidFifoBlockNumber:%d" "ddc_status:0x%02x\n", block_number, hw_context->current_edid_req_blk, hw_context->edid_fifo_block_number, ddc_status); /* Setup auto increment and kick off read */ mhl_tx_write_reg(hw_context, REG_EDID_CTRL, VAL_EDID_CTRL_EDID_PRIME_VALID_DISABLE | VAL_EDID_CTRL_DEVCAP_SELECT_EDID | VAL_EDID_CTRL_EDID_FIFO_ADDR_AUTO_ENABLE | ((block_number & 0x01) << 2) | VAL_EDID_CTRL_EDID_MODE_EN_ENABLE); #ifndef MANUAL_EDID_FETCH #define SWWA_BZ30759 #endif #ifdef SWWA_BZ30759 freeze_MHL_connect(hw_context); #endif /* Setup which block to read */ if (0 == block_number) { /* Enable EDID interrupt */ enable_intr(hw_context, INTR_EDID, (BIT_INTR9_DEVCAP_DONE_MASK | BIT_INTR9_EDID_DONE_MASK | BIT_INTR9_EDID_ERROR)); mhl_tx_write_reg(hw_context, REG_TPI_CBUS_START, BIT_TPI_CBUS_START_GET_EDID_START_0); } else { uint8_t param = (1 << (block_number - 1)); MHL_TX_DBG_INFO("EDID HW Assist: Programming " "Reg %02X:%02X to %02X\n", REG_EDID_START_EXT, param); mhl_tx_write_reg(hw_context, REG_EDID_START_EXT, param); } return true; } else { MHL_TX_DBG_INFO("\n\tNo HPD for EDID block request:%d\n" "\tcurrentEdidRequestBlock:%d\n" "\tedidFifoBlockNumber:%d\n", block_number, hw_context->current_edid_req_blk, hw_context->edid_fifo_block_number); return false; } } /* * si_mhl_tx_drv_send_block */ void mhl_tx_drv_send_block(struct drv_hw_context *hw_context, struct block_req *req) { uint8_t count; count = req->sub_payload_size; MHL_TX_DBG_INFO(" req->sub_payload_size: %d req->count: %d\n", count, req->count); MHL_TX_DBG_WARN("total bytes to ack: %d\n", hw_context->block_protocol.received_byte_count); if (hw_context->block_protocol.received_byte_count >= 256) { /* * Can't represent numbers >= 256 in 8 bits, * so ack as much as possible */ req->payload->hdr_and_burst_id.tport_hdr.rx_unload_ack = 255; hw_context->block_protocol.received_byte_count -= 255; } else { req->payload->hdr_and_burst_id.tport_hdr.rx_unload_ack = (uint8_t) hw_context->block_protocol.received_byte_count; hw_context->block_protocol.received_byte_count = 0; } MHL_TX_DBG_WARN("rx_unload_ack: %d\n", req->payload->hdr_and_burst_id.tport_hdr.rx_unload_ack); /* dump_array(0, "eMSC BLOCK message", &req->payload->as_bytes[0], req->count); */ if (use_spi) { mhl_tx_write_block_spi_emsc(hw_context, req); } else { if (req->count < EMSC_PAYLOAD_LEN) mhl_tx_write_reg_block(hw_context, REG_EMSC_XMIT_WRITE_PORT, req->count, &req->payload->as_bytes[0]); } } /* pending hawb write burst status */ uint8_t si_mhl_tx_drv_get_pending_hawb_write_status(struct mhl_dev_context *dev_context) { struct drv_hw_context *hw_context = (struct drv_hw_context *)&dev_context->drv_context; return hw_context->hawb_write_pending; } /* si_mhl_tx_drv_hawb_xfifo_avail */ uint8_t si_mhl_tx_drv_hawb_xfifo_avail(struct mhl_dev_context *dev_context) { struct drv_hw_context *hw_context = (struct drv_hw_context *)&dev_context->drv_context; return MSK_MDT_XFIFO_STAT_MDT_XFIFO_LEVEL_AVAIL & mhl_tx_read_reg(hw_context, REG_MDT_XFIFO_STAT); } #ifdef MANUAL_EDID_FETCH static uint8_t fetch_edid_block(struct drv_hw_context *hw_context, uint8_t *buffer, uint8_t block_num, bool trigger_on_last) { int lm_ddc, ddc_cmd, ddc_status, ddc_address, ddc_limit, step, dout_cnt, intr3_status, cbus_status; cbus_status = ok_to_proceed_with_ddc(hw_context); lm_ddc = mhl_tx_read_reg(hw_context, REG_LM_DDC); ddc_cmd = mhl_tx_read_reg(hw_context, REG_DDC_CMD); ddc_cmd &= ~MSK_DDC_CMD_DDC_CMD; /* Disable EDID interrupt */ enable_intr(hw_context, INTR_EDID, 0); hw_context->hdcp2_started = false; /* disable auto edid function */ mhl_tx_write_reg(hw_context, REG_EDID_CTRL, VAL_EDID_CTRL_EDID_FIFO_ADDR_AUTO_ENABLE); /* Disable HDCP2 DDC polling */ mhl_tx_write_reg(hw_context, REG_HDCP2X_POLL_CS, 0x71); /* disable encryption */ mhl_tx_write_reg(hw_context, REG_HDCP2X_CTRL_0, 0x02); /* disable TPI mode */ mhl_tx_write_reg(hw_context, REG_LM_DDC, lm_ddc | VAL_LM_DDC_SW_TPI_EN_DISABLED); for (step = 0; step < 256; ++step) { ddc_status = mhl_tx_read_reg(hw_context, REG_DDC_STATUS); if (0 == (BIT_DDC_STATUS_DDC_I2C_IN_PROG & ddc_status)) break; mhl_tx_write_reg(hw_context, REG_DDC_STATUS, BIT_DDC_STATUS_DDC_FIFO_EMPTY); } /* set DDC slave address to EDID */ mhl_tx_write_reg(hw_context, REG_DDC_ADDR, 0xA0); step = 16; ddc_limit = (block_num << 7) + EDID_BLOCK_SIZE; for (ddc_address = block_num << 7; ddc_address < ddc_limit; ddc_address += step) { ddc_status = mhl_tx_read_reg(hw_context, REG_DDC_STATUS); mhl_tx_write_reg(hw_context, REG_DDC_CMD, ddc_cmd | VAL_DDC_CMD_DDC_CMD_ABORT); mhl_tx_write_reg(hw_context, REG_DDC_CMD, ddc_cmd | VAL_DDC_CMD_DDC_CMD_CLEAR_FIFO); mhl_tx_write_reg(hw_context, REG_DDC_STATUS, BIT_DDC_STATUS_DDC_FIFO_EMPTY); /* synchronize by making sure that any stale interrupt * is cleared */ intr3_status = mhl_tx_read_reg(hw_context, REG_INTR3); mhl_tx_write_reg(hw_context, REG_INTR3, intr3_status); mhl_tx_write_reg(hw_context, REG_DDC_SEGM, HIGH_BYTE_16(ddc_address)); mhl_tx_write_reg(hw_context, REG_DDC_OFFSET, LOW_BYTE_16(ddc_address)); mhl_tx_write_reg(hw_context, REG_DDC_DIN_CNT1, LOW_BYTE_16(step)); mhl_tx_write_reg(hw_context, REG_DDC_DIN_CNT2, HIGH_BYTE_16(step)); mhl_tx_write_reg(hw_context, REG_DDC_CMD, ddc_cmd | VAL_DDC_CMD_ENH_DDC_READ_NO_ACK); do { intr3_status = mhl_tx_read_reg(hw_context, REG_INTR3); cbus_status = ok_to_proceed_with_ddc(hw_context); if (BIT_DDC_CMD_DONE & intr3_status) break; } while (cbus_status); if (!cbus_status) break; ddc_status = mhl_tx_read_reg(hw_context, REG_DDC_STATUS); dout_cnt = mhl_tx_read_reg(hw_context, REG_DDC_DOUT_CNT); if (use_spi) { int k; for (k = 0; k < step; ++k) { buffer[(ddc_address + k) % EDID_BLOCK_SIZE] = (uint8_t) mhl_tx_read_reg(hw_context, REG_DDC_DATA); } } else { mhl_tx_read_reg_block(hw_context, REG_DDC_DATA, step, &buffer[ddc_address % EDID_BLOCK_SIZE]); } if (0 == ddc_address) { struct EDID_block0_t *p_EDID_block_0 = (struct EDID_block0_t *) buffer; if (!si_mhl_tx_check_edid_header (hw_context->intr_info->edid_parser_context, p_EDID_block_0)) { #if defined(DEBUG) int start = ddc_address % EDID_BLOCK_SIZE; #endif /* back-up by one step to retry */ MHL_TX_DBG_ERR("%02x %02x %02x %02x " "%02x %02x %02x %02x\n", buffer[start + 0], buffer[start + 1], buffer[start + 2], buffer[start + 3], buffer[start + 4], buffer[start + 5], buffer[start + 6], buffer[start + 7] ); ddc_address -= step; } } if ((ddc_address + step) >= ddc_limit) { /* make sure that done is triggered for sinks with * only 1 EDID block (DVI) */ if (0 == block_num) { struct EDID_block0_t *p_EDID_block_0 = (struct EDID_block0_t *) buffer; if (0 == p_EDID_block_0->extension_flag) trigger_on_last = true; } if (trigger_on_last) { enable_intr(hw_context, INTR_DDC, BIT_DDC_CMD_DONE); /* let int_3_isr or si_mhl_tx_drv_device_isr * clear the interrupt */ } else { mhl_tx_write_reg(hw_context, REG_INTR3, intr3_status); } } else { mhl_tx_write_reg(hw_context, REG_INTR3, intr3_status); } } /* restore TPI mode state */ mhl_tx_write_reg(hw_context, REG_LM_DDC, lm_ddc); return cbus_status; } #endif /* * si_mhl_tx_drv_send_cbus_command * * Write the specified Sideband Channel command to the CBUS. * such as READ_DEVCAP, SET_INT, WRITE_STAT, etc. * Command can be a MSC_MSG command (RCP/RAP/RCPK/RCPE/RAPK), or another * Parameters: * req - Pointer to a cbus_req_t structure containing the command to write * * Returns: * for WRITE_BURST, if an MDT XFIFO level is available, * it returns non-zero, otherwise zero. * for MHL_READ_EDID_BLOCK it returns either the command type, * or 0 if downstream HPD is low. * for all other commands, the return value is the command type. * */ uint8_t si_mhl_tx_drv_send_cbus_command(struct drv_hw_context *hw_context, struct cbus_req *req) { uint8_t ret_val = req->command; uint8_t block_write_buffer[3]; int success; uint8_t cbus_status; switch (req->command) { case MHL_WRITE_BURST: break; default: /* Disable h/w automation of WRITE_BURST until * this command completes */ disable_gen2_write_burst_rcv(hw_context); /* TODO: Review with John */ disable_gen2_write_burst_xmit(hw_context); } hw_context->current_cbus_req = *req; switch (req->command) { case MHL_SEND_3D_REQ_OR_FEAT_REQ: /* DO NOT RE-ORDER THIS CASE */ /* * Do a complete reset of HAWB */ /*mhl_tx_write_reg(hw_context, REG_MDT_RCV_CONTROL, BIT_MDT_RCV_CONTROL_MDT_DISABLE); */ /* TODO: Review with John */ /* Insert disable of both rcv and xmit engine */ /* Enable h/w automation of WRITE_BURST receive */ hw_context->delayed_hawb_enable_reg_val = BIT_MDT_RCV_CONTROL_MDT_DELAY_RCV_EN; enable_gen2_write_burst_rcv(hw_context); hw_context->cbus1_state = CBUS1_MSC_PEND_DLY_RCV_EN; mhl_tx_write_reg(hw_context, REG_MSC_CMD_OR_OFFSET, req->reg); mhl_tx_write_reg(hw_context, REG_MSC_1ST_TRANSMIT_DATA, req->reg_data); mhl_tx_write_reg(hw_context, REG_MSC_COMMAND_START, BIT_MSC_COMMAND_START_MSC_WRITE_STAT_CMD); break; case MHL_SET_INT: case MHL_WRITE_STAT: case MHL_WRITE_XSTAT: MHL_TX_DBG_INFO("->reg: 0x%02x data: 0x%02x\n", req->reg, req->reg_data); #ifdef WRITE_STAT_SET_INT_COALESCE mhl_tx_write_reg_block(hw_context, REG_MSC_CMD_OR_OFFSET, 2, &req->reg); #else mhl_tx_write_reg(hw_context, REG_MSC_CMD_OR_OFFSET, req->reg); mhl_tx_write_reg(hw_context, REG_MSC_1ST_TRANSMIT_DATA, req->reg_data); #endif mhl_tx_write_reg(hw_context, REG_MSC_COMMAND_START, BIT_MSC_COMMAND_START_MSC_WRITE_STAT_CMD); if (MHL_RCHANGE_INT == hw_context->current_cbus_req.reg) { if (MHL2_INT_3D_REQ & hw_context->current_cbus_req. reg_data) { MHL_TX_DBG_WARN("3D_REQ sent\n"); } if (MHL3_INT_FEAT_REQ & hw_context->current_cbus_req. reg_data) { MHL_TX_DBG_WARN("FEAT_REQ sent\n"); } if (MHL_INT_GRT_WRT & hw_context->current_cbus_req. reg_data) { MHL_TX_DBG_WARN("GRT_WRT sent\n"); } } break; case MHL_READ_DEVCAP: MHL_TX_DBG_WARN("Read DEVCAP\n"); /* Enable DEVCAP_DONE interrupt */ enable_intr(hw_context, INTR_EDID, BIT_INTR9_DEVCAP_DONE); mhl_tx_write_reg(hw_context, REG_EDID_CTRL, VAL_EDID_CTRL_EDID_PRIME_VALID_DISABLE | VAL_EDID_CTRL_DEVCAP_SELECT_DEVCAP | VAL_EDID_CTRL_EDID_FIFO_ADDR_AUTO_ENABLE | VAL_EDID_CTRL_EDID_MODE_EN_ENABLE); /* read the entire DEVCAP array in one command */ mhl_tx_write_reg(hw_context, REG_TPI_CBUS_START, BIT_TPI_CBUS_START_GET_DEVCAP_START); break; case MHL_READ_DEVCAP_REG: MHL_TX_DBG_INFO("Trigger DEVCAP_REG Read\n"); MHL_TX_DBG_INFO("Read DEVCAP (0x%02x)\n", req->reg); mhl_tx_write_reg(hw_context, REG_MSC_CMD_OR_OFFSET, req->reg); mhl_tx_write_reg(hw_context, REG_MSC_COMMAND_START, BIT_MSC_COMMAND_START_MSC_READ_DEVCAP_CMD); break; case MHL_READ_XDEVCAP: MHL_TX_DBG_INFO("Trigger XDEVCAP Read\n"); /* Enable DEVCAP_DONE interrupt */ enable_intr(hw_context, INTR_EDID, BIT_INTR9_DEVCAP_DONE); mhl_tx_write_reg(hw_context, REG_EDID_CTRL, VAL_EDID_CTRL_EDID_PRIME_VALID_DISABLE | BIT_EDID_CTRL_XDEVCAP_EN | VAL_EDID_CTRL_DEVCAP_SELECT_DEVCAP | VAL_EDID_CTRL_EDID_FIFO_ADDR_AUTO_ENABLE | VAL_EDID_CTRL_EDID_MODE_EN_ENABLE); /* read the entire DEVCAP array in one command */ mhl_tx_write_reg(hw_context, REG_TPI_CBUS_START, BIT_TPI_CBUS_START_GET_DEVCAP_START); break; case MHL_READ_XDEVCAP_REG: MHL_TX_DBG_INFO("Read XDEVCAP_REG (0x%02x)\n", req->reg); mhl_tx_write_reg(hw_context, REG_MSC_CMD_OR_OFFSET, req->reg); mhl_tx_write_reg(hw_context, REG_MSC_COMMAND_START, BIT_MSC_COMMAND_START_MSC_READ_DEVCAP_CMD); break; case MHL_READ_EDID_BLOCK: hw_context->current_edid_req_blk = 0; #ifdef MANUAL_EDID_FETCH #define HPD_AND_CONNECTED (BIT_CBUS_STATUS_CBUS_HPD | \ BIT_CBUS_STATUS_CBUS_CONNECTED) success = 1; cbus_status = fetch_edid_block(hw_context, hw_context->edid_block, hw_context->current_edid_req_blk, false); if (cbus_status == (BIT_CBUS_STATUS_CBUS_HPD | BIT_CBUS_STATUS_CBUS_CONNECTED)) { int num_extensions; num_extensions = si_mhl_tx_get_num_cea_861_extensions (hw_context->intr_info->edid_parser_context, hw_context->current_edid_req_blk); if (num_extensions < 0) { success = 0; } else { for (hw_context->current_edid_req_blk = 1; hw_context->current_edid_req_blk <= num_extensions; hw_context->current_edid_req_blk++) { cbus_status = fetch_edid_block(hw_context, hw_context->edid_block, hw_context-> current_edid_req_blk, (hw_context-> current_edid_req_blk == num_extensions) ? true : false); if (cbus_status != HPD_AND_CONNECTED) { success = 0; break; } num_extensions = si_mhl_tx_get_num_cea_861_extensions (hw_context->intr_info-> edid_parser_context, hw_context->current_edid_req_blk); if (num_extensions < 0) { MHL_TX_DBG_ERR( "edid problem:%d\n", num_extensions); success = 0; break; } } } } #else success = issue_edid_read_request(hw_context, hw_context->current_edid_req_blk); #endif ret_val = success ? ret_val : 0; break; case MHL_GET_STATE: case MHL_GET_VENDOR_ID: /* for vendor id */ case MHL_SET_HPD: /* Set Hot Plug Detect */ case MHL_CLR_HPD: /* Clear Hot Plug Detect */ case MHL_GET_SC1_ERRORCODE: /* Get channel 1 command error code */ case MHL_GET_DDC_ERRORCODE: /* Get DDC channel command err code */ case MHL_GET_MSC_ERRORCODE: /* Get MSC command error code */ case MHL_GET_SC3_ERRORCODE: /* Get channel 3 command error code */ MHL_TX_DBG_INFO("Sending MSC command %02x, %02x, %02x\n", req->command, req->reg, req->reg_data); mhl_tx_write_reg(hw_context, REG_MSC_CMD_OR_OFFSET, req->command); mhl_tx_write_reg(hw_context, REG_MSC_1ST_TRANSMIT_DATA, req->reg_data); mhl_tx_write_reg(hw_context, REG_MSC_COMMAND_START, BIT_MSC_COMMAND_START_MSC_PEER_CMD); break; case MHL_MSC_MSG: MHL_TX_DBG_INFO("MHL_MSC_MSG sub cmd: 0x%02x data: 0x%02x\n", req->msg_data[0], req->msg_data[1]); block_write_buffer[0] = req->command; block_write_buffer[1] = req->msg_data[0]; block_write_buffer[2] = req->msg_data[1]; if (MHL_MSC_MSG_BIST_REQUEST_STAT == req->msg_data[0]) { hw_context->delayed_hawb_enable_reg_val = BIT_MDT_RCV_CONTROL_MDT_DELAY_RCV_EN; enable_gen2_write_burst_rcv(hw_context); hw_context->cbus1_state = CBUS1_MSC_PEND_DLY_RCV_EN; } mhl_tx_write_reg_block(hw_context, REG_MSC_CMD_OR_OFFSET, 3, block_write_buffer); mhl_tx_write_reg(hw_context, REG_MSC_COMMAND_START, BIT_MSC_COMMAND_START_MSC_MSC_MSG_CMD); break; case MHL_WRITE_BURST: MHL_TX_DBG_INFO ("MHL_WRITE_BURST offset: 0x%02x length: 0x%02x\n", req->burst_offset, req->length); hw_context->hawb_write_pending = true; enable_gen2_write_burst_xmit(hw_context); if (req->length > CBUS_REQ_MSG_DATA_LEN) { ret_val = -EINVAL; break; } mhl_tx_write_reg_block(hw_context, REG_MDT_XMIT_WRITE_PORT, req->length, req->msg_data); ret_val = (MSK_MDT_XFIFO_STAT_MDT_XFIFO_LEVEL_AVAIL & mhl_tx_read_reg(hw_context, REG_MDT_XFIFO_STAT) ); break; default: MHL_TX_DBG_ERR("Unsupported command 0x%02x detected!\n", req->command); ret_val = 0; break; } return ret_val; } void si_mhl_tx_drv_set_3d_mode(struct drv_hw_context *hw_context, bool do_3D, enum _3D_structure_e three_d_mode) { if (do_3D) { if (tdsFramePacking == three_d_mode) { MHL_TX_DBG_INFO("using frame packing\n"); mhl_tx_write_reg(hw_context, REG_VID_OVRRD, BIT_VID_OVRRD_PP_AUTO_DISABLE | VAL_VID_OVRRD_3DCONV_EN_FRAME_PACK); } else { MHL_TX_DBG_INFO("NOT using frame packing\n"); mhl_tx_write_reg(hw_context, REG_VID_OVRRD, BIT_VID_OVRRD_PP_AUTO_DISABLE); } } else { MHL_TX_DBG_INFO("NOT using frame packing\n"); mhl_tx_write_reg(hw_context, REG_VID_OVRRD, BIT_VID_OVRRD_PP_AUTO_DISABLE); } } struct SI_PACK_THIS_STRUCT si_incoming_hw_timing_t { uint8_t h_total_low; uint8_t h_total_high; uint8_t v_total_low; uint8_t v_total_high; uint8_t iadjust; uint8_t pol_detect; uint8_t columns_low; uint8_t columns_high; uint8_t rows_low; uint8_t rows_high; uint8_t field_rate_low; uint8_t field_rate_high; }; uint16_t si_mhl_tx_drv_get_incoming_timing(struct drv_hw_context *hw_context, struct si_incoming_timing_t *p_timing) { uint16_t ret_val; struct si_incoming_hw_timing_t hw_timing; ret_val = mhl_tx_read_reg_block(hw_context, REG_H_RESL, sizeof(hw_timing), (uint8_t *) &hw_timing); p_timing->h_total = (((uint16_t) hw_timing.h_total_high) << 8) | (uint16_t) hw_timing.h_total_low; p_timing->v_total = (((uint16_t) hw_timing.v_total_high) << 8) | (uint16_t) hw_timing.v_total_low; p_timing->columns = (((uint16_t) hw_timing.columns_high) << 8) | (uint16_t) hw_timing.columns_low; p_timing->rows = (((uint16_t) hw_timing.rows_high) << 8) | (uint16_t) hw_timing.rows_low; p_timing->field_rate = (((uint16_t) hw_timing.field_rate_high) << 8) | (uint16_t) hw_timing.field_rate_low; return ret_val; } int si_mhl_tx_drv_get_aksv(struct drv_hw_context *hw_context, uint8_t * buffer) { memcpy(buffer, hw_context->aksv, 5); return 0; } void si_mhl_tx_drv_skip_to_next_edid_block(struct drv_hw_context *hw_context) { hw_context->edid_fifo_block_number++; } int si_mhl_tx_drv_get_edid_fifo_partial_block(struct drv_hw_context *hw_context, uint8_t start, uint8_t length, uint8_t *edid_buf) { int ret_val; uint8_t offset; offset = EDID_BLOCK_SIZE * (hw_context->edid_fifo_block_number & 0x01); offset += start; MHL_TX_DBG_INFO("%p %p\n", hw_context, edid_buf); if (EDID_BLOCK_SIZE == (offset + length)) hw_context->edid_fifo_block_number++; #ifdef MANUAL_EDID_FETCH memcpy(edid_buf, &hw_context->edid_block[start], length); #else mhl_tx_write_reg(hw_context, REG_EDID_FIFO_ADDR, offset); ret_val = mhl_tx_read_reg_block(hw_context, REG_EDID_FIFO_RD_DATA, length, edid_buf); #endif DUMP_EDID_BLOCK(0, edid_buf, length); ret_val = ok_to_proceed_with_ddc(hw_context); if (!ret_val) { MHL_TX_DBG_INFO("No HPD ret_val:0x%02x\n", ret_val); return ne_NO_HPD; } else { MHL_TX_DBG_INFO("EDID block read complete. ret_val:0x%02x\n", ret_val); return ne_SUCCESS; } } int si_mhl_tx_drv_get_edid_fifo_next_block(struct drv_hw_context *hw_context, uint8_t *edid_buf) { int ret_val; uint8_t offset; offset = EDID_BLOCK_SIZE * (hw_context->edid_fifo_block_number & 0x01); MHL_TX_DBG_INFO("%p %p\n", hw_context, edid_buf); hw_context->edid_fifo_block_number++; #ifdef MANUAL_EDID_FETCH memcpy(edid_buf, hw_context->edid_block, EDID_BLOCK_SIZE); #else mhl_tx_write_reg(hw_context, REG_EDID_FIFO_ADDR, offset); ret_val = mhl_tx_read_reg_block(hw_context, REG_EDID_FIFO_RD_DATA, EDID_BLOCK_SIZE, edid_buf); #endif DUMP_EDID_BLOCK(0, edid_buf, EDID_BLOCK_SIZE); ret_val = ok_to_proceed_with_ddc(hw_context); if (!ret_val) { MHL_TX_DBG_INFO("No HPD ret_val:0x%02x\n", ret_val); return ne_NO_HPD; } else { MHL_TX_DBG_ERR("EDID block read complete. ret_val:0x%02x\n", ret_val); return ne_SUCCESS; } } int si_mhl_tx_drv_get_scratch_pad(struct drv_hw_context *hw_context, uint8_t start_reg, uint8_t *data, uint8_t length) { if ((start_reg + length) > (int)MHL_SCRATCHPAD_SIZE) return -1; memcpy(data, &hw_context->write_burst_data[start_reg], length); return 0; } static bool packed_pixel_available(struct mhl_dev_context *dev_context) { struct drv_hw_context *hw_context; hw_context = (struct drv_hw_context *)&dev_context->drv_context; if (hw_context->cbus_mode >= CM_eCBUS_S) { if ((MHL_DEV_VID_LINK_SUPP_16BPP & DEVCAP_VAL_VID_LINK_MODE) && (dev_context->dev_cap_cache.mdc.vid_link_mode & MHL_DEV_VID_LINK_SUPP_16BPP)) { return true; } } else { if ((MHL_DEV_VID_LINK_SUPP_PPIXEL & DEVCAP_VAL_VID_LINK_MODE) && (dev_context->dev_cap_cache.mdc.vid_link_mode & MHL_DEV_VID_LINK_SUPP_PPIXEL)) { return true; } } return false; } #define SIZE_AVI_INFOFRAME 14 static uint8_t calculate_avi_info_frame_checksum( union hw_avi_payload_t *payload) { uint8_t checksum; checksum = 0x82 + 0x02 + 0x0D; /* these are set by the hardware */ return calculate_generic_checksum(payload->ifData, checksum, SIZE_AVI_INFOFRAME); } static int is_valid_avi_info_frame(struct mhl_dev_context *dev_context, struct avi_info_frame_t *avif) { uint8_t checksum; checksum = calculate_generic_checksum((uint8_t *) avif, 0, sizeof(*avif)); if (0 != checksum) { MHL_TX_DBG_ERR("AVI info frame checksum is: 0x%02x " "should be 0\n", checksum); return 0; } else if (0x82 != avif->header.type_code) { MHL_TX_DBG_ERR("Invalid AVI type code: 0x%02x\n", avif->header.type_code); return 0; } else if (0x02 != avif->header.version_number) { MHL_TX_DBG_ERR("Invalid AVI version: 0x%02x\n", avif->header.version_number); return 0; } else if (0x0D != avif->header.length) { return 0; } else { return 1; } } #define IEEE_OUI(x) ((uint32_t)x[0] | \ (((uint32_t)x[1]) << 8) | \ (((uint32_t)x[2]) << 16)) static int is_valid_vsif(struct mhl_dev_context *dev_context, union vsif_mhl3_or_hdmi_u *vsif) { uint8_t checksum; checksum = calculate_generic_checksum((uint8_t *) vsif, 0, sizeof(vsif->common.header) + vsif->common.header.length); if (0 != checksum) { MHL_TX_DBG_WARN("VSIF info frame checksum is: 0x%02x " "should be 0\n", checksum); /* Try again, assuming that the header includes the checksum. */ checksum = calculate_generic_checksum((uint8_t *) vsif, 0, sizeof(vsif->common.header) + vsif->common.header.length + sizeof(vsif->common.checksum)); if (0 != checksum) { MHL_TX_DBG_ERR("VSIF info frame checksum (adjusted " "for checksum itself) is: 0x%02x " "should be 0\n", checksum); return 0; } } if (0x81 != vsif->common.header.type_code) { MHL_TX_DBG_ERR("Invalid VSIF type code: 0x%02x\n", vsif->common.header.type_code); return 0; } else { uint32_t ieee_oui = IEEE_OUI(vsif->common.ieee_oui); switch (ieee_oui) { case IEEE_OUI_HDMI: if (0x01 == vsif->common.header.version_number) return 1; MHL_TX_DBG_ERR("Invalid VSIF version: 0x%02x\n", vsif->common.header.version_number); break; case IEEE_OUI_MHL: if (0x03 == vsif->common.header.version_number) return 1; MHL_TX_DBG_ERR("Invalid VSIF version: 0x%02x\n", vsif->common.header.version_number); break; default: MHL_TX_DBG_ERR("Invalid IEEE OUI: 0x%06x\n", ieee_oui); } } return 0; } static void print_vic_modes_impl(struct drv_hw_context *hw_context, uint8_t vic, const char *function, int iLine) { int i; struct vic_name { uint8_t vic; char name[15]; } vic_name_table[] = { { 2, "480p"}, { 4, "720p60"}, { 5, "1080i60"}, { 6, "480i"}, { 16, "1080p60"}, { 17, "576p50"}, { 19, "720p50"}, { 20, "1080i50"}, { 21, "576i50"}, { 31, "1080p50"}, { 32, "1080p24"}, { 33, "1080p25"}, { 34, "1080p30"}, { 60, "720p24"}, { 61, "720p25"}, { 62, "720p30"}, { 63, "1080p120"}, { 64, "1080p100"}, { 86, "2560x1080p24w"}, { 87, "2560x1080p25w"}, { 89, "2560x1080p50w"}, { 93, "2160p24"}, { 94, "2160p25"}, { 95, "2160p30"}, { 98, "4096x2160p24"}, { 99, "4096x2160p25"}, {100, "4096x2160p30"}, /* to handle the case where VIC is not found in the table */ {0, ""} }; #define NUM_VIC_NAMES (sizeof(vic_name_table)/sizeof(vic_name_table[0])) /* stop before the terminator */ for (i = 0; i < (NUM_VIC_NAMES - 1); i++) { if (vic == vic_name_table[i].vic) break; } if (vic) { MHL_TX_PROXY_DBG_PRINT(0, function, iLine, "VIC = %s%d%s (%s)\n", ANSI_ESC_GREEN_TEXT, vic, ANSI_ESC_RESET_TEXT, vic_name_table[i].name); } else { MHL_TX_PROXY_DBG_PRINT(0, function, iLine, "VIC:%s%d%s\n", ANSI_ESC_YELLOW_TEXT, vic, ANSI_ESC_RESET_TEXT); } } #define print_vic_modes(ctx, vic) \ print_vic_modes_impl(ctx, vic, __func__, __LINE__) #define DUMP_INFO_FRAMES #ifdef DUMP_INFO_FRAMES static void dump_avif_vsif_impl(struct drv_hw_context *hw_context, const char *function, int line_num) { if (debug_level >= DBG_MSG_LEVEL_WARN) { struct hdmi_vsif_t vsif; struct avi_info_frame_t avif; int i; unsigned char *c; mhl_tx_write_reg(hw_context, REG_RX_HDMI_CTRL2, hw_context->rx_hdmi_ctrl2_defval | VAL_RX_HDMI_CTRL2_VSI_MON_SEL_VSI); mhl_tx_read_reg_block(hw_context, REG_RX_HDMI_MON_PKT_HEADER1, sizeof(vsif), (uint8_t *) &vsif); mhl_tx_write_reg(hw_context, REG_RX_HDMI_CTRL2, hw_context->rx_hdmi_ctrl2_defval | VAL_RX_HDMI_CTRL2_VSI_MON_SEL_AVI); mhl_tx_read_reg_block(hw_context, REG_RX_HDMI_MON_PKT_HEADER1, sizeof(avif), (uint8_t *) &avif); print_formatted_debug_msg(NULL, function, line_num, "VSIF:"); for (i = 0, c = (unsigned char *)&vsif; i < sizeof(vsif); ++i, ++c) { printk(KERN_CONT " %02x", *c); } printk("\n"); print_formatted_debug_msg(NULL, function, line_num, "AVIF:"); for (i = 0, c = (unsigned char *)&avif; i < sizeof(avif); ++i, ++c) { printk(KERN_CONT " %02x", *c); } printk(KERN_CONT "\n"); } } #define DUMP_AVIF_VSIF(hw_context) \ dump_avif_vsif_impl(hw_context, __func__, __LINE__); #else #define DUMP_AVIF_VSIF(hw_context) /* nothing */ #endif enum timing_info_basis_e { use_avi_vic, use_hdmi_vic, use_mhl3_sequence_index, use_hardware_totals }; static bool process_hdmi_vsif(struct drv_hw_context *hw_context, struct avi_info_frame_data_byte_4_t *p_input_video_code, enum timing_info_basis_e *p_timing_info_basis, enum mhl_vid_fmt_e *p_vid_fmt, uint32_t *p_threeDPixelClockRatio, uint8_t *p_fp_3d_mode, enum mhl_3d_fmt_type_e *p_3d_fmt_type) { uint8_t input_VIC = (uint8_t) (*p_input_video_code).VIC; /* * HDMI spec. v1.4: * "When transmitting any 2D video format of section 6.3 above, an * HDMI Source shall set the VIC field to the Video Code for that * format. See CEA-861-D section 6.4 for detatils. The additional VIC * values from 60 to 64 are defined in Table 8-4. When transmitting any * 3D video format using the 3D_Structure field in the HDMI Vendor * Specific InfoFrame, an HDMI Source shall set the AVI InfoFrame VIC * field to satisfy the relation described in section 8.2.3.2. * When transmitting any extended video format indicated through use of * the HDMI_VIC field in the HDMI Vendor Specific InfoFrame or any * other format which is not described in the above cases, and HDMI * Source shall set the AVI InfoFrame VIC field to zero." */ MHL_TX_DBG_WARN("valid HDMI VSIF\n"); print_vic_modes(hw_context, input_VIC); if (0 == input_VIC) { if (hvfExtendedResolutionFormatPresent == hw_context->current_vsif.hdmi.payLoad.pb4. HDMI_Video_Format) { #if defined(DEBUG) uint8_t vic = hw_context->current_vsif.hdmi.payLoad.pb5. HDMI_VIC; #endif /* HDMI_VIC should contain one of 0x01 through 0x04 */ MHL_TX_DBG_ERR("HDMI extended resolution %d\n", vic); *p_timing_info_basis = use_hdmi_vic; } else { #ifdef PC_MODE_VIDEO_TIMING_SUPPORT MHL_TX_DBG_WARN("AVI VIC is zero!!!\n"); *p_timing_info_basis = use_hardware_totals; #else /* * Instead of no video, let us attempt HDCP and if * possible show video. If hdcp fails due to clock * difference on input (which we don't know about * clearly), after 5 attempts it will anyways stabilize * and use an infoframe interrupt if that arrives with * a good vic. * * TODO: Flag arrival of an infoframe from the time * this path was executed and abort HDCP a bit earlier. */ MHL_TX_DBG_ERR("AVI VIC is zero!!!\n"); return false; #endif } } /* * From VSIF bytes, figure out if we need to perform * frame packing or not. This helps decide if packed pixel * (16-bit) is required or not in conjunction with the VIC. */ if (hvf3DFormatIndicationPresent == hw_context->current_vsif.hdmi.payLoad.pb4.HDMI_Video_Format) { *p_vid_fmt = mhl_vid_fmt_3d_fmt_present; MHL_TX_DBG_INFO("VSIF indicates 3D\n"); switch (hw_context->current_vsif.hdmi.payLoad.pb5. ThreeDStructure.threeDStructure) { case tdsFramePacking: MHL_TX_DBG_INFO("mhl_tx: tdsFramePacking\n"); *p_threeDPixelClockRatio = 2; *p_fp_3d_mode |= VAL_VID_OVRRD_3DCONV_EN_FRAME_PACK; *p_3d_fmt_type = MHL_3D_FMT_TYPE_FS; break; case tdsTopAndBottom: *p_3d_fmt_type = MHL_3D_FMT_TYPE_TB; break; case tdsSideBySide: *p_3d_fmt_type = MHL_3D_FMT_TYPE_LR; break; default: break; } } return true; } /* Find pixel clock from closest match to input timing parameters */ static uint32_t find_pixel_clock_from_closest_match_timing( struct mhl_dev_context *dev_context, struct si_incoming_timing_t *p_timing) { struct drv_hw_context *hw_context = (struct drv_hw_context *)&dev_context->drv_context; uint32_t pixels_per_second = 0; uint32_t pixel_clock_frequency = 0; uint32_t pixels_per_line, lines_per_field, fields_per_second; uint32_t lines_per_second; int ret_val; pixel_clock_frequency = 0; /* Measure the HTOTAL and VTOTAL and look them up in a table */ /* also consider display enable periods and field rates */ ret_val = si_mhl_tx_drv_get_incoming_timing(hw_context, p_timing); pixels_per_line = (uint32_t) p_timing->h_total; lines_per_field = (uint32_t) p_timing->v_total; fields_per_second = (uint32_t) p_timing->field_rate; lines_per_second = lines_per_field * fields_per_second; pixels_per_second = pixels_per_line * lines_per_second; /* did we get a valid pixel clock? */ if (pixels_per_second) { p_timing->calculated_pixel_clock = 0; pixel_clock_frequency = si_mhl_tx_find_timings_from_totals( dev_context->edid_parser_context, p_timing); MHL_TX_DBG_WARN ("{%d,%d,%dx%d-%dHz, %s%d.%d%s MHz, %d.%d MHz}\n", p_timing->h_total, p_timing->v_total, p_timing->columns, p_timing->rows, p_timing->field_rate, ANSI_ESC_YELLOW_TEXT, p_timing->calculated_pixel_clock / 1000000, p_timing->calculated_pixel_clock % 1000000, ANSI_ESC_RESET_TEXT, pixels_per_second / 1000000, pixels_per_second % 1000000); } if (0 == pixel_clock_frequency) { uint8_t mask; uint8_t idx; uint8_t limit; int32_t total; int32_t average; int32_t epsilon; limit = ARRAY_SIZE(hw_context->pixel_clock_history); mask = limit - 1; idx = hw_context->idx_pixel_clock_history++ & mask; hw_context->pixel_clock_history[idx] = pixels_per_second; /* emphasize modes that are not in the table */ MHL_TX_DBG_ERR("%s{%4d,%4d,%5d,%4d,%3d,%9d,??," "{0,0},\"%dx%d-%2d\"}%s\n", (0 != pixel_clock_frequency) ? ANSI_ESC_GREEN_TEXT : ANSI_ESC_YELLOW_TEXT, p_timing->h_total, p_timing->v_total, p_timing->columns, p_timing->rows, p_timing->field_rate, pixels_per_second, p_timing->columns, p_timing->rows, p_timing->field_rate, ANSI_ESC_RESET_TEXT); for (idx = 0, total = 0; idx < limit; ++idx) { MHL_TX_DBG_INFO("total: %d\n", total) total += hw_context->pixel_clock_history[idx]; } average = total / limit; epsilon = pixels_per_second - average; /* Ignore field rates less than 24 FPS */ if (p_timing->field_rate >= 24) { if (0 == epsilon) { MHL_TX_DBG_ERR("stable pixel clock: %d\n", pixels_per_second) pixel_clock_frequency = pixels_per_second; } } } else { MHL_TX_DBG_ERR("%s%dx%d-%dHz@%d.%dMHz%s\n", ANSI_ESC_GREEN_TEXT, p_timing->columns, p_timing->rows, p_timing->field_rate, pixel_clock_frequency / 1000000, pixel_clock_frequency % 1000000, ANSI_ESC_RESET_TEXT); } return pixel_clock_frequency; } #define ENABLE_FP VAL_VID_OVRRD_3DCONV_EN_FRAME_PACK static void process_mhl3_vsif(struct drv_hw_context *hw_context, enum mhl_3d_fmt_type_e *p_3d_fmt_type, enum timing_info_basis_e *p_timing_info_basis, uint32_t *p_threeDPixelClockRatio, uint8_t *p_fp_3d_mode) { switch (PB4_MASK_MHL_VID_FMT & hw_context->vsif_mhl3_or_hdmi_from_callback.mhl3.pb4) { case mhl_vid_fmt_no_additional: break; case mhl_vid_fmt_3d_fmt_present: *p_3d_fmt_type = PB4_MASK_MHL_3D_FMT_TYPE & hw_context->vsif_mhl3_or_hdmi_from_callback.mhl3.pb4; switch (*p_3d_fmt_type) { case MHL_3D_FMT_TYPE_TB: case MHL_3D_FMT_TYPE_LR: case MHL_3D_FMT_TYPE_TBLR: break; case MHL_3D_FMT_TYPE_FS: case MHL_3D_FMT_TYPE_FS_TB: case MHL_3D_FMT_TYPE_FS_LR: *p_threeDPixelClockRatio = 2; *p_fp_3d_mode |= ENABLE_FP; break; } break; case mhl_vid_fmt_multi_view: break; case mhl_vid_fmt_dual_3d: break; } switch (PB6_MASK_MHL_HEV_FMT & hw_context->vsif_mhl3_or_hdmi_from_callback.mhl3.pb6) { case mhl_hev_fmt_hev_present: *p_timing_info_basis = use_mhl3_sequence_index; break; case mhl_hev_fmt_no_additional: case mhl_hev_fmt_reserved_2: case mhl_hev_fmt_reserved_3: /* nothing to do in these cases */ break; } } /* * This function must not be called for DVI mode. */ static int set_hdmi_params(struct mhl_dev_context *dev_context) { uint32_t pixel_clock_frequency = 0; uint8_t tpi_output = 0; uint8_t tpi_input = 0; uint32_t threeDPixelClockRatio; uint8_t packedPixelNeeded = 0; uint8_t fp_3d_mode; enum AviColorSpace_e input_clr_spc = acsRGB; enum avi_quant_range_e input_quant_range = aqr_default; enum avi_quant_range_e output_quant_range = aqr_default; uint8_t output_clr_spc = acsRGB; struct avi_info_frame_data_byte_4_t input_video_code; struct avi_info_frame_data_byte_4_t output_video_code; struct drv_hw_context *hw_context = (struct drv_hw_context *)&dev_context->drv_context; enum timing_info_basis_e timing_info_basis = use_avi_vic; /* default values for MHL3 VSIF, which we will always send */ enum mhl_vid_fmt_e vid_fmt = mhl_vid_fmt_no_additional; enum mhl_3d_fmt_type_e _3d_fmt_type = MHL_3D_FMT_TYPE_FS; enum mhl_sep_audio_e sep_aud = mhl_sep_audio_not_available; enum mhl_hev_fmt_e hev_fmt = mhl_hev_fmt_no_additional; uint16_t hev_fmt_type = 0; uint32_t delay_sync = 0; enum mhl_av_delay_dir_e delay_dir = mhl_av_delay_dir_audio_earlier; /* Extract VIC from incoming AVIF */ input_video_code = hw_context->current_avi_info_frame.payLoad.hwPayLoad. namedIfData.ifData_u.bitFields.VIC; threeDPixelClockRatio = 1; fp_3d_mode = REG_VID_OVRRD_DEFVAL | BIT_VID_OVRRD_M1080P_OVRRD; /* did we get an MHL3 vsif from the callback API? */ if (hw_context->valid_vsif) { uint32_t ieee_oui; ieee_oui = IEEE_OUI(hw_context->current_vsif.common.ieee_oui); switch (hw_context->hpd_high_callback_status) { case HH_FMT_HDMI_VSIF_MHL3_HDCP_ON: case HH_FMT_HDMI_VSIF_MHL3: case HH_FMT_HDMI_VSIF_MHL3_HDCP_ON_NOT_RPT: case HH_FMT_HDMI_VSIF_MHL3_NOT_RPT: MHL_TX_DBG_WARN("MHL3 VSIF from callback\n"); process_mhl3_vsif(hw_context, &_3d_fmt_type, &timing_info_basis, &threeDPixelClockRatio, &fp_3d_mode); break; case HH_FMT_HDMI_VSIF_HDMI: case HH_FMT_HDMI_VSIF_HDMI_HDCP_ON: case HH_FMT_HDMI_VSIF_HDMI_NOT_RPT: case HH_FMT_HDMI_VSIF_HDMI_HDCP_ON_NOT_RPT: MHL_TX_DBG_WARN ("HDMI VSIF from callback\n"); if (false == process_hdmi_vsif(hw_context, &input_video_code, &timing_info_basis, &vid_fmt, &threeDPixelClockRatio, &fp_3d_mode, &_3d_fmt_type)) return false; break; default: MHL_TX_DBG_WARN("VSIF from data islands\n") switch (ieee_oui) { case IEEE_OUI_HDMI: if (false == process_hdmi_vsif(hw_context, &input_video_code, &timing_info_basis, &vid_fmt, &threeDPixelClockRatio, &fp_3d_mode, &_3d_fmt_type)) { return false; } break; case IEEE_OUI_MHL: process_mhl3_vsif(hw_context, &_3d_fmt_type, &timing_info_basis, &threeDPixelClockRatio, &fp_3d_mode); break; } break; } } else { /* no incoming HDMI VSIF */ if (0 == input_video_code.VIC) { DUMP_AVIF_VSIF(hw_context) #ifdef PC_MODE_VIDEO_TIMING_SUPPORT timing_info_basis = use_hardware_totals; #else /* * This routine will not be called until we know * (from the downstream EDID)that the sink is HDMI. * We do not support DVI only sources. The * upstream source is expected to choose between * HDMI and DVI based upon the EDID that we present * upstream. The other information in the infoframe * is not helpful for determining the pixel clock * frequency. So we try to infer the pixel clock * from the HTOTAL and VTOTAL registers. This is * the case for PC modes on HDMI (valid AVIF, * no VSIF, VIC==0); */ MHL_TX_DBG_ERR ("no VSIF and AVI VIC (offset 0x%x) is zero!!! " "trying HTOTAL/VTOTAL\n", (size_t) &hw_context->current_avi_info_frame. payLoad.hwPayLoad.namedIfData.ifData_u.bitFields. VIC - (size_t) &hw_context->current_avi_info_frame); /* Return error to avoid further processing. */ return false; #endif } else { print_vic_modes(hw_context, (uint8_t) input_video_code.VIC); } } mhl_tx_write_reg(hw_context, REG_VID_OVRRD, fp_3d_mode); #ifndef MANUAL_INFO_FRAME_CLEAR_AT_HPD_DRIVEN_HIGH /* Do not remember previous VSIF */ hw_context->valid_vsif = false; #endif /* make a copy of avif */ hw_context->outgoingAviPayLoad = hw_context->current_avi_info_frame.payLoad.hwPayLoad; switch (timing_info_basis) { case use_avi_vic: /* compute pixel frequency */ pixel_clock_frequency = si_edid_find_pixel_clock_from_AVI_VIC( dev_context->edid_parser_context, input_video_code.VIC); output_video_code = input_video_code; break; case use_hdmi_vic: output_video_code.VIC = hw_context->current_vsif.hdmi.payLoad.pb5.HDMI_VIC; pixel_clock_frequency = si_edid_find_pixel_clock_from_HDMI_VIC( dev_context->edid_parser_context, output_video_code.VIC); output_video_code.VIC = si_edid_map_hdmi_vic_to_mhl3_vic( dev_context->edid_parser_context, output_video_code.VIC); output_video_code.futureMustBeZero = 0; print_vic_modes(hw_context, output_video_code.VIC); break; case use_mhl3_sequence_index: output_video_code.VIC = 0; output_video_code.futureMustBeZero = 0; pixel_clock_frequency = si_edid_find_pixel_clock_from_HEV_DTD( dev_context->edid_parser_context, hw_context->vsif_mhl3_or_hdmi_from_callback. mhl3.mhl_hev_fmt_type); break; case use_hardware_totals: output_video_code = input_video_code; { struct si_incoming_timing_t timing; /* initialize this here */ memset((void *)&timing, 0, sizeof(timing)); pixel_clock_frequency = find_pixel_clock_from_closest_match_timing (dev_context, &timing); if (0 == pixel_clock_frequency) { MHL_TX_DBG_WARN( "%sVIC==0 and totals not supported%s\n", ANSI_ESC_YELLOW_TEXT, ANSI_ESC_RESET_TEXT); mhl_tx_start_timer(dev_context, hw_context-> input_field_rate_measurement_timer, FIELD_RATE_MEASUREMENT_INTERVAL); return false; } else { MHL_TX_DBG_INFO("MHL3 vic:%d\n", timing.mhl3_vic); /* This value will be zero for modes that * don't have an MHL3 VIC */ output_video_code.VIC = timing.mhl3_vic; print_vic_modes(hw_context, output_video_code.VIC); } } break; default: MHL_TX_DBG_ERR( "%s'shouldn't get here (invalid switch value)%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); output_video_code.VIC = 0; output_video_code.futureMustBeZero = 0; break; } mhl_tx_stop_timer(dev_context, hw_context->input_field_rate_measurement_timer); hw_context->outgoingAviPayLoad.namedIfData.ifData_u.bitFields.VIC = output_video_code; /* extract input color space */ input_clr_spc = hw_context->current_avi_info_frame.payLoad.hwPayLoad. namedIfData.ifData_u.bitFields.pb1.colorSpace; input_quant_range = hw_context->current_avi_info_frame.payLoad.hwPayLoad. namedIfData.ifData_u.bitFields.pb3.RGBQuantizationRange; output_quant_range = input_quant_range; MHL_TX_DBG_INFO("input_clr_spc = %02X infoData[0]:%02X\n", input_clr_spc, hw_context->current_avi_info_frame.payLoad.hwPayLoad. namedIfData.ifData_u.infoFrameData[0]); /* * decide about packed pixel mode */ pixel_clock_frequency *= threeDPixelClockRatio; MHL_TX_DBG_INFO("pixel clock:%u\n", pixel_clock_frequency); if (qualify_pixel_clock_for_mhl(dev_context->edid_parser_context, pixel_clock_frequency, 24)) { MHL_TX_DBG_INFO("OK for 24 bit pixels\n"); } else { /* not enough bandwidth for uncompressed video */ if (si_edid_sink_supports_YCbCr422( dev_context->edid_parser_context)) { MHL_TX_DBG_INFO("Sink supports YCbCr422\n"); if (qualify_pixel_clock_for_mhl( dev_context->edid_parser_context, pixel_clock_frequency, 16)) { /* enough for packed pixel */ packedPixelNeeded = 1; } else { MHL_TX_DBG_ERR("unsupported video mode." "pixel clock too high %s\n", si_peer_supports_packed_pixel (dev_context) ? "" : "(peer does not support packed pixel)." ); return false; } } else { MHL_TX_DBG_ERR("unsupported video mode." "Sink doesn't support 4:2:2.\n"); return false; } } /* Does output color space need to be 4:2:2 or same as input */ output_clr_spc = input_clr_spc; #define MHL3_PACKED_PIXEL_MODE VAL_M3_P0CTRL_MHL3_P0_PIXEL_MODE_PACKED switch (hw_context->pp_16bpp_override) { case pp_16bpp_automatic: case pp_16bpp_override_24bpp: break; case pp_16bpp_override_16bpp: packedPixelNeeded = 1; break; } if (packedPixelNeeded) { if (packed_pixel_available(dev_context)) { dev_context->link_mode = MHL_STATUS_PATH_ENABLED | MHL_STATUS_CLK_MODE_PACKED_PIXEL; /* enforcing 4:2:2 if packed pixel. */ output_clr_spc = BIT_EDID_FIELD_FORMAT_YCbCr422; /* begin BZ 32674 ( */ #if 1 if (aqr_default != input_quant_range) { /* do nothing */ } else if (acsRGB == input_clr_spc) { input_quant_range = aqr_limited_range; #if 0 output_quant_range = aqr_full_range; hw_context->outgoingAviPayLoad.namedIfData. ifData_u.bitFields.pb3. RGBQuantizationRange = aqr_full_range; #endif } #endif #if 1 tpi_output |= BIT_TPI_OUTPUT_CSCMODE709; /* indicate ITU BT709 */ hw_context->outgoingAviPayLoad.namedIfData. ifData_u.bitFields. colorimetryAspectRatio.Colorimetry = 2; #endif /* end BZ 32674 ) */ if (IN_MHL3_MODE(hw_context)) { MHL_TX_DBG_INFO("setting 16BPP mode\n"); mhl_tx_modify_reg(hw_context, REG_M3_P0CTRL, BIT_M3_P0CTRL_MHL3_P0_PIXEL_MODE, MHL3_PACKED_PIXEL_MODE); } else { MHL_TX_DBG_INFO("setting packed pixel mode\n"); mhl_tx_write_reg(hw_context, REG_VID_MODE, VAL_VID_MODE_M1080P_ENABLE); mhl_tx_write_reg(hw_context, REG_MHL_TOP_CTL, 0x41); mhl_tx_write_reg(hw_context, REG_MHLTX_CTL6, 0x60); } } else { MHL_TX_DBG_ERR( "Unsupported video mode. Packed Pixel not " "available. Sink's link mode = 0x%02x\n", dev_context->dev_cap_cache.mdc.vid_link_mode); return false; } } else { dev_context->link_mode = MHL_STATUS_PATH_ENABLED | MHL_STATUS_CLK_MODE_NORMAL; if (IN_MHL3_MODE(hw_context)) { MHL_TX_DBG_INFO("setting 24BPP Mode\n"); mhl_tx_modify_reg(hw_context, REG_M3_P0CTRL, BIT_M3_P0CTRL_MHL3_P0_PIXEL_MODE, VAL_M3_P0CTRL_MHL3_P0_PIXEL_MODE_NORMAL); } else { MHL_TX_DBG_INFO( "normal Mode, Packed Pixel mode disabled\n"); mhl_tx_write_reg(hw_context, REG_VID_MODE, VAL_VID_MODE_M1080P_DISABLE); mhl_tx_write_reg(hw_context, REG_MHL_TOP_CTL, 0x01); mhl_tx_write_reg(hw_context, REG_MHLTX_CTL6, 0xA0); } } tpi_input = colorSpaceTranslateInfoFrameToHw[input_clr_spc]; tpi_input |= input_quant_range << 2; /* Set input color space */ mhl_tx_write_reg(hw_context, REG_TPI_INPUT, tpi_input); /* Set output color space */ tpi_output |= colorSpaceTranslateInfoFrameToHw[output_clr_spc]; tpi_output |= input_quant_range << 2; mhl_tx_write_reg(hw_context, REG_TPI_OUTPUT, tpi_output); if (IN_MHL3_MODE(hw_context)) { struct MHL_bits_per_pixel_fmt_data_t bpp_fmt; struct MHL_bits_per_pixel_fmt_data_t *p_buffer; size_t xfer_size; /* only one descriptor to send */ xfer_size = sizeof(bpp_fmt) - sizeof(p_buffer->descriptors) + sizeof(p_buffer->descriptors[0]); p_buffer = si_mhl_tx_get_sub_payload_buffer(dev_context, xfer_size); if (p_buffer) { p_buffer->header.burst_id.low = LOW_BYTE_16(burst_id_BITS_PER_PIXEL_FMT); p_buffer->header.burst_id.high = HIGH_BYTE_16(burst_id_BITS_PER_PIXEL_FMT); p_buffer->header.checksum = 0; p_buffer->header.total_entries = 1; p_buffer->header.sequence_index = 1; p_buffer->num_entries_this_burst = 1; p_buffer->descriptors[0].stream_id = 0; switch (dev_context-> link_mode & MHL_STATUS_CLK_MODE_MASK) { case MHL_STATUS_CLK_MODE_PACKED_PIXEL: p_buffer->descriptors[0].stream_pixel_format = VIEW_PIX_FMT_16BPP; break; case MHL_STATUS_CLK_MODE_NORMAL: p_buffer->descriptors[0].stream_pixel_format = VIEW_PIX_FMT_24BPP; break; } p_buffer->header.checksum = calculate_generic_checksum(p_buffer, 0, xfer_size); si_mhl_tx_push_block_transactions(dev_context); } /* enable TMDS */ mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL0, 0xF0); /* fill in values for MHL3 VSIF */ hw_context->outgoing_mhl3_vsif.header.type_code = MHL3_VSIF_TYPE; hw_context->outgoing_mhl3_vsif.header.version_number = MHL3_VSIF_VERSION; hw_context->outgoing_mhl3_vsif.header.length = sizeof(hw_context->outgoing_mhl3_vsif); hw_context->outgoing_mhl3_vsif.checksum = 0; hw_context->outgoing_mhl3_vsif.ieee_oui[0] = (uint8_t) (IEEE_OUI_MHL & 0xFF); hw_context->outgoing_mhl3_vsif.ieee_oui[1] = (uint8_t) ((IEEE_OUI_MHL >> 8) & 0xFF); hw_context->outgoing_mhl3_vsif.ieee_oui[2] = (uint8_t) ((IEEE_OUI_MHL >> 16) & 0xFF); hw_context->outgoing_mhl3_vsif.pb4 = MHL3_VSIF_PB4(vid_fmt, _3d_fmt_type, sep_aud); hw_context->outgoing_mhl3_vsif.pb5_reserved = 0; hw_context->outgoing_mhl3_vsif.pb6 = MHL3_VSIF_PB6(hev_fmt); hw_context->outgoing_mhl3_vsif.mhl_hev_fmt_type.high = HIGH_BYTE_16(hev_fmt_type); hw_context->outgoing_mhl3_vsif.mhl_hev_fmt_type.low = LOW_BYTE_16(hev_fmt_type); hw_context->outgoing_mhl3_vsif.pb9 = MHL3_VSIF_PB9(delay_sync, delay_dir); hw_context->outgoing_mhl3_vsif.av_delay_sync.high = HIGH_BYTE_16(delay_sync); hw_context->outgoing_mhl3_vsif.av_delay_sync.low = LOW_BYTE_16(delay_sync); hw_context->outgoing_mhl3_vsif.checksum = calculate_generic_checksum( &hw_context->outgoing_mhl3_vsif, 0, hw_context->outgoing_mhl3_vsif.header. length); /* * Program TMDS link speeds */ switch (dev_context->link_mode & MHL_STATUS_CLK_MODE_MASK) { case MHL_STATUS_CLK_MODE_PACKED_PIXEL: si_mhl_tx_drv_set_lowest_tmds_link_speed(dev_context, pixel_clock_frequency, 16); break; case MHL_STATUS_CLK_MODE_NORMAL: si_mhl_tx_drv_set_lowest_tmds_link_speed(dev_context, pixel_clock_frequency, 24); break; } } else { /* * MSC WRITE_STATUS is required to prepare sink for new mode */ si_mhl_tx_set_status(dev_context, false, MHL_STATUS_REG_LINK_MODE, dev_context->link_mode); } /* * Prepare outgoing AVIF for later programming the registers * * the checksum itself is included in the calculation. */ hw_context->outgoingAviPayLoad.namedIfData.checksum = 0; hw_context->outgoingAviPayLoad.namedIfData.ifData_u.bitFields.pb1. colorSpace = output_clr_spc; hw_context->outgoingAviPayLoad.ifData[1] &= 0x7F; /* Clear PB1[7] */ hw_context->outgoingAviPayLoad.ifData[4] &= 0x7F; /* Clear PB4[7] */ hw_context->outgoingAviPayLoad.namedIfData.checksum = calculate_avi_info_frame_checksum( &hw_context->outgoingAviPayLoad); return true; } /* * process_info_frame_change * called by the MHL Tx driver when a * new AVI info frame is received from upstream * OR * called by customer's SOC video driver when a mode change is desired. */ void process_info_frame_change(struct drv_hw_context *hw_context, union vsif_mhl3_or_hdmi_u *vsif, struct avi_info_frame_t *avif) { bool mode_change = false; struct mhl_dev_context *dev_context; dev_context = container_of((void *)hw_context, struct mhl_dev_context, drv_context); if (NULL != vsif) { if (is_valid_vsif(dev_context, vsif)) { hw_context->current_vsif = *vsif; hw_context->valid_vsif = 1; mode_change = true; } } if (NULL != avif) { if (is_valid_avi_info_frame(dev_context, avif)) { hw_context->current_avi_info_frame = *avif; mode_change = true; } } if (mode_change) { int cstat_p3; int bits_of_interest; cstat_p3 = mhl_tx_read_reg(hw_context, REG_TMDS_CSTAT_P3); bits_of_interest = cstat_p3 & (BIT_TMDS_CSTAT_P3_SCDT | BIT_TMDS_CSTAT_P3_CKDT); if ((BIT_TMDS_CSTAT_P3_SCDT | VAL_TMDS_CSTAT_P3_CKDT_DETECTED) == bits_of_interest) { start_video(hw_context); } } } #define dump_edid_fifo(hw_context, block_number) /* do nothing */ #define RX_DPD_BITS (BIT_DPD_PDNRX12 \ | BIT_DPD_PDIDCK_N \ | BIT_DPD_PD_MHL_CLK_N) static int init_rx_regs(struct drv_hw_context *hw_context) { /* power up the RX */ mhl_tx_modify_reg(hw_context, REG_DPD, RX_DPD_BITS, RX_DPD_BITS); /* TODO: add to PR. Default for 2A4 is 0x0f */ mhl_tx_write_reg(hw_context, REG_RX_HDMI_CTRL3, 0x00); /* Before exposing the EDID upstream, setup to drop all packets. This ensures we do not get Packet Overflow interrupt. We still get the AVIF interrupts which is crucial. Packet filters must be disabled until after TMDS is enabled. */ mhl_tx_write_reg(hw_context, REG_PKT_FILTER_0, 0xFF); mhl_tx_write_reg(hw_context, REG_PKT_FILTER_1, 0xFF); mhl_tx_write_reg(hw_context, REG_ALICE0_BW_I2C, 0x06); mhl_tx_modify_reg(hw_context, REG_RX_HDMI_CLR_BUFFER, BIT_RX_HDMI_CLR_BUFFER_VSI_CLR_EN, VAL_RX_HDMI_CLR_BUFFER_VSI_CLR_EN_CLEAR); return 0; } #ifdef USE_HW_TIMER static void start_hw_timer(struct drv_hw_context *hw_context) { MHL_TX_DBG_INFO("Start HW Timer.\n"); mhl_tx_write_reg(hw_context, REG_SYS_CTRL3, BIT_SYS_CTRL3_SYS_CNTR); } static void stop_hw_timer(struct drv_hw_context *hw_context) { MHL_TX_DBG_INFO("Stop HW Timer.\n"); mhl_tx_write_reg(hw_context, REG_SYS_CTRL3, 0x00); } /* Time (ms) Reg value 5000 2710 3000 1770 2000 0FA0 1500 0BB8 */ static void setup_hw_timer(struct drv_hw_context *hw_context, uint16_t time_ms) { MHL_TX_DBG_INFO("Setup HW Timer for %dms.\n", time_ms); /* Max time is 32,767ms. */ time_ms &= 0x7FFF; /* Divide by 0.5 for register value. */ time_ms <<= 1; mhl_tx_write_reg(hw_context, REG_SYS_CNTR_0, (uint8_t) (time_ms & 0x00FF)); mhl_tx_write_reg(hw_context, REG_SYS_CNTR_1, (uint8_t) ((time_ms >> 8) & 0x00FF)); } #endif /* This function is exported from the driver */ int si_mhl_tx_drv_set_display_mode(struct mhl_dev_context *dev_context, enum hpd_high_callback_status status) { struct drv_hw_context *hw_context; hw_context = (struct drv_hw_context *)&dev_context->drv_context; hw_context->hpd_high_callback_status = status; /* did the client begin sending video? */ if (status >= 0) { switch (status) { case HH_FMT_DVI_HDCP_ON: case HH_FMT_DVI: case HH_FMT_DVI_HDCP_ON_NOT_RPT: case HH_FMT_DVI_NOT_RPT: /* * output video here for DVI */ start_video(hw_context); break; case HH_FMT_HDMI_VSIF_NONE_HDCP_ON: case HH_FMT_HDMI_VSIF_NONE: case HH_FMT_HDMI_VSIF_NONE_HDCP_ON_NOT_RPT: case HH_FMT_HDMI_VSIF_NONE_NOT_RPT: process_info_frame_change(hw_context, NULL, &hw_context-> avif_or_dtd_from_callback. avif); break; case HH_FMT_HDMI_VSIF_MHL3_HDCP_ON: case HH_FMT_HDMI_VSIF_MHL3: case HH_FMT_HDMI_VSIF_MHL3_HDCP_ON_NOT_RPT: case HH_FMT_HDMI_VSIF_MHL3_NOT_RPT: #ifdef MANUAL_INFO_FRAME_CLEAR_AT_HPD_DRIVEN_HIGH hw_context->valid_vsif = true; #endif /* set_hdmi_params uses MHL3 vsif in the hw_context */ process_info_frame_change(hw_context, NULL, &hw_context-> avif_or_dtd_from_callback. avif); break; case HH_FMT_HDMI_VSIF_HDMI_HDCP_ON: case HH_FMT_HDMI_VSIF_HDMI: case HH_FMT_HDMI_VSIF_HDMI_HDCP_ON_NOT_RPT: case HH_FMT_HDMI_VSIF_HDMI_NOT_RPT: #ifdef MANUAL_INFO_FRAME_CLEAR_AT_HPD_DRIVEN_HIGH hw_context->valid_vsif = true; #endif process_info_frame_change(hw_context, &hw_context->vsif_mhl3_or_hdmi_from_callback, &hw_context->avif_or_dtd_from_callback. avif); break; default: /* other values are not applicable */ break; } } return 0; } static void do_hpd_driven_high_callback(struct drv_hw_context *hw_context, uint8_t *edid, uint16_t length) { int status; struct mhl_dev_context *dev_context = container_of((void *)hw_context, struct mhl_dev_context, drv_context); struct edid_3d_data_t *p_edid_data = dev_context->edid_parser_context; status = hw_context->callbacks.hpd_driven_high( hw_context->callbacks.context, edid, length, p_edid_data->p_edid_emsc, p_edid_data->num_edid_emsc_blocks * EDID_BLOCK_SIZE, p_edid_data->hev_dtd_list, p_edid_data->hev_dtd_info.num_items, p_edid_data->hev_vic_list, p_edid_data->hev_vic_info.num_items, p_edid_data->_3d_dtd_list, p_edid_data->_3d_dtd_info.num_items, p_edid_data->_3d_vic_list, p_edid_data->_3d_vic_info.num_items, &hw_context->avif_or_dtd_from_callback, sizeof(hw_context->avif_or_dtd_from_callback), &hw_context->vsif_mhl3_or_hdmi_from_callback, sizeof(hw_context-> vsif_mhl3_or_hdmi_from_callback) ); si_mhl_tx_drv_set_display_mode(dev_context, status); } #define BIT_0_DISABLED 0x01 #define BIT_0_HIGH 0x02 #define BIT_1_DISABLED 0x04 #define BIT_1_HIGH 0x08 #define BITS_GPIO_01_HPD_HIGH (BIT_0_HIGH | BIT_1_HIGH) #define BITS_GPIO_01_HPD_LOW 0 #define BITS_HPD_CTRL_OPEN_DRAIN_HIGH (BITS_GPIO_01_HPD_HIGH | 0x70) #define BITS_HPD_CTRL_PUSH_PULL_HIGH (BITS_GPIO_01_HPD_HIGH | 0x30) #define BITS_HPD_CTRL_OPEN_DRAIN_LOW (BITS_GPIO_01_HPD_LOW | 0x50) #define BITS_HPD_CTRL_PUSH_PULL_LOW (BITS_GPIO_01_HPD_LOW | 0x10) /* * drive_hpd_high -- sets upstream HPD high * returns the value of REG_TMDS_CSTAT_P3 */ static int drive_hpd_high(struct drv_hw_context *hw_context, uint8_t *edid, uint16_t length) { enum hpd_control_mode mode; int ret_val = -1; int cstat_p3; mode = platform_get_hpd_control_mode(); /* sample REG_TMDS_CSTAT_P3 before driving upstream HDP high */ cstat_p3 = mhl_tx_read_reg(hw_context, REG_TMDS_CSTAT_P3); /* disable auto-clear */ cstat_p3 |= BIT_TMDS_CSTAT_P3_DISABLE_AUTO_AVIF_CLEAR; #ifdef MANUAL_INFO_FRAME_CLEAR_AT_HPD_DRIVEN_HIGH cstat_p3 |= BIT_TMDS_CSTAT_P3_AVIF_MANUAL_CLEAR_STROBE; #endif mhl_tx_write_reg(hw_context, REG_TMDS_CSTAT_P3, cstat_p3); if (HPD_CTRL_OPEN_DRAIN == mode) ret_val = mhl_tx_write_reg(hw_context, REG_HPD_CTRL, BITS_HPD_CTRL_OPEN_DRAIN_HIGH | (IN_MHL3_MODE(hw_context) ? BIT_HPD_CTRL_HPD_DS_SIGNAL : 0)); else if (HPD_CTRL_PUSH_PULL == mode) ret_val = mhl_tx_write_reg(hw_context, REG_HPD_CTRL, BITS_HPD_CTRL_PUSH_PULL_HIGH | (IN_MHL3_MODE(hw_context) ? BIT_HPD_CTRL_HPD_DS_SIGNAL : 0)); if (edid) { MHL_TX_DBG_INFO("\n") do_hpd_driven_high_callback(hw_context, edid, length); } if (ret_val >= 0) return cstat_p3; return ret_val; } static int drive_hpd_low(struct drv_hw_context *hw_context) { enum hpd_control_mode mode; int ret_val = -1; ddc_abort_count = 0; mode = platform_get_hpd_control_mode(); mhl_tx_modify_reg(hw_context, REG_EDID_CTRL, BIT_EDID_CTRL_EDID_PRIME_VALID, VAL_EDID_CTRL_EDID_PRIME_VALID_DISABLE); if (HPD_CTRL_OPEN_DRAIN == mode) ret_val = mhl_tx_write_reg(hw_context, REG_HPD_CTRL, BITS_HPD_CTRL_OPEN_DRAIN_LOW | (IN_MHL3_MODE(hw_context) ? BIT_HPD_CTRL_HPD_DS_SIGNAL : 0)); else if (HPD_CTRL_PUSH_PULL == mode) ret_val = mhl_tx_write_reg(hw_context, REG_HPD_CTRL, BITS_HPD_CTRL_PUSH_PULL_LOW | (IN_MHL3_MODE(hw_context) ? BIT_HPD_CTRL_HPD_DS_SIGNAL : 0)); enable_intr(hw_context, INTR_INFR, 0x00); hw_context->hpd_high_callback_status = HH_VIDEO_NOT_RDY; hw_context->callbacks.hpd_driven_low(hw_context->callbacks.context); return ret_val; } #ifdef SWWA_BZ30759 /* we are done with the EDID for now. We now expect to start doing HDCP, which * can destroy the contents of our EDID buffer, so do another EDID read, which * we know will fail, but that will reset the DDC fifo in such a way as to * leave the buffer contents alone */ void edid_hw_sm_clean_up(struct drv_hw_context *hw_context) { mhl_tx_write_reg(hw_context, REG_PAGE_8_HDCP1X_LB_BIST, BIT_PAGE_8_HDCP1X_LB_BIST_HDCP1X_LB_BIST_EN); mhl_tx_write_reg(hw_context, REG_DDC_MANUAL, BIT_DDC_MANUAL_MAN_DDC); mhl_tx_write_reg(hw_context, REG_INTR9, 0xFF); /* Disable EDID interrupt */ enable_intr(hw_context, INTR_EDID, 0); /* Trigger EDID to generate an error to reset state machine */ mhl_tx_write_reg(hw_context, REG_TPI_CBUS_START, BIT_TPI_CBUS_START_GET_EDID_START_0); mhl_tx_write_reg(hw_context, REG_INTR9, 0xFF); mhl_tx_write_reg(hw_context, REG_DDC_MANUAL, 0x00); mhl_tx_write_reg(hw_context, REG_PAGE_8_HDCP1X_LB_BIST, 0x00); } #endif int si_mhl_tx_drv_set_upstream_edid(struct drv_hw_context *hw_context, uint8_t *edid, uint16_t length) { uint8_t reg_val; if (MHL_SEND_3D_REQ_OR_FEAT_REQ == hw_context->current_cbus_req.command) { struct mhl_dev_context *dev_context; dev_context = container_of((void *)hw_context, struct mhl_dev_context, drv_context); MHL_TX_DBG_WARN("3D_REQ or FEAT_REQ completed\n"); hw_context->current_cbus_req.command = 0x00; si_mhl_tx_msc_command_done(dev_context, 0x00); } if (!ok_to_proceed_with_ddc(hw_context)) return -1; MHL_TX_DBG_INFO("presenting EDID upstream\n"); init_rx_regs(hw_context); #ifdef SWWA_BZ30759 /* we are done with the EDID for now. We now expect to start doing * HDCP, which can destroy the contents of our EDID buffer, so do * another EDID read, which we know will fail, but that will reset the * DDC fifo in such a way as to leave the buffer contents alone */ edid_hw_sm_clean_up(hw_context); #endif /* choose EDID instead of devcap to appear at the FIFO */ mhl_tx_write_reg(hw_context, REG_EDID_CTRL, VAL_EDID_CTRL_EDID_PRIME_VALID_DISABLE | VAL_EDID_CTRL_DEVCAP_SELECT_EDID | VAL_EDID_CTRL_EDID_FIFO_ADDR_AUTO_ENABLE | VAL_EDID_CTRL_EDID_MODE_EN_ENABLE); mhl_tx_write_reg(hw_context, REG_EDID_FIFO_ADDR, 0); mhl_tx_write_reg_block(hw_context, REG_EDID_FIFO_WR_DATA, length, edid); mhl_tx_write_reg(hw_context, REG_EDID_CTRL, VAL_EDID_CTRL_EDID_PRIME_VALID_ENABLE | VAL_EDID_CTRL_DEVCAP_SELECT_EDID | VAL_EDID_CTRL_EDID_FIFO_ADDR_AUTO_ENABLE | VAL_EDID_CTRL_EDID_MODE_EN_ENABLE); /* Enable SCDT interrupt to detect stable incoming clock */ enable_intr(hw_context, INTR_SCDT, BIT_INTR_SCDT_CHANGE); /* Disable EDID interrupt */ enable_intr(hw_context, INTR_EDID, 0); #ifndef EARLY_HSIC if (IN_MHL3_MODE(hw_context)) { /* * todo hsic_init configures the transmitter for USB host mode. * This call should be deferred until the driver has negotiated * with the sink to take over the host role. * The call is placed here for test purposes. */ hsic_init(hw_context); } #endif MHL_TX_DBG_ERR("Expose EDID\n"); /* HPD was held low all this time. Now we send an HPD high */ reg_val = drive_hpd_high(hw_context, edid, length); /* If SCDT is already high, then we will not get an interrupt */ if (BIT_TMDS_CSTAT_P3_SCDT & reg_val) { MHL_TX_DBG_INFO("SCDT status is already HIGH. " "Simulate int_5: %s0x%02x%s\n", ANSI_ESC_YELLOW_TEXT, mhl_tx_read_reg(hw_context, REG_RX_HDMI_CTRL0), ANSI_ESC_RESET_TEXT); int_5_isr(hw_context, BIT_INTR_SCDT_CHANGE); } return 0; } static void hsic_init(struct drv_hw_context *hw_context) { MHL_TX_DBG_INFO("Initialize USB Tunneling\n"); /* Enable USB flow control and select host mode */ mhl_tx_write_reg(hw_context, REG_FCGC, 0x03); /* * strobe_stay_then_reset * bit0: host=1, device=0 * bit1: status_en */ mhl_tx_modify_reg(hw_context, REG_HRXCTRL3, BIT_HRXCTRL3_HRX_STAY_RESET | BIT_HRXCTRL3_STATUS_EN, BIT_HRXCTRL3_HRX_STAY_RESET | BIT_HRXCTRL3_STATUS_EN); /* tdm_tx_num_of_bits_per_symbol bit[2:0]: 4=8-bit */ mhl_tx_modify_reg(hw_context, REG_TTXNUMB, MSK_TTXNUMB_TTX_NUMBPS_2_0, 4); /* tdm_rx_from_se_coc bit3:doc=0, coc=1 */ mhl_tx_modify_reg(hw_context, REG_TRXCTRL, 0x08, 0x08); /* hsic_tx_respect_tdm_evn_if_rx_busy bit2:host=0, device=1 */ mhl_tx_modify_reg(hw_context, REG_HTXCTRL, 0x02, 0x00); /* keeper_mode bit[1:0]:host=0, device=2 */ mhl_tx_modify_reg(hw_context, REG_KEEPER, 0x03, 0x00); mhl_tx_write_reg(hw_context, REG_TDMLLCTL, 0x00); /* Reset USB Tunneling module: hsic_rx/hsic_tx/hsic_fc/keeper */ mhl_tx_write_reg(hw_context, REG_UTSRST, BIT_UTSRST_HRX_SRST | BIT_UTSRST_HTX_SRST | BIT_UTSRST_KEEPER_SRST | BIT_UTSRST_FC_SRST); mhl_tx_write_reg(hw_context, REG_UTSRST, BIT_UTSRST_HRX_SRST | BIT_UTSRST_HTX_SRST); /* Interrupt clear * todo Not sure these are necessary. Need to look into removing * them once HSIC is working. */ mhl_tx_write_reg(hw_context, REG_HRXINTL, 0xFF); /* hsic_rx */ mhl_tx_write_reg(hw_context, REG_HRXINTH, 0xFF); /* hsic_rx */ mhl_tx_write_reg(hw_context, REG_TTXINTL, 0xFF); /* tdm_tx */ mhl_tx_write_reg(hw_context, REG_TTXINTH, 0xFF); /* tdm_tx */ mhl_tx_write_reg(hw_context, REG_TRXINTL, 0xFF); /* tdm_rx */ mhl_tx_write_reg(hw_context, REG_TRXINTH, 0xFF); /* tdm_rx */ mhl_tx_write_reg(hw_context, REG_HTXINTL, 0xFF); /* hsic_tx */ mhl_tx_write_reg(hw_context, REG_HTXINTH, 0xFF); /* hsic_tx */ mhl_tx_write_reg(hw_context, REG_FCINTR0, 0xFF); /* hsic_fc */ mhl_tx_write_reg(hw_context, REG_FCINTR1, 0xFF); /* hsic_fc */ mhl_tx_write_reg(hw_context, REG_FCINTR2, 0xFF); /* hsic_fc */ mhl_tx_write_reg(hw_context, REG_FCINTR3, 0xFF); /* hsic_fc */ mhl_tx_write_reg(hw_context, REG_FCINTR4, 0xFF); /* hsic_fc */ mhl_tx_write_reg(hw_context, REG_FCINTR5, 0xFF); /* hsic_fc */ mhl_tx_write_reg(hw_context, REG_FCINTR6, 0xFF); /* hsic_fc */ mhl_tx_write_reg(hw_context, REG_FCINTR7, 0xFF); /* hsic_fc */ } #define MHL_LOGICAL_DEVICE_MAP (MHL_DEV_LD_AUDIO | MHL_DEV_LD_VIDEO | \ MHL_DEV_LD_MEDIA | MHL_DEV_LD_GUI) #define DEVCAP_REG(x) (REG_MHL_DEVCAP_0 | DEVCAP_OFFSET_##x) #define XDEVCAP_REG(x) (REG_MHL_EXTDEVCAP_0 | \ XDEVCAP_OFFSET(XDEVCAP_ADDR_##x)) /* Local devcap values. Populated at init_regs time from si_app_devcap.h */ uint8_t dev_cap_values[16] = { DEVCAP_VAL_DEV_STATE, DEVCAP_VAL_MHL_VERSION, DEVCAP_VAL_DEV_CAT, DEVCAP_VAL_ADOPTER_ID_H, DEVCAP_VAL_ADOPTER_ID_L, DEVCAP_VAL_VID_LINK_MODE, DEVCAP_VAL_AUD_LINK_MODE, DEVCAP_VAL_VIDEO_TYPE, DEVCAP_VAL_LOG_DEV_MAP, DEVCAP_VAL_BANDWIDTH, DEVCAP_VAL_FEATURE_FLAG, 0, 0, DEVCAP_VAL_SCRATCHPAD_SIZE, DEVCAP_VAL_INT_STAT_SIZE, DEVCAP_VAL_RESERVED }; /* Local xdevcap values. Populated at init_regs time from si_app_devcap.h */ uint8_t xdev_cap_values[4] = { XDEVCAP_VAL_ECBUS_SPEEDS, XDEVCAP_VAL_TMDS_SPEEDS, XDEVCAP_VAL_DEV_ROLES, XDEVCAP_VAL_LOG_DEV_MAPX }; static void mhl3_specific_init(struct drv_hw_context *hw_context) { /* Even in MHL3 mode, TPI:1A[0] controls DVI vs. HDMI */ mhl_tx_write_reg(hw_context, REG_SYS_CTRL1, BIT_SYS_CTRL1_BLOCK_DDC_BY_HPD | BIT_SYS_CTRL1_TX_CONTROL_HDMI); enable_intr(hw_context, INTR_LINK_TRAINING, BIT_EMSCINTR1_EMSC_TRAINING_COMMA_ERR); } static int init_regs(struct drv_hw_context *hw_context) { int ret_val = 0; MHL_TX_DBG_INFO("called\n"); /* disable and clear uninteresting interrupts for MHL 1/2 */ enable_intr(hw_context, INTR_HDCP2, 0x00); enable_intr(hw_context, INTR_LINK_TRAINING, 0x00); mhl_tx_write_reg(hw_context, REG_HDCP2X_INTR0, 0xFF); mhl_tx_write_reg(hw_context, REG_INTR1, 0xFF); mhl_tx_write_reg(hw_context, REG_SYS_CTRL1, BIT_SYS_CTRL1_BLOCK_DDC_BY_HPD | BIT_SYS_CTRL1_TX_CONTROL_HDMI); /* default values for flags */ hw_context->video_ready = false; hw_context->video_path = 1; hw_context->rx_hdmi_ctrl2_defval = REG_RX_HDMI_CTRL2_DEFVAL_DVI; /* Drop the Hot Plug to upstream and hide EDID */ drive_hpd_low(hw_context); mhl_tx_write_reg(hw_context, REG_EDID_CTRL, VAL_EDID_CTRL_EDID_FIFO_ADDR_AUTO_ENABLE); /* * DO NOT enable wake pulses or discovery pulses until RGND == 1K * No OTG, */ /* this is extraneous here, this register is properly set elsewhere */ mhl_tx_write_reg(hw_context, REG_DISC_CTRL9, BIT_DISC_CTRL9_WAKE_DRVFLT | BIT_DISC_CTRL9_WAKE_PULSE_BYPASS); mhl_tx_write_reg(hw_context, REG_TMDS0_CCTRL1, 0x90); /* Enable TxPLL Clock */ mhl_tx_write_reg(hw_context, REG_TMDS_CLK_EN, 0x01); /* Enable Tx Clock Path & Equalizer */ mhl_tx_write_reg(hw_context, REG_TMDS_CH_EN, 0x11); mhl_tx_write_reg(hw_context, REG_BGR_BIAS, 0x87); mhl_tx_write_reg(hw_context, REG_ALICE0_ZONE_CTRL, 0xE8); mhl_tx_write_reg(hw_context, REG_ALICE0_MODE_CTRL, 0x04); /* Enable TPI */ ret_val = mhl_tx_read_reg(hw_context, REG_LM_DDC); ret_val &= ~BIT_LM_DDC_SW_TPI_EN; ret_val |= VAL_LM_DDC_SW_TPI_EN_ENABLED; mhl_tx_write_reg(hw_context, REG_LM_DDC, ret_val); /* mhl_tx_write_reg(hw_context, REG_TPI_COPP_DATA2, VAL_TPI_COPP_PROTLEVEL_MIN);*/ /* * TODO: TPI:0xBB has bad default. Need to document in the PR * If this initialization is not changed, we have unstable HDCP * (while testing 9612 as source) */ mhl_tx_write_reg(hw_context, REG_TPI_HW_OPT3, 0x76); /* TMDS should be enabled for both MHL1/2 and MHL3 cases. * MHL3 needs it for CoC calibration. */ mhl_tx_write_reg(hw_context, REG_TMDS_CCTRL, BIT_TMDS_CCTRL_TMDS_OE); /* set time base for one second to be 60Hz/4*5 + 4 */ mhl_tx_write_reg(hw_context, REG_TPI_DTD_B2, 79); /* setup local DEVCAP and few more CBUS registers. */ /* * Fill-in DEVCAP device ID values with those read * from the transmitter. */ dev_cap_values[DEVCAP_OFFSET_DEVICE_ID_L] = (uint8_t) hw_context->chip_device_id; dev_cap_values[DEVCAP_OFFSET_DEVICE_ID_H] = (uint8_t) (hw_context->chip_device_id >> 8); /* Setup local DEVCAP registers */ mhl_tx_write_reg_block(hw_context, DEVCAP_REG(DEV_STATE), ARRAY_SIZE(dev_cap_values), dev_cap_values); /* * Adjust XDEVCAP values to match capabilities of the transmitter. */ xdev_cap_values[XDEVCAP_OFFSET(XDEVCAP_ADDR_ECBUS_SPEEDS)] = MHL_XDC_ECBUS_S_075 | MHL_XDC_ECBUS_S_8BIT; if (si_mhl_tx_drv_support_e_cbus_d(hw_context)) xdev_cap_values[XDEVCAP_OFFSET(XDEVCAP_ADDR_ECBUS_SPEEDS)] |= MHL_XDC_ECBUS_D_150 | MHL_XDC_ECBUS_D_8BIT; mhl_tx_write_reg_block(hw_context, XDEVCAP_REG(ECBUS_SPEEDS), ARRAY_SIZE(xdev_cap_values), xdev_cap_values); /* * Make sure MDT registers are initialized and the MDT * transmit/receive are both disabled. */ mhl_tx_write_reg(hw_context, REG_MDT_XMIT_TIMEOUT, 100); /* Clear transmit FIFOs and make sure MDT transmit is disabled */ mhl_tx_write_reg(hw_context, REG_MDT_XMIT_CONTROL, 0x03); /* Disable MDT transmit preemptive handshake option */ mhl_tx_write_reg(hw_context, REG_MDT_XFIFO_STAT, 0x00); mhl_tx_write_reg(hw_context, REG_MDT_RCV_TIMEOUT, 100); /* 2013-03-01 bugzilla 27180 */ mhl_tx_write_reg(hw_context, REG_CBUS_LINK_CONTROL_8, 0x1D); si_mhl_tx_drv_start_gen2_write_burst(hw_context); mhl_tx_write_reg(hw_context, REG_BIST_CTRL, 0x00); /* Setup MHL3 specs parameters in CoC registers */ mhl_tx_write_reg(hw_context, REG_COC_CTL1, 0x10); mhl_tx_write_reg(hw_context, REG_COC_CTL2, 0x18); mhl_tx_write_reg(hw_context, REG_COC_CTLF, 0x07); /* set eCBUS BIST hw duration to infinite 7:4 == 0xF */ mhl_tx_write_reg(hw_context, REG_COC_CTL11, 0xF8); mhl_tx_write_reg(hw_context, REG_COC_CTL17, 0x61); mhl_tx_write_reg(hw_context, REG_COC_CTL18, 0x46); mhl_tx_write_reg(hw_context, REG_COC_CTL19, 0x15); mhl_tx_write_reg(hw_context, REG_COC_CTL1A, 0x01); mhl_tx_write_reg(hw_context, REG_MHL_COC_CTL3, BIT_MHL_COC_CTL3_COC_AECHO_EN); mhl_tx_write_reg(hw_context, REG_MHL_COC_CTL4, 0x2D); mhl_tx_write_reg(hw_context, REG_MHL_COC_CTL5, 0xF9); disable_heartbeat(hw_context); disable_gen2_write_burst_rcv(hw_context); disable_gen2_write_burst_xmit(hw_context); return ret_val; } static void si_mhl_tx_drv_start_gen2_write_burst(struct drv_hw_context *hw_context) { enable_intr(hw_context, INTR_G2WB_ERR, BIT_MDT_RCV_TIMEOUT | BIT_MDT_RCV_SM_ABORT_PKT_RCVD | BIT_MDT_RCV_SM_ERROR | BIT_MDT_XMIT_TIMEOUT | BIT_MDT_XMIT_SM_ABORT_PKT_RCVD | BIT_MDT_XMIT_SM_ERROR); enable_intr(hw_context, INTR_G2WB, BIT_MDT_XFIFO_EMPTY | BIT_MDT_IDLE_AFTER_HAWB_DISABLE | BIT_MDT_RFIFO_DATA_RDY); /* Want HAWB RCV on at all times except * when sending a non-WB transaction */ hw_context->cbus1_state = CBUS1_IDLE_RCV_ENABLED; hw_context->delayed_hawb_enable_reg_val = 0; enable_gen2_write_burst_rcv(hw_context); } void si_mhl_tx_drv_disable_video_path(struct drv_hw_context *hw_context) { /* If video was already being output */ if (hw_context->video_ready && (0 == (VAL_TPI_SC_TPI_AV_MUTE_MUTED & mhl_tx_read_reg(hw_context, REG_TPI_SC)))) { /* stop hdcp and video and remember */ stop_video(hw_context); hw_context->video_path = 0; } } void si_mhl_tx_drv_enable_video_path(struct drv_hw_context *hw_context) { uint8_t mask = (BIT_TPI_SC_REG_TMDS_OE | BIT_TPI_SC_TPI_AV_MUTE); uint8_t reg; /* if a path_en = 0 had stopped the video, * restart it unless done already. */ if (hw_context->video_ready && (0 == hw_context->video_path)) { /* remember ds has enabled our path */ hw_context->video_path = 1; reg = mhl_tx_read_reg(hw_context, REG_TPI_SC); if (mask == (mask & reg)) start_video(hw_context); } } void si_mhl_tx_drv_content_off(struct drv_hw_context *hw_context) { MHL_TX_DBG_INFO("RAP CONTENT_OFF video %sready\n", hw_context->video_ready ? "" : "NOT "); /* If video was already being output */ if (hw_context->video_ready && (0 == (VAL_TPI_SC_TPI_AV_MUTE_MUTED & mhl_tx_read_reg(hw_context, REG_TPI_SC)))) { MHL_TX_DBG_INFO("RAP CONTENT_OFF\n"); /* stop hdcp and video and remember */ stop_video(hw_context); } } void si_mhl_tx_drv_content_on(struct drv_hw_context *hw_context) { uint8_t mask = (BIT_TPI_SC_REG_TMDS_OE | BIT_TPI_SC_TPI_AV_MUTE); uint8_t reg; /* if a path_en = 0 had stopped the video, * restart it unless done already. */ if (hw_context->video_ready) { reg = mhl_tx_read_reg(hw_context, REG_TPI_SC); if (mask == (mask & reg)) start_video(hw_context); } } static void unmute_video(struct drv_hw_context *hw_context) { MHL_TX_DBG_INFO("AV unmuted\n"); if (!IN_MHL3_MODE(hw_context)) { if (hdcp_content_type == 0) { if (si_edid_sink_is_hdmi (hw_context->intr_info->edid_parser_context)) { mhl_tx_write_reg(hw_context, REG_TPI_SC, VAL_TPI_SC_TPI_OUTPUT_MODE_0_HDMI); } else { mhl_tx_write_reg(hw_context, REG_TPI_SC, VAL_TPI_SC_TPI_OUTPUT_MODE_0_DVI); } } else { MHL_TX_DBG_INFO ("HDCP Content Type 1, AV remain muted\n"); } } /* Now we can entertain PATH_EN */ hw_context->video_ready = 1; } /* Sequence of operations in this function is important. 1. Turn off HDCP interrupt 2. Turn of TMDS output 3. Turn off HDCP engine/encryption 4. Clear any leftover HDCP interrupt */ #define OUTPUT_OFF_HDMI (VAL_TPI_SC_REG_TMDS_OE_POWER_DOWN | \ VAL_TPI_SC_TPI_AV_MUTE_MUTED | \ VAL_TPI_SC_TPI_OUTPUT_MODE_0_HDMI) #define OUTPUT_OFF_DVI (VAL_TPI_SC_REG_TMDS_OE_POWER_DOWN | \ VAL_TPI_SC_TPI_AV_MUTE_MUTED | \ VAL_TPI_SC_TPI_OUTPUT_MODE_0_DVI) static void stop_video(struct drv_hw_context *hw_context) { if (IN_MHL3_MODE(hw_context)) { MHL_TX_DBG_WARN("for >= MHL3.0\n"); /* disable TMDS early */ mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL0, 0xC0); /* reset H2M here */ mhl_tx_write_reg(hw_context, REG_M3_CTRL, VAL_M3_CTRL_MHL3_VALUE | BIT_M3_CTRL_H2M_SWRST); si_mhl_tx_drv_shut_down_HDCP2(hw_context); } else { /* MHL 2/1 with HDCP 1.x */ MHL_TX_DBG_WARN(" for MHL1/2.x\n"); /* Turn off HDCP interrupt */ enable_intr(hw_context, INTR_HDCP, 0x00); enable_intr(hw_context, INTR_HDCP2, 0x00); /* stop hdcp engine */ mhl_tx_write_reg(hw_context, REG_TPI_COPP_DATA2, 0); /* clear any leftover hdcp interrupt */ mhl_tx_write_reg(hw_context, g_intr_tbl[INTR_HDCP].stat_addr, 0xFF); MHL_TX_DBG_WARN("hw_context = %x; ->intr_info = %x\n", hw_context, hw_context->intr_info); if (NULL != hw_context->intr_info) { if (NULL != hw_context->intr_info->edid_parser_context) { /* We must maintain the output bit (bit 0) to * allow just one bit change later when BKSV * read is triggered. This programming is * required for HDCP CTS 1.x to pass */ if (si_edid_sink_is_hdmi( hw_context->intr_info-> edid_parser_context)) { mhl_tx_write_reg(hw_context, REG_TPI_SC, OUTPUT_OFF_HDMI); } else { mhl_tx_write_reg(hw_context, REG_TPI_SC, OUTPUT_OFF_DVI); } } } } } void si_mhl_tx_drv_start_cp(struct mhl_dev_context *dev_context) { /* if hdcp is already running, shut it down here and * wait for the next "NOT normal"/"normal" message pair from the sink. */ struct drv_hw_context *hw_context = (struct drv_hw_context *)(&dev_context->drv_context); if (hw_context->hdcp2_started) { MHL_TX_DBG_ERR("%salready started%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); si_mhl_tx_drv_shut_down_HDCP2(hw_context); } else start_hdcp(hw_context); } static void start_hdcp(struct drv_hw_context *hw_context) { if (IN_MHL3_MODE(hw_context)) { MHL_TX_DBG_ERR("Start HDCP 2.2\n"); start_hdcp_content_type(hw_context); /* Unmask HDCP2 INTs */ enable_intr(hw_context, INTR_HDCP2, BIT_HDCP2_INTR_AUTH_DONE | BIT_HDCP2_INTR_AUTH_FAIL | BIT_HDCP2_INTR_RPTR_RCVID_CHANGE); /* Enable HDCP 2.2 */ mhl_tx_modify_reg(hw_context, REG_M3_P0CTRL, BIT_M3_P0CTRL_MHL3_P0_HDCP_EN, BIT_M3_P0CTRL_MHL3_P0_HDCP_EN); /* enable encryption */ mhl_tx_write_reg(hw_context, REG_HDCP2X_CTRL_0, 0x83); /* Enable HDCP2 DDC polling */ hw_context->hdcp2_started = true; mhl_tx_write_reg(hw_context, REG_HDCP2X_POLL_CS, 0x70); mhl_tx_write_reg(hw_context, REG_HDCP2X_RPT_SMNG_K, 1); mhl_tx_write_reg(hw_context, REG_HDCP2X_CTRL_1, 0x01); mhl_tx_write_reg(hw_context, REG_HDCP2X_CTRL_1, 0x00); } else { /* First stop hdcp and video */ stop_video(hw_context); /* Enable HDCP interrupts * Clear interrupt first. */ MHL_TX_DBG_ERR("Start HDCP 1.x\n"); mhl_tx_write_reg(hw_context, g_intr_tbl[INTR_HDCP].stat_addr, 0xFF); if (ok_to_proceed_with_ddc(hw_context)) { /* Enable HDCP interrupt */ enable_intr(hw_context, INTR_HDCP, (BIT_TPI_INTR_ST0_TPI_AUTH_CHNGE_STAT | BIT_TPI_INTR_ST0_TPI_COPP_CHNGE_STAT | /*#define KSV_FIFO_RDY_INTERRUPT*/ #ifdef KSV_FIFO_RDY_INTERRUPT BIT_TPI_INTR_ST0_KSV_FIFO_FIRST_STAT | #endif BIT_TPI_INTR_ST0_READ_BKSV_BCAPS_DONE_STAT | BIT_TPI_INTR_ST0_READ_BKSV_ERR_STAT)); msleep(250); /* * Chip requires only bit 4 to change for BKSV read * No other bit should change. */ mhl_tx_modify_reg(hw_context, REG_TPI_SC, BIT_TPI_SC_REG_TMDS_OE, VAL_TPI_SC_REG_TMDS_OE_ACTIVE); } } } static void start_hdcp_content_type(struct drv_hw_context *hw_context) { uint8_t misc_ctrl; uint8_t index; uint8_t msg[2] = {MHL_STREAM_ID, CONTENT_TYPE_DEFAULT}; if (hdcp_content_type == 1) msg[1] = 0x01; MHL_TX_DBG_INFO("HDCP Content Type = %d\n", msg[1]); misc_ctrl = mhl_tx_read_reg(hw_context, REG_HDCP2X_MISC_CTRL); /* * Toggle SMNG_WR_START */ mhl_tx_write_reg(hw_context, REG_HDCP2X_MISC_CTRL, misc_ctrl | BIT_HDCP2X_MISC_CTRL_HDCP2X_RPT_SMNG_WR_START); mhl_tx_write_reg(hw_context, REG_HDCP2X_MISC_CTRL, misc_ctrl); /* Write message */ for (index = 0; index < 2; index++) { mhl_tx_write_reg(hw_context, REG_HDCP2X_RPT_SMNG_IN, msg[index]); mhl_tx_write_reg(hw_context, REG_HDCP2X_MISC_CTRL, misc_ctrl | BIT_HDCP2X_MISC_CTRL_HDCP2X_RPT_SMNG_WR); mhl_tx_write_reg(hw_context, REG_HDCP2X_MISC_CTRL, misc_ctrl); } } /* video_sans_cbus1 */ static void video_sans_cbus1(struct mhl_dev_context *dev_context) { struct drv_hw_context *hw_context = (struct drv_hw_context *)(&dev_context->drv_context); struct bist_setup_info *bist_setup; uint32_t pixel_clock_frequency; bist_setup = &dev_context->bist_setup; pixel_clock_frequency = si_edid_find_pixel_clock_from_AVI_VIC( dev_context->edid_parser_context, bist_setup->avlink_video_mode); mhl_tx_write_reg(hw_context, REG_RX_HDMI_CTRL2, REG_RX_HDMI_CTRL2_DEFVAL_HDMI | VAL_RX_HDMI_CTRL2_VSI_MON_SEL_AVI); mhl_tx_write_reg(hw_context, REG_VID_OVRRD, BIT_VID_OVRRD_PP_AUTO_DISABLE | BIT_VID_OVRRD_M1080P_OVRRD); mhl_tx_modify_reg(hw_context, REG_M3_P0CTRL, BIT_M3_P0CTRL_MHL3_P0_HDCP_EN | BIT_M3_P0CTRL_MHL3_P0_PIXEL_MODE | BIT_M3_P0CTRL_MHL3_P0_PORT_EN, 0x00 | VAL_M3_P0CTRL_MHL3_P0_PIXEL_MODE_NORMAL | BIT_M3_P0CTRL_MHL3_P0_PORT_EN); mhl_tx_write_reg(hw_context, REG_TPI_INPUT, 0x01); mhl_tx_write_reg(hw_context, REG_TPI_OUTPUT, 0x01); mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL0, 0xF0); si_mhl_tx_drv_set_lowest_tmds_link_speed(dev_context, pixel_clock_frequency, 24); /* Assert H2M reset */ mhl_tx_write_reg(hw_context, REG_M3_CTRL, VAL_M3_CTRL_MHL3_VALUE | BIT_M3_CTRL_H2M_SWRST); /* Deassert H2M reset */ mhl_tx_write_reg(hw_context, REG_M3_CTRL, VAL_M3_CTRL_MHL3_VALUE); mhl_tx_write_reg(hw_context, REG_PKT_FILTER_0, 0xA5); mhl_tx_write_reg(hw_context, REG_PKT_FILTER_1, 0x83); enable_intr(hw_context, INTR_HDCP2, 0x00); mhl_tx_write_reg(hw_context, REG_HDCP2X_POLL_CS, 0x71); mhl_tx_write_reg(hw_context, REG_HDCP2X_CTRL_0, 0x02); mhl_tx_write_reg(hw_context, g_intr_tbl[INTR_HDCP2].stat_addr, 0xff); mhl_tx_write_reg(hw_context, REG_TPI_SC, VAL_TPI_SC_TPI_OUTPUT_MODE_0_HDMI); switch (bist_setup->avlink_randomizer) { case 0: mhl_tx_modify_reg(hw_context, REG_M3_SCTRL, BIT_M3_SCTRL_MHL3_SCRAMBLER_EN, 0); break; case 1: mhl_tx_modify_reg(hw_context, REG_M3_SCTRL, BIT_M3_SCTRL_MHL3_SCRAMBLER_EN, BIT_M3_SCTRL_MHL3_SCRAMBLER_EN); break; default: MHL_TX_DBG_ERR("%s incoherent AV_LINK_RANDOMIZER:%d%s\n", ANSI_ESC_RED_TEXT, bist_setup->avlink_randomizer, ANSI_ESC_RESET_TEXT); } switch (bist_setup->avlink_pattern) { case BIST_AVLINK_PATTERN_UNSPECIFIED: case BIST_AVLINK_PATTERN_PRBS: case BIST_AVLINK_PATTERN_FIXED_8: MHL_TX_DBG_ERR("Start Fixed 8/PRBS!\n"); mhl_tx_modify_reg(hw_context, REG_BIST_CTRL, BIT_BIST_START_BIT, BIT_BIST_START_BIT); break; case BIST_AVLINK_PATTERN_FIXED_10: MHL_TX_DBG_ERR("Start Fixed 10!\n"); mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_CNTLSTA, BIT_TX_IP_BIST_CNTLSTA_TXBIST_EN | BIT_TX_IP_BIST_CNTLSTA_TXBIST_SEL); break; default: MHL_TX_DBG_ERR("Unrecognized test pattern\n"); } #ifdef CoC_FSM_MONITORING #ifdef BIST_MONITORING mhl_tx_modify_reg(hw_context, REG_GPIO_CTRL1, BIT_CTRL1_GPIO_I_6, BIT_CTRL1_GPIO_I_6); #endif #endif } /* * start_video * * */ static int start_video(struct drv_hw_context *hw_context) { struct mhl_dev_context *dev_context; dev_context = get_mhl_device_context(hw_context); if (!IN_MHL3_MODE(hw_context)) { /* * stop hdcp and video * this kills any hdcp thread going on already */ stop_video(hw_context); } /* * if path has been disabled by PATH_EN = 0 return with error; * When enabled, this function will be called again. * if downstream connection has been lost (CLR_HPD), return with error. */ if ((0 == hw_context->video_path) || (!ok_to_proceed_with_ddc(hw_context)) || (false == dev_context->misc_flags.flags.rap_content_on) ) { return false; } if ( #ifdef MHL3_DVI_SUPPORT IN_MHL3_MODE(hw_context) || #endif si_edid_sink_is_hdmi( hw_context->intr_info->edid_parser_context)) { /* * setup registers for packed pixel, 3D, colors and AVIF * using incoming info frames. VSIF sent out by the h/w. */ mhl_tx_write_reg(hw_context, REG_RX_HDMI_CTRL2, hw_context->rx_hdmi_ctrl2_defval = REG_RX_HDMI_CTRL2_DEFVAL_HDMI | VAL_RX_HDMI_CTRL2_VSI_MON_SEL_AVI); if (false == set_hdmi_params(dev_context)) { /* Do not disrupt video for bad incoming infoframes */ return false; } if (IN_MHL3_MODE(hw_context)) { /* Assert H2M reset */ mhl_tx_write_reg(hw_context, REG_M3_CTRL, VAL_M3_CTRL_MHL3_VALUE | BIT_M3_CTRL_H2M_SWRST); /* Deassert H2M reset */ mhl_tx_write_reg(hw_context, REG_M3_CTRL, VAL_M3_CTRL_MHL3_VALUE); } else { set_auto_zone_for_mhl_1_2(hw_context); start_hdcp(hw_context); } /* * TODO: Check if the following block has to be performed * again at HDCP restart. * * Start sending out AVIF now. Also ensure other appropriate * InfoFrames are being forwarded or dropped. */ /* * Send infoframe out */ mhl_tx_write_reg_block(hw_context, REG_TPI_AVI_CHSUM, sizeof(hw_context->outgoingAviPayLoad.ifData), hw_context->outgoingAviPayLoad.ifData); /* * Change packet filters to drop AIF and GCP. * TODO: Describe this in the PR appropriately. * TODO: for MHL3 mode, drop VSI packets and convert * HDMI VSIF to MHL3 VSIF. */ mhl_tx_write_reg(hw_context, REG_PKT_FILTER_0, 0xA5); /* Value written does not match comment above per values below * BIT_PKT_FILTER_0_DROP_CEA_GAMUT_PKT (0x80) * BIT_PKT_FILTER_0_DROP_MPEG_PKT (0x20) * BIT_PKT_FILTER_0_DROP_AVI_PKT (0x04) * BIT_PKT_FILTER_0_DROP_GCP_PKT (0x01) */ if (IN_MHL3_MODE(hw_context)) { enum info_sel_e { info_sel_avi = 0, info_sel_spd, info_sel_audio, info_sel_mpeg, info_sel_generic, info_sel_generic2, info_sel_vsi, info_sel_reserved }; uint8_t vsif_buffer[31]; /* * disable HDMI to MHL VSIF conversion (do it in * software) (set bit 7)and drop generic infoframes * (set bit 1) */ mhl_tx_write_reg(hw_context, REG_PKT_FILTER_1, BIT_PKT_FILTER_1_VSI_OVERRIDE_DIS | BIT_PKT_FILTER_1_DROP_GEN_PKT | BIT_PKT_FILTER_1_DROP_VSIF_PKT); mhl_tx_write_reg(hw_context, REG_TPI_INFO_FSEL, BIT_TPI_INFO_FSEL_TPI_INFO_EN | BIT_TPI_INFO_FSEL_TPI_INFO_RPT | info_sel_vsi); /* hardware takes effect on the write to TPI_INFO_B30, and checksum is calculated on just the first part, so pad the remainder of the buffer */ memset(vsif_buffer, 0, sizeof(vsif_buffer)); switch (hw_context->hpd_high_callback_status) { case HH_FMT_HDMI_VSIF_MHL3: case HH_FMT_HDMI_VSIF_MHL3_HDCP_ON: case HH_FMT_HDMI_VSIF_MHL3_NOT_RPT: case HH_FMT_HDMI_VSIF_MHL3_HDCP_ON_NOT_RPT: memcpy(vsif_buffer, (uint8_t *) &hw_context-> vsif_mhl3_or_hdmi_from_callback.mhl3, sizeof(hw_context-> vsif_mhl3_or_hdmi_from_callback.mhl3)); break; default: memcpy(vsif_buffer, (uint8_t *) &hw_context-> outgoing_mhl3_vsif, sizeof(hw_context-> outgoing_mhl3_vsif)); } mhl_tx_write_reg_block(hw_context, REG_TPI_INFO_B0, sizeof(vsif_buffer), (uint8_t *) vsif_buffer); } else { /* * enable HDMI to MHL VSIF conversion (clear bit 7) * and drop generic infoframes (set bit 1) */ mhl_tx_write_reg(hw_context, REG_PKT_FILTER_1, BIT_PKT_FILTER_1_DROP_GEN_PKT); } } else { /* For DVI, output video w/o infoframe. * No video settings are changed. */ mhl_tx_write_reg(hw_context, REG_RX_HDMI_CTRL2, hw_context->rx_hdmi_ctrl2_defval = REG_RX_HDMI_CTRL2_DEFVAL_DVI | VAL_RX_HDMI_CTRL2_VSI_MON_SEL_AVI); MHL_TX_DBG_ERR("DVI - Start HDCP\n"); start_hdcp(hw_context); } return true; } static int hdcp_isr(struct drv_hw_context *hw_context, uint8_t intr_status) { uint8_t query_data; query_data = mhl_tx_read_reg(hw_context, REG_TPI_COPP_DATA1); MHL_TX_DBG_INFO("R3D= %02x R29= %02x\n", intr_status, query_data); if (BIT_TPI_INTR_ST0_READ_BKSV_BCAPS_DONE_STAT & intr_status) { /* See 32702 why 200ms was added before enabling authentication. */ msleep(200); if (BIT_TPI_COPP_DATA1_COPP_PROTYPE & query_data) { /* * If the downstream device is a repeater, enforce a * 5-second delay to pass HDCP CTS 1B-03. * TODO: Describe this in the PR. * Allow the enforcement to exit early if the * KSV_FIFO_LAST bit gets set, which, implies that * BCAPS[5] (KSV_FIFO_RDY) is set. This fixes the * occasional "Inactivity Timer Expired"/"Not Judged" * result in 1B-02. */ if (BIT_TPI_COPP_DATA1_COPP_HDCP_REP & query_data) { #ifdef KSV_FIFO_RDY_INTERRUPT /* Start authentication here */ mhl_tx_write_reg(hw_context, REG_TPI_COPP_DATA2, BIT_TPI_COPP_DATA2_KSV_FORWARD | VAL_TPI_COPP_PROTLEVEL_MAX); #else msleep(SLEEP_10MS); /* Start authentication here */ mhl_tx_write_reg(hw_context, REG_TPI_COPP_DATA2, VAL_TPI_COPP_PROTLEVEL_MAX); #endif } else { /* Start authentication here */ mhl_tx_write_reg(hw_context, REG_TPI_COPP_DATA2, VAL_TPI_COPP_PROTLEVEL_MAX); } } #ifdef KSV_FIFO_RDY_INTERRUPT } else if (BIT_TPI_INTR_ST0_KSV_FIFO_FIRST_STAT & intr_status) { /* this will happen in repeater cases */ int ksv_fifo_stat; int cbus_connected_state; /* Read out the KSV list */ do { int ksv_fifo_bytes; int dummy = mhl_tx_read_reg(hw_context, REG_TPI_DS_BCAPS); ksv_fifo_stat = mhl_tx_read_reg(hw_context, REG_TPI_KSV_FIFO_STAT); ksv_fifo_bytes = ksv_fifo_stat & MSK_TPI_KSV_FIFO_STAT_KSV_FIFO_BYTES; if (ksv_fifo_bytes) { int ksv; ksv = mhl_tx_read_reg(hw_context, REG_TPI_KSV_FIFO_FORW); } if (BIT_TPI_KSV_FIFO_STAT_KSV_FIFO_LAST & ksv_fifo_stat) { break; } cbus_connected_state = mhl_tx_read_reg(hw_context, REG_CBUS_STATUS); cbus_connected_state &= (BIT_CBUS_STATUS_CBUS_CONNECTED | BIT_CBUS_STATUS_CBUS_HPD); } while ((BIT_CBUS_STATUS_CBUS_CONNECTED | BIT_CBUS_STATUS_CBUS_HPD) == cbus_connected_state); #endif } else if (BIT_TPI_INTR_ST0_READ_BKSV_ERR_STAT & intr_status) { MHL_TX_DBG_WARN("BKSV ERROR - Start HDCP\n"); start_hdcp(hw_context); } else if (BIT_TPI_INTR_ST0_TPI_COPP_CHNGE_STAT & intr_status) { int link_status; link_status = query_data & MSK_TPI_COPP_DATA1_COPP_LINK_STATUS; switch (link_status) { case VAL_TPI_COPP_LINK_STATUS_NORMAL: /* * Device asserts this status when authentication is * turned off. * Nothing should be done here. */ break; case VAL_TPI_COPP_LINK_STATUS_LINK_LOST: MHL_TX_DBG_ERR("LINK LOST - Start HDCP\n"); start_hdcp(hw_context); break; case VAL_TPI_COPP_LINK_STATUS_RENEGOTIATION_REQ: MHL_TX_DBG_INFO("tpi BSTATUS2: 0x%x\n", mhl_tx_read_reg(hw_context, REG_TPI_BSTATUS2) ); /* * Device asserts this status for Ri mismatch and starts * BKSV READ by itself. All we need to do is turn off * HDCP Authentication control. * Once BKSV READ (and BCAPS) concludes we will get * another interrupt and start authentication. */ mhl_tx_modify_reg(hw_context, REG_TPI_SC, BIT_TPI_SC_TPI_AV_MUTE, VAL_TPI_SC_TPI_AV_MUTE_MUTED); mhl_tx_write_reg(hw_context, REG_TPI_COPP_DATA2, 0); break; case VAL_TPI_COPP_LINK_STATUS_LINK_SUSPENDED: MHL_TX_DBG_ERR("LINK suspended - Start HDCP\n"); start_hdcp(hw_context); break; } } else if (BIT_TPI_INTR_ST0_TPI_AUTH_CHNGE_STAT & intr_status) { uint8_t new_link_prot_level; new_link_prot_level = (uint8_t) (query_data & (BIT_TPI_COPP_DATA1_COPP_GPROT | BIT_TPI_COPP_DATA1_COPP_LPROT)); switch (new_link_prot_level) { case (VAL_TPI_COPP_GPROT_NONE | VAL_TPI_COPP_LPROT_NONE): MHL_TX_DBG_ERR("?PROT_NONE - Start HDCP\n"); start_hdcp(hw_context); break; /* Device asserts both LPROT and GPROT if DS is a repeater. We should unmute the video only once. */ case VAL_TPI_COPP_GPROT_SECURE: break; /* When both bits are simultaneously set. */ case (VAL_TPI_COPP_GPROT_SECURE | VAL_TPI_COPP_LPROT_SECURE): case VAL_TPI_COPP_LPROT_SECURE: MHL_TX_DBG_ERR("HDCP 1.x Authentication Done\n"); unmute_video(hw_context); break; } } return 0; } static int hdcp2_isr(struct drv_hw_context *hw_context, uint8_t intr_status) { uint8_t rcvr_info[3]; uint8_t rcvr_id_list[5]; if (intr_status & BIT_HDCP2_INTR_AUTH_DONE) { MHL_TX_DBG_ERR("HDCP2.2 Authentication Done.\n"); /* Enable high-value content / disable mute */ } if (intr_status & BIT_HDCP2_INTR_AUTH_FAIL) { uint8_t ro_gp0; uint8_t ro_auth[2]; /* Disable high-value content / enable mute */ ro_gp0 = mhl_tx_read_reg(hw_context, REG_HDCP2X_GP_OUT0); mhl_tx_read_reg_block(hw_context, REG_HDCP2X_AUTH_STAT, sizeof(ro_auth), ro_auth); MHL_TX_DBG_ERR("HDCP2.2 Authentication Failed" " - gp0 %02X, status %02X %02X\n", ro_gp0, ro_auth[0], ro_auth[1]); } if (intr_status & BIT_HDCP2_INTR_RPTR_RCVID_CHANGE) { MHL_TX_DBG_ERR("HDCP2.2 RCV_ID Changed.\n"); /* Read RCVR INFO and RCVR ID LIST */ mhl_tx_read_reg_block(hw_context, REG_HDCP2X_RPT_RCVID_OUT, sizeof(rcvr_info), rcvr_info); mhl_tx_read_reg_block(hw_context, REG_HDCP2X_RPT_RCVR_ID0, sizeof(rcvr_id_list), rcvr_id_list); } mhl_tx_write_reg(hw_context, REG_HDCP2X_INTR0, intr_status); return intr_status; } static int int_8_isr(struct drv_hw_context *hw_context, uint8_t intr_8_status) { union vsif_mhl3_or_hdmi_u vsif; struct avi_info_frame_t avif; memset(&vsif, 0, sizeof(vsif)); /* Clear interrupt status immediately */ mhl_tx_write_reg(hw_context, REG_INTR8, intr_8_status); if (hw_context->hpd_high_callback_status >= 0) { /* Video is already started. * Set this flag so that we catch mode changes */ hw_context->hpd_high_callback_status = HH_VIDEO_NOT_RDY; return intr_8_status; } /* Resolution change interrupt (NEW_AVIF or NEW_VSIF) */ if ((BIT_CEA_NEW_VSI | BIT_CEA_NEW_AVI) & intr_8_status) { MHL_TX_DBG_WARN("got NEW_VSI\n"); mhl_tx_write_reg(hw_context, REG_RX_HDMI_CTRL2, hw_context-> rx_hdmi_ctrl2_defval | VAL_RX_HDMI_CTRL2_VSI_MON_SEL_VSI); mhl_tx_read_reg_block(hw_context, REG_RX_HDMI_MON_PKT_HEADER1, sizeof(vsif), (uint8_t *)&vsif); MHL_TX_DBG_WARN( "Got vsif: {%02x %02x %02x} %02x %02x%02x%02x %02x %02x\n", vsif.common.header.type_code, vsif.common.header.version_number, vsif.common.header.length, vsif.common.checksum, vsif.common.ieee_oui[0], vsif.common.ieee_oui[1], vsif.common.ieee_oui[2], *((uint8_t *) &vsif.hdmi.payLoad.pb4), vsif.hdmi.payLoad.pb5.HDMI_VIC); #ifdef MANUAL_INFO_FRAME_CLEAR_AT_HPD_DRIVEN_HIGH /* we rely on this interrupt to tell us when VSIF has gone away * as well as when it arrives. process_info_frame_change will * set valid_vsif according to the contents acquired here. */ hw_context->valid_vsif = false; #endif } if (BIT_CEA_NEW_AVI & intr_8_status) { MHL_TX_DBG_WARN("got NEW_AVIF\n"); /* Read AVIF packet */ mhl_tx_write_reg(hw_context, REG_RX_HDMI_CTRL2, hw_context-> rx_hdmi_ctrl2_defval | VAL_RX_HDMI_CTRL2_VSI_MON_SEL_AVI); mhl_tx_read_reg_block(hw_context, REG_RX_HDMI_MON_PKT_HEADER1, sizeof(avif), (uint8_t *) &avif); if (0 == avif.header.type_code) { MHL_TX_DBG_ERR( "bogus AVIF: avif.header.type_code is 0\n"); return intr_8_status; } } switch (intr_8_status & (BIT_CEA_NEW_VSI | BIT_CEA_NEW_AVI)) { case BIT_CEA_NEW_VSI: process_info_frame_change(hw_context, &vsif, NULL); break; case BIT_CEA_NEW_AVI: process_info_frame_change(hw_context, NULL, &avif); break; case (BIT_CEA_NEW_VSI | BIT_CEA_NEW_AVI): process_info_frame_change(hw_context, &vsif, &avif); break; } return intr_8_status; } static int int_3_isr(struct drv_hw_context *hw_context, uint8_t int_3_status) { if (BIT_DDC_CMD_DONE & int_3_status) { /* Inform MHL module of manual EDID read completion */ hw_context->intr_info->flags |= DRV_INTR_MSC_DONE; hw_context->intr_info->msc_done_data = 0; /* ensure that no other DDC interrupts occur */ enable_intr(hw_context, INTR_DDC, 0); } return 0; } static int int_9_isr(struct drv_hw_context *hw_context, uint8_t int_9_status) { if (int_9_status) { mhl_tx_write_reg(hw_context, g_intr_tbl[INTR_EDID].stat_addr, int_9_status); if (BIT_INTR9_DEVCAP_DONE & int_9_status) { hw_context->intr_info->flags |= DRV_INTR_MSC_DONE; hw_context->intr_info->msc_done_data = 0; } if (BIT_INTR9_EDID_DONE & int_9_status) { int ddcStatus; ddcStatus = mhl_tx_read_reg(hw_context, REG_DDC_STATUS); #ifdef SWWA_BZ30759 unfreeze_MHL_connect(hw_context); #endif if (BIT_DDC_STATUS_DDC_NO_ACK & ddcStatus) { /* Restart EDID read from block 0 */ hw_context->current_edid_req_blk = 0; if (!issue_edid_read_request(hw_context, hw_context->current_edid_req_blk)) { hw_context->intr_info->flags |= DRV_INTR_MSC_DONE; hw_context->intr_info->msc_done_data = 1; MHL_TX_DBG_ERR ("%sedid request problem%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); } } else { int num_extensions; MHL_TX_DBG_INFO("EDID block read complete\n"); num_extensions = si_mhl_tx_get_num_cea_861_extensions (hw_context->intr_info->edid_parser_context, hw_context->current_edid_req_blk); if (num_extensions < 0) { MHL_TX_DBG_ERR("edid problem:%d\n", num_extensions); if (ne_NO_HPD == num_extensions) { /* no HPD, so start over */ hw_context->intr_info->flags |= DRV_INTR_MSC_DONE; hw_context->intr_info-> msc_done_data = 1; MHL_TX_DBG_ERR("%shpd low%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); } else { /* * Restart EDID read * from block 0 */ hw_context-> current_edid_req_blk = 0; if (!issue_edid_read_request( hw_context, hw_context-> current_edid_req_blk)) { /* Notify the component * layer with error */ hw_context->intr_info-> flags |= DRV_INTR_MSC_DONE; hw_context->intr_info-> msc_done_data = 1; MHL_TX_DBG_ERR("%sedid" " request problem%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); } } } else if (hw_context->current_edid_req_blk < num_extensions) { /* EDID read next block */ if (!issue_edid_read_request( hw_context, ++hw_context-> current_edid_req_blk)) { /* Notify the MHL module with * error */ hw_context->intr_info->flags |= DRV_INTR_MSC_DONE; hw_context->intr_info-> msc_done_data = 1; MHL_TX_DBG_ERR("%sedid request" " problem%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); } } else { /* Inform MHL module of EDID read MSC * command completion */ hw_context->intr_info->flags |= DRV_INTR_MSC_DONE; hw_context->intr_info->msc_done_data = 0; } } } if (BIT_INTR9_EDID_ERROR & int_9_status) { #ifdef SWWA_BZ30759 unfreeze_MHL_connect(hw_context); #endif MHL_TX_DBG_INFO("EDID read error, retrying\n"); hw_context->edid_fifo_block_number = 0; hw_context->current_edid_req_blk = 0; if (ok_to_proceed_with_ddc(hw_context)) { /* Restart EDID read from block 0 */ hw_context->current_edid_req_blk = 0; if (!issue_edid_read_request(hw_context, hw_context->current_edid_req_blk)) { /* Notify the MHL module of error */ hw_context->intr_info->flags |= DRV_INTR_MSC_DONE; hw_context->intr_info->msc_done_data = 1; } } else { /* Notify the MHL module with error */ hw_context->intr_info->flags |= DRV_INTR_MSC_DONE; hw_context->intr_info->msc_done_data = 1; } } } return int_9_status; } void si_mhl_tx_read_devcap_fifo(struct drv_hw_context *hw_context, union MHLDevCap_u *dev_cap_buf) { MHL_TX_DBG_INFO("called\n"); /* Enable EDID interrupt */ enable_intr(hw_context, INTR_EDID, (BIT_INTR9_DEVCAP_DONE_MASK | BIT_INTR9_EDID_DONE_MASK | BIT_INTR9_EDID_ERROR)); /* choose devcap instead of EDID to appear at the FIFO */ mhl_tx_write_reg(hw_context, REG_EDID_CTRL, VAL_EDID_CTRL_EDID_PRIME_VALID_DISABLE | VAL_EDID_CTRL_DEVCAP_SELECT_DEVCAP | VAL_EDID_CTRL_EDID_FIFO_ADDR_AUTO_ENABLE | VAL_EDID_CTRL_EDID_MODE_EN_ENABLE); mhl_tx_write_reg(hw_context, REG_EDID_FIFO_ADDR, 0); /* Retrieve the DEVCAP register values from the FIFO */ mhl_tx_read_reg_block(hw_context, REG_EDID_FIFO_RD_DATA, DEVCAP_SIZE, dev_cap_buf->devcap_cache); MHL_TX_DBG_INFO("%sgot DEVCAP%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); } void si_mhl_tx_read_xdevcap_fifo(struct drv_hw_context *hw_context, union MHLXDevCap_u *xdev_cap_buf) { MHL_TX_DBG_INFO("called\n"); /* choose XDEVCAP instead of EDID to appear at the FIFO */ mhl_tx_write_reg(hw_context, REG_EDID_CTRL, VAL_EDID_CTRL_EDID_PRIME_VALID_DISABLE | BIT_EDID_CTRL_XDEVCAP_EN | VAL_EDID_CTRL_DEVCAP_SELECT_DEVCAP | VAL_EDID_CTRL_EDID_FIFO_ADDR_AUTO_ENABLE | VAL_EDID_CTRL_EDID_MODE_EN_ENABLE); mhl_tx_write_reg(hw_context, REG_EDID_FIFO_ADDR, 0); /* Retrieve the XDEVCAP register values from the FIFO */ mhl_tx_read_reg_block(hw_context, REG_EDID_FIFO_RD_DATA, XDEVCAP_OFFSET(XDEVCAP_LIMIT), xdev_cap_buf->xdevcap_cache); MHL_TX_DBG_INFO("%sgot XDEVCAP%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); } static int mhl_cbus_err_isr(struct drv_hw_context *hw_context, uint8_t cbus_err_int) { int ret_val = 0; uint8_t msc_abort_reason = 0; /* * Three possible errors could be asserted. * 94[2] = DDC_ABORT * 94[3] = ABORT while receiving a command - MSC_RCV_ERROR * The ABORT reasons are in 9A * 94[6] = ABORT while sending a command - MSC_SEND_ERROR * The ABORT reasons are in 9C */ #ifdef PRINT_DDC_ABORTS if (cbus_err_int & BIT_CBUS_DDC_ABORT) { uint8_t ddc_abort_reason = 0; /* * For DDC ABORTs, options are * 1. reset DDC block. This will hamper a broken HDCP or EDID. * 2. if error persists, reset the chip. In any case video is * not working. So it should not cause blinks. * In either case, call an API to let SoC know what happened. * * Only option 1 has been implemented here. */ ddc_abort_reason = mhl_tx_read_reg(hw_context, REG_DDC_ABORT_INT); MHL_TX_DBG_INFO("CBUS DDC ABORT. Reason = %02X\n", ddc_abort_reason); if (DDC_ABORT_THRESHOLD < ++ddc_abort_count) { ddc_abort_count = 0; } else if (MHL_READ_EDID_BLOCK == hw_context->current_cbus_req.command) { hw_context->intr_info->flags |= DRV_INTR_MSC_DONE; /* setting 1 indicates there was an error */ hw_context->intr_info->msc_done_data = 1; } } #endif if (cbus_err_int & BIT_CBUS_MSC_ABORT_RCVD) { /* * For MSC Receive time ABORTs * Defer submission of new commands by 2 seconds per MHL spec. * This is not even worth reporting to SoC. * Action is in the hands of peer. */ hw_context->intr_info->flags |= DRV_INTR_CBUS_ABORT; msc_abort_reason = mhl_tx_read_reg(hw_context, REG_MSC_MR_ABORT_INT); ++msc_abort_count; MHL_TX_DBG_ERR("#%d: ABORT during MSC RCV. Reason = %02X\n", msc_abort_count, msc_abort_reason); } if (cbus_err_int & BIT_CBUS_CMD_ABORT) { /* * Defer submission of new commands by 2 seconds per MHL spec * * For API operations such as RCP/UCP etc., report the * situation to SoC and let the decision be there. Internal * retries have been already done. */ hw_context->intr_info->flags |= DRV_INTR_CBUS_ABORT; msc_abort_reason = mhl_tx_read_reg(hw_context, REG_MSC_MT_ABORT_INT); MHL_TX_DBG_ERR("CBUS ABORT during MSC SEND. Reason = %02X\n", msc_abort_reason); mhl_tx_write_reg(hw_context, REG_MSC_MT_ABORT_INT, msc_abort_reason); } /* * Print the reason for information */ if (msc_abort_reason) { if (BIT_CBUS_MSC_MT_ABORT_INT_MAX_FAIL & msc_abort_reason) MHL_TX_DBG_ERR("Retry threshold exceeded\n"); if (BIT_CBUS_MSC_MT_ABORT_INT_PROTO_ERR & msc_abort_reason) MHL_TX_DBG_ERR("Protocol Error\n"); if (BIT_CBUS_MSC_MT_ABORT_INT_TIMEOUT & msc_abort_reason) MHL_TX_DBG_ERR("Translation layer timeout\n"); if (BIT_CBUS_MSC_MT_ABORT_INT_UNDEF_CMD & msc_abort_reason) MHL_TX_DBG_ERR("Undefined opcode\n"); if (BIT_CBUS_MSC_MT_ABORT_INT_MSC_MT_PEER_ABORT & msc_abort_reason) MHL_TX_DBG_ERR("MSC Peer sent an ABORT\n"); } return ret_val; } static void process_mhl3_dcap_rdy(struct drv_hw_context *hw_context) { bool peer_is_mhl3 = 0; /* * Peer device is MHL3.0 or * newer. Enable DEVCAP_X and * STATUS_X operations */ switch (hw_context->cbus_mode) { case CM_oCBUS_PEER_VERSION_PENDING: hw_context->cbus_mode = CM_oCBUS_PEER_IS_MHL3; peer_is_mhl3 = 1; break; case CM_oCBUS_PEER_VERSION_PENDING_BIST_SETUP: hw_context->cbus_mode = CM_oCBUS_PEER_IS_MHL3_BIST_SETUP; peer_is_mhl3 = 1; break; case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_SENT: case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY: peer_is_mhl3 = 1; break; case CM_oCBUS_PEER_VERSION_PENDING_BIST_STAT: hw_context->cbus_mode = CM_oCBUS_PEER_IS_MHL3_BIST_STAT; peer_is_mhl3 = 1; break; default: break; } if (peer_is_mhl3) { MHL_TX_DBG_ERR("%sdownstream device supports MHL3.0+%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); mhl_tx_write_reg(hw_context, REG_M3_CTRL, VAL_M3_CTRL_MHL3_VALUE); si_set_cbus_mode_leds(hw_context->cbus_mode); /* * Now that we have positively * identified the MHL version * of the peer, we re-evaluate * our settings. */ mhl3_specific_init(hw_context); } } /* * mhl_cbus_isr * * Only when MHL connection has been established. This is where we have the * first looks on the CBUS incoming commands or returned data bytes for the * previous outgoing command. * * It simply stores the event and allows application to pick up the event * and respond at leisure. * * return values: * 0 - MHL interrupts (all of them) have been cleared * - calling routine should exit * 1 - MHL interrupts (at least one of them) may not have been cleared * - calling routine should proceed with interrupt processing. */ static int mhl_cbus_isr(struct drv_hw_context *hw_context, uint8_t cbus_int) { /* some times a sink will send a CBUS1 message before we get the TDM_SYNC interrupt. If we were in the calibrated state, then ignore this interrupt until we are calibrated. */ switch (hw_context->cbus_mode) { case CM_eCBUS_S_AV_BIST: case CM_eCBUS_D_AV_BIST: /* * Only allow BIST_STOP (MSC_MSG) and command done * in BIST mode */ mhl_tx_write_reg(hw_context, REG_CBUS_INT_0, cbus_int & ~(BIT_CBUS_MSC_MR_MSC_MSG | BIT_CBUS_MSC_MT_DONE)); cbus_int &= (BIT_CBUS_MSC_MR_MSC_MSG | BIT_CBUS_MSC_MT_DONE); break; case CM_TRANSITIONAL_TO_eCBUS_S_CAL_BIST: case CM_TRANSITIONAL_TO_eCBUS_D_CAL_BIST: case CM_TRANSITIONAL_TO_eCBUS_S_CALIBRATED: case CM_TRANSITIONAL_TO_eCBUS_D_CALIBRATED: MHL_TX_DBG_ERR("%sCBUS1 message received cbus_int:0x%02x " "hpd status: 0x%02x%s\n", ANSI_ESC_YELLOW_TEXT, cbus_int, mhl_tx_read_reg(hw_context, REG_CBUS_STATUS), ANSI_ESC_RESET_TEXT); return -1; default: break; } if (CBUS1_IDLE_RCV_PEND == hw_context->cbus1_state) { /* don't process these interrupts until we exit the cbus1_idle_rcv_pending state */ return -1; } if (cbus_int & ~BIT_CBUS_HPD_CHG) { /* bugzilla 27396 * Logic to detect missed HPD interrupt. * Do not clear BIT_CBUS_INT_0_CBUS_INT_0_STAT2 yet. */ mhl_tx_write_reg(hw_context, REG_CBUS_INT_0, cbus_int & ~BIT_CBUS_HPD_CHG); } if (BIT_CBUS_HPD_CHG & cbus_int) { uint8_t cbus_status; uint8_t status; /* Check if a SET_HPD came from the downstream device. */ cbus_status = mhl_tx_read_reg(hw_context, REG_CBUS_STATUS); status = cbus_status & BIT_CBUS_STATUS_CBUS_HPD; if (BIT_CBUS_STATUS_CBUS_HPD & (hw_context->cbus_status ^ cbus_status)) { /* bugzilla 27396 * No HPD interrupt has been missed yet. * Clear BIT_CBUS_INT_0_CBUS_INT_0_STAT2. */ mhl_tx_write_reg(hw_context, REG_CBUS_INT_0, BIT_CBUS_INT_0_CBUS_INT_0_STAT2); MHL_TX_DBG_INFO("HPD change\n"); } else { MHL_TX_DBG_ERR("missed HPD change\n"); /* leave the BIT_CBUS_INT_0_CBUS_INT_0_STAT2 * interrupt uncleared, so that we get another interrupt */ /* whatever was missed is the inverse of what we got */ status ^= BIT_CBUS_STATUS_CBUS_HPD; cbus_status ^= BIT_CBUS_STATUS_CBUS_HPD; } MHL_TX_DBG_INFO("DS HPD changed to %02X\n", status); hw_context->intr_info->flags |= DRV_INTR_HPD_CHANGE; hw_context->intr_info->hpd_status = status; if (0 == status) { struct mhl_dev_context *dev_context; dev_context = get_mhl_device_context(hw_context); mhl_tx_stop_timer(dev_context, hw_context->input_field_rate_measurement_timer); MHL_TX_DBG_ERR("%sgot CLR_HPD%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); /* HW edid fifo state DOES NOT get reset on CLR_HPD */ if (MHL_SEND_3D_REQ_OR_FEAT_REQ == hw_context->current_cbus_req.command) { hw_context->current_cbus_req.command = 0x00; hw_context->intr_info->flags |= DRV_INTR_MSC_DONE; hw_context->intr_info->msc_done_data = 1; } else if (MHL_READ_EDID_BLOCK == hw_context->current_cbus_req.command) { hw_context->current_cbus_req.command = 0x00; hw_context->intr_info->flags |= DRV_INTR_MSC_DONE; /* setting 1 indicates there was an error */ hw_context->intr_info->msc_done_data = 1; } /* todo: investigate if this call should be after the * call to stop_video() */ drive_hpd_low(hw_context); hw_context->current_edid_req_blk = 0; /* default values for video */ hw_context->video_ready = false; hw_context->video_path = 1; /* * This cannot wait for the upper layer to notice * DRV_INTR_FLAG_HPD_CHANGE. * stop_video relies on the result. */ si_edid_reset(dev_context->edid_parser_context); /* if DS sent CLR_HPD ensure video is not there */ stop_video(hw_context); } else { MHL_TX_DBG_ERR("%sGot SET_HPD%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); } hw_context->cbus_status = cbus_status; } if (BIT_CBUS_MSC_MT_DONE_NACK & cbus_int) { MHL_TX_DBG_ERR("%sGot MSC_MT_DONE_NACK%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); hw_context->intr_info->flags |= DRV_INTR_MSC_NAK; } if (BIT_CBUS_MSC_MR_WRITE_STAT & cbus_int) { /* read status bytes */ mhl_tx_read_reg_block(hw_context, REG_MHL_STAT_0, ARRAY_SIZE(hw_context->intr_info->dev_status. write_stat), hw_context->intr_info->dev_status.write_stat); /* read xstatus bytes */ mhl_tx_read_reg_block(hw_context, REG_MHL_EXTSTAT_0, ARRAY_SIZE(hw_context->intr_info->dev_status. write_xstat), hw_context->intr_info->dev_status.write_xstat); MHL_TX_DBG_WARN( "%sGot WRITE_STAT: " "%02x %02x %02x : %02x %02x %02x %02x%s\n", ANSI_ESC_GREEN_TEXT, hw_context->intr_info->dev_status.write_stat[0], hw_context->intr_info->dev_status.write_stat[1], hw_context->intr_info->dev_status.write_stat[2], hw_context->intr_info->dev_status.write_xstat[0], hw_context->intr_info->dev_status.write_xstat[1], hw_context->intr_info->dev_status.write_xstat[2], hw_context->intr_info->dev_status.write_xstat[3], ANSI_ESC_RESET_TEXT); if (hw_context->intr_info->dev_status.write_stat[2] >= 0x30) { hw_context->mhl_peer_version_stat = hw_context->intr_info->dev_status.write_stat[2]; } #ifdef FORCE_OCBUS_FOR_ECTS /* This compile option is always enabled. * It is intended to help identify code deletion by adopters * who do not need this feauture. The control for forcing oCBUS * works by using module parameter below. Peer version is forced * to 2.0 allowing 8620 to treat the sink as if it is MHL 2.0 * device and as a result never switch cbus to MHL3 eCBUS. */ { /* todo: what if the sink/dongle is MHL1.x? */ if (force_ocbus_for_ects) { hw_context->mhl_peer_version_stat = hw_context->intr_info->dev_status. write_stat[2] = 0x20; } } #endif if (MHL_STATUS_DCAP_RDY & hw_context->intr_info->dev_status. write_stat[0]) { MHL_TX_DBG_INFO("DCAP_RDY in effect\n"); if (hw_context->mhl_peer_version_stat >= 0x30) { if (MHL_STATUS_XDEVCAPP_SUPP & hw_context->intr_info->dev_status. write_stat[0]) process_mhl3_dcap_rdy(hw_context); } /* after the above does not indicate MHL3, * then it's MHL1.x or MHL2.x */ if (CM_oCBUS_PEER_VERSION_PENDING == hw_context->cbus_mode) { /* * Initialize registers to operate in oCBUS mode */ hw_context->cbus_mode = CM_oCBUS_PEER_IS_MHL1_2; si_mhl_tx_drv_switch_cbus_mode(hw_context, CM_oCBUS_PEER_IS_MHL1_2); si_set_cbus_mode_leds(hw_context->cbus_mode); } /* Enable EDID interrupt */ enable_intr(hw_context, INTR_EDID, (BIT_INTR9_DEVCAP_DONE_MASK | BIT_INTR9_EDID_DONE_MASK | BIT_INTR9_EDID_ERROR)); } /* * Save received write_stat info for later * post interrupt processing */ hw_context->intr_info->flags |= DRV_INTR_WRITE_STAT; } if ((BIT_CBUS_MSC_MR_MSC_MSG & cbus_int)) { /* * Save received MSC message info for later * post interrupt processing */ hw_context->intr_info->flags |= DRV_INTR_MSC_RECVD; mhl_tx_read_reg_block(hw_context, REG_MSC_MR_MSC_MSG_RCVD_1ST_DATA, ARRAY_SIZE(hw_context->intr_info->msc_msg), hw_context->intr_info->msc_msg); MHL_TX_DBG_INFO("MSC MSG: %02X %02X\n", hw_context->intr_info->msc_msg[0], hw_context->intr_info->msc_msg[1]); } /* * don't do anything for a scratch pad write received interrupt. * instead wait for the DSCR_CHG interrupt */ if (BIT_CBUS_MSC_MR_SET_INT & cbus_int) { MHL_TX_DBG_WARN("MHL INTR Received\n"); /* * Save received SET INT message info for later * post interrupt processing */ hw_context->intr_info->flags |= DRV_INTR_SET_INT; mhl_tx_read_reg_block(hw_context, REG_MHL_INT_0, ARRAY_SIZE(hw_context->intr_info->int_msg), hw_context->intr_info->int_msg); /* clear the individual SET_INT bits */ mhl_tx_write_reg_block(hw_context, REG_MHL_INT_0, ARRAY_SIZE(hw_context->intr_info->int_msg), hw_context->intr_info->int_msg); if (MHL_INT_EDID_CHG & hw_context->intr_info->int_msg[1]) { int reg_val; MHL_TX_DBG_INFO("%sgot EDID_CHG%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); /* clear this bit so that * BIT_TPI_INFO_FSEL_TPI_INFO_EN * will get cleared by the h/w */ mhl_tx_modify_reg(hw_context, REG_TPI_INFO_FSEL, BIT_TPI_INFO_FSEL_TPI_INFO_RPT, 0); /* MHL module will re-read EDID */ drive_hpd_low(hw_context); stop_video(hw_context); /* prevent HDCP interrupts from coming in */ reg_val = mhl_tx_read_reg(hw_context, REG_LM_DDC); MHL_TX_DBG_INFO("REG_LM_DDC:%02x\n", reg_val); reg_val &= ~BIT_LM_DDC_SW_TPI_EN; reg_val |= VAL_LM_DDC_SW_TPI_EN_DISABLED; mhl_tx_write_reg(hw_context, REG_LM_DDC, reg_val); /* SetTPIMode */ MHL_TX_DBG_INFO("REG_LM_DDC:%02x\n", reg_val); reg_val &= ~BIT_LM_DDC_SW_TPI_EN; reg_val |= VAL_LM_DDC_SW_TPI_EN_ENABLED; mhl_tx_write_reg(hw_context, REG_LM_DDC, reg_val); /* * Clear HDCP interrupt. * Due to TPI enable, we may get one. * TODO: Document this in the PR. */ mhl_tx_write_reg(hw_context, g_intr_tbl[INTR_HDCP].stat_addr, 0xFF); } else if (MHL_INT_DSCR_CHG & hw_context->intr_info->int_msg[0]) { MHL_TX_DBG_WARN("got DSCR_CHG\n"); if (hw_context->gen2_write_burst_rcv) { /* this is NOT expected */ MHL_TX_DBG_ERR( "Ignored DSCR_CHG " "since MDT is enabled\n"); } else { mhl_tx_read_reg_block(hw_context, REG_MHL_SCRPAD_0, ARRAY_SIZE(hw_context-> write_burst_data), hw_context->write_burst_data); } } else if (MHL_INT_DCAP_CHG & hw_context->intr_info->int_msg[0]) { MHL_TX_DBG_WARN("got DCAP_CHG\n"); } else if (MHL_INT_REQ_WRT & hw_context->intr_info->int_msg[0]) { MHL_TX_DBG_WARN("got REQ_WRT\n"); } } if (BIT_CBUS_MSC_MT_DONE & cbus_int) { bool completed = true; MHL_TX_DBG_INFO("MSC_REQ_DONE,0x%02x(0x%02x,0x%02x)\n", hw_context->current_cbus_req.command, hw_context->current_cbus_req.reg, hw_context->current_cbus_req.reg_data); if (MHL_SET_INT == hw_context->current_cbus_req.command) { if (MHL_RCHANGE_INT == hw_context->current_cbus_req.reg) { if (MHL2_INT_3D_REQ & hw_context-> current_cbus_req.reg_data) { MHL_TX_DBG_WARN("3D_REQ complete\n"); hw_context->cbus1_state = CBUS1_IDLE_RCV_ENABLED; } if (MHL_INT_GRT_WRT & hw_context-> current_cbus_req.reg_data) { MHL_TX_DBG_WARN("GRT_WRT complete\n"); hw_context->cbus1_state = CBUS1_IDLE_RCV_ENABLED; } } } else if (MHL_SEND_3D_REQ_OR_FEAT_REQ == hw_context->current_cbus_req.command) { /* if this command completes before we can enable HAWB, * control will come here. We want to hold off other * CBUS activity until the peer finishes sending 3D_DTD * and 3D_VIC bursts. si_mhl_tx_msc_command_done will * be called from si_mhl_tx_drv_set_upstream_edid which * is called by the upper layer. */ completed = false; hw_context->cbus1_state = CBUS1_IDLE_RCV_ENABLED; } if (completed) { hw_context->current_cbus_req.command = 0x00; hw_context->intr_info->flags |= DRV_INTR_MSC_DONE; hw_context->intr_info->msc_done_data = mhl_tx_read_reg(hw_context, REG_MSC_MT_RCVD_DATA0); } } return -1; } static int int_5_isr(struct drv_hw_context *hw_context, uint8_t int_5_status) { int ret_val = 0; if (int_5_status & BIT_INTR_SCDT_CHANGE) { uint8_t temp; temp = mhl_tx_read_reg(hw_context, REG_TMDS_CSTAT_P3); if (BIT_TMDS_CSTAT_P3_SCDT & temp) { enum bist_cmd_status bcs; struct mhl_dev_context *dev_context; dev_context = get_mhl_device_context(hw_context); MHL_TX_DBG_ERR("%sGot SCDT HIGH%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); switch (hw_context->cbus_mode) { case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP: case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY: MHL_TX_DBG_ERR("\n") if (dev_context-> misc_flags.flags.bist_role_TE) { MHL_TX_DBG_ERR( "%sissuing BIST_SETUP%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT) bcs = si_mhl_tx_bist_setup(dev_context, &dev_context->bist_setup); } else { MHL_TX_DBG_ERR( "%sissuing BIST_READY%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT) send_bist_ready(dev_context); } break; default: MHL_TX_DBG_INFO("%s\n", si_mhl_tx_drv_get_cbus_mode_str( hw_context->cbus_mode)) #ifdef MHL3_DVI_SUPPORT #else /* * enable infoframe interrupt */ enable_intr(hw_context, INTR_INFR, BIT_CEA_NEW_AVI | BIT_CEA_NEW_VSI); #endif if (IN_MHL3_MODE(hw_context) || si_edid_sink_is_hdmi(hw_context-> intr_info->edid_parser_context)) { #ifdef MHL3_DVI_SUPPORT uint8_t src_signal_hdmi; #endif mhl_tx_write_reg(hw_context, REG_TPI_SC, VAL_TPI_SC_TPI_OUTPUT_MODE_0_HDMI); #ifdef MHL3_DVI_SUPPORT #define HDMI_MODE BIT_RX_HDMI_CTRL0_RX_HDMI_HDMI_MODE /* query the Rx for DVI vs. HDMI */ src_signal_hdmi = HDMI_MODE & mhl_tx_read_reg(hw_context, REG_RX_HDMI_CTRL0); if (src_signal_hdmi) { MHL_TX_DBG_ERR( "source signal HDMI\n"); #endif /* * enable infoframe interrupt */ enable_intr(hw_context, INTR_INFR, BIT_CEA_NEW_AVI | BIT_CEA_NEW_VSI); #ifdef MHL3_DVI_SUPPORT } else { MHL_TX_DBG_ERR( "source signal DVI\n"); start_video(hw_context); } #endif } else { if (hw_context-> hpd_high_callback_status >= 0) { /* Video is already started. * Set this flag so that we * catch mode changes */ hw_context-> hpd_high_callback_status = HH_VIDEO_NOT_RDY; return ret_val; } /* * output video here for DVI or * if a VIC is in hand for HDMI */ start_video(hw_context); } MHL_TX_DBG_INFO("actual_mode: %s0x%02x%s\n", ANSI_ESC_YELLOW_TEXT, mhl_tx_read_reg(hw_context, REG_RX_HDMI_CTRL0), ANSI_ESC_RESET_TEXT); } } else { struct mhl_dev_context *dev_context; dev_context = get_mhl_device_context(hw_context); mhl_tx_stop_timer(dev_context, hw_context->input_field_rate_measurement_timer); MHL_TX_DBG_WARN("%sGot SCDT LOW%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); /* Clear all InfoFrame info which is now stale. */ mhl_tx_write_reg(hw_context, REG_TMDS_CSTAT_P3, BIT_TMDS_CSTAT_P3_AVIF_MANUAL_CLEAR_STROBE | BIT_TMDS_CSTAT_P3_DISABLE_AUTO_AVIF_CLEAR); #ifndef MANUAL_INFO_FRAME_CLEAR_AT_HPD_DRIVEN_HIGH hw_context->valid_vsif = false; memset(&hw_context->current_vsif, 0, sizeof(hw_context->current_vsif)); memset(&hw_context->current_avi_info_frame, 0, sizeof(hw_context->current_avi_info_frame)); #endif stop_video(hw_context); /* Disable infoframe interrupt */ enable_intr(hw_context, INTR_INFR, 0); } } return ret_val; } #ifdef USE_HW_TIMER static int int_1_isr(struct drv_hw_context *hw_context, uint8_t int_1_status) { if (int_1_status & BIT_HW_TIMER_POP) { MHL_TX_DBG_INFO("Timer Pop\n"); /* Check timer flag(s) and process them here */ } mhl_tx_write_reg(hw_context, REG_INTR1, 0x80); return 0; } #endif #define BIT_0072_SW_INTR 0x04 static int int_2_isr(struct drv_hw_context *hw_context, uint8_t int_2_status) { if (BIT_0072_SW_INTR & int_2_status) { MHL_TX_DBG_ERR("enabling RGND\n"); /* Keep only RGND interrupt enabled for 8620 */ enable_intr(hw_context, INTR_DISC, BIT_RGND_READY_INT); wait_for_user_intr = 0; } return 0; } /* get_device_id returns chip Id */ int get_device_id(struct drv_hw_context *hw_context) { int ret_val; uint16_t number; ret_val = mhl_tx_read_reg(hw_context, REG_DEV_IDH); if (ret_val < 0) { MHL_TX_DBG_ERR("I2C error 0x%x\n", ret_val); return ret_val; } number = ret_val << 8; ret_val = mhl_tx_read_reg(hw_context, REG_DEV_IDL); if (ret_val < 0) { MHL_TX_DBG_ERR("I2C error 0x%x\n", ret_val); return ret_val; } ret_val |= number; return ret_val; } /* get_device_rev returns chip revision */ static int get_device_rev(struct drv_hw_context *hw_context) { int ret_val; ret_val = mhl_tx_read_reg(hw_context, REG_DEV_REV); if (ret_val < 0) { MHL_TX_DBG_ERR("I2C error\n"); ret_val = -1; } return ret_val; } /* * clear_and_disable_on_disconnect */ static void clear_and_disable_on_disconnect(struct drv_hw_context *hw_context) { uint8_t intr_num; struct mhl_dev_context *dev_context; dev_context = get_mhl_device_context(hw_context); mhl_tx_stop_timer(dev_context, hw_context->input_field_rate_measurement_timer); /* clear and mask all interrupts */ for (intr_num = 0; intr_num < MAX_INTR; intr_num++) { if (INTR_DISC == intr_num) { /* clear discovery interrupts except MHL_EST */ mhl_tx_write_reg(hw_context, g_intr_tbl[INTR_DISC].stat_addr, ~BIT_RGND_READY_INT); if (wait_for_user_intr) { /* Keep only USER interrupt enabled */ enable_intr(hw_context, INTR_DISC, 0); } else { /* Keep only RGND interrupt enabled */ enable_intr(hw_context, INTR_DISC, BIT_RGND_READY_INT); } } else { /* Clear and disable all other interrupts */ mhl_tx_write_reg(hw_context, g_intr_tbl[intr_num].stat_addr, 0xFF); if (INTR_USER == intr_num) { if (wait_for_user_intr) { /* Keep only USER interrupt enabled */ enable_intr(hw_context, INTR_USER, BIT_0072_SW_INTR); } else { enable_intr(hw_context, INTR_USER, 0x00); } } else { enable_intr(hw_context, intr_num, 0x00); } } } } /* * switch_to_idle * This function performs s/w as well as h/w state transitions. */ static void switch_to_idle(struct drv_hw_context *hw_context, bool do_interrupt_clear) { if (do_interrupt_clear) clear_and_disable_on_disconnect(hw_context); /* Adjust RGND vbias threshold and calibration resistance. CBUS driver strength is maintained at POR default of "Strong". */ mhl_tx_write_reg(hw_context, REG_MHL_CBUS_CTL0, (VAL_MHL_CBUS_CTL0_CBUS_DRV_SEL_STRONG | VAL_MHL_CBUS_CTL0_CBUS_RGND_VBIAS_734)); mhl_tx_write_reg(hw_context, REG_MHL_CBUS_CTL1, VAL_MHL_CBUS_CTL1_1115_OHM); /* * Leave just enough of the transmitter powered up * to detect connections. * i.e. disable the following: * BIT_DPD_PDNRX12, BIT_DPD_PWRON_HSIC, * BIT_DPD_PDIDCK_N, BIT_DPD_PD_MHL_CLK_N */ mhl_tx_write_reg(hw_context, REG_DPD, BIT_DPD_PWRON_PLL | BIT_DPD_PDNTX12 | BIT_DPD_OSC_EN); } static void cbus_reset(struct drv_hw_context *hw_context) { MHL_TX_DBG_WARN("Perform CBUS reset to clean MHL STAT values\n"); mhl_tx_write_reg(hw_context, REG_PWD_SRST, BIT_PWD_SRST_CBUS_RST | BIT_PWD_SRST_CBUS_RST_SW_EN); mhl_tx_write_reg(hw_context, REG_PWD_SRST, BIT_PWD_SRST_CBUS_RST_SW_EN); #ifdef SWWA_BZ30759 /* switch away from connector wires using 6051 */ set_pin(X02_USB_SW_CTRL, 0); platform_mhl_tx_hw_reset(TX_HW_RESET_PERIOD, TX_HW_RESET_DELAY); /* Power up the device */ mhl_tx_write_reg(hw_context, REG_DPD, BIT_DPD_PWRON_PLL | BIT_DPD_PDNTX12 | BIT_DPD_OSC_EN); /* Based on module parameter "crystal_khz=xxxxx" program registers. */ program_ext_clock_regs(hw_context, crystal_khz); /* switch back to connector wires using 6051 */ set_pin(X02_USB_SW_CTRL, 1); #endif #ifdef CoC_FSM_MONITORING /* Begin enable CoC FSM monitoring */ { #define REG_COC_MISC_CTL0 (TX_PAGE_7 | 0x28) mhl_tx_modify_reg(hw_context, REG_COC_MISC_CTL0, 0x80, 0x80); mhl_tx_modify_reg(hw_context, REG_GPIO_CTRL1, BIT_CTRL1_GPIO_I_7 | BIT_CTRL1_GPIO_OEN_7 | BIT_CTRL1_GPIO_I_6 | BIT_CTRL1_GPIO_OEN_6, 0); } /* End enable CoC FSM monitoring */ #endif } /* * disconnect_mhl * This function performs s/w as well as h/w state transitions. */ static void disconnect_mhl(struct drv_hw_context *hw_context, bool do_interrupt_clear) { disable_gen2_write_burst_rcv(hw_context); disable_gen2_write_burst_xmit(hw_context); stop_video(hw_context); MHL_TX_DBG_WARN("STOP_VIDEO DONE\n"); mhl_tx_vbus_control(VBUS_OFF); mhl_tx_vbus_current_ctl(I_VBUS_PRE_DISCOVERY); /* Meet an MHL CTS timing - Tsrc:cbus_float. * Must do before resetting CBUS * Fixes failing cases 3.3.14.1, 3.3.14.3, 3.3.22.2 */ msleep(50); cbus_reset(hw_context); clear_auto_zone_for_mhl_3(hw_context); mhl_tx_write_reg(hw_context, REG_COC_CTL0, 0x40); mhl_tx_write_reg(hw_context, REG_CBUS3_CNVT, 0x84); mhl_tx_write_reg(hw_context, REG_COC_CTL14, 0x00); mhl_tx_write_reg(hw_context, REG_COC_CTL0, 0x40); mhl_tx_write_reg(hw_context, REG_HRXCTRL3, 0x07); /* mhl_tx_write_reg(hw_context, REG_CBUS3_CNVT, 0x84); */ mhl_tx_write_reg(hw_context, REG_MHL_PLL_CTL0, (VAL_MHL_PLL_CTL0_HDMI_CLK_RATIO_1X | BIT_MHL_PLL_CTL0_CRYSTAL_CLK_SEL | BIT_MHL_PLL_CTL0_ZONE_MASK_OE)); mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL0, 0xC0); mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL1, 0xBB); mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL3, 0x48); mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL5, 0x3F); mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL2, 0x2F); mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL6, 0x2A); mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL7, 0x08); MHL_TX_DBG_WARN("cbus_mode: %s\n", si_mhl_tx_drv_get_cbus_mode_str(hw_context->cbus_mode)) switch (hw_context->cbus_mode) { case CM_NO_CONNECTION_BIST_STAT: case CM_eCBUS_S_BIST: case CM_eCBUS_S_AV_BIST: case CM_eCBUS_D_BIST: case CM_eCBUS_D_AV_BIST: case CM_BIST_DONE_PENDING_DISCONNECT: hw_context->cbus_mode = CM_NO_CONNECTION_BIST_STAT; break; case CM_NO_CONNECTION_BIST_SETUP: case CM_oCBUS_PEER_VERSION_PENDING_BIST_SETUP: case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP: case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_SENT: case CM_oCBUS_PEER_IS_MHL3_BIST_SETUP_PEER_READY: hw_context->cbus_mode = CM_NO_CONNECTION_BIST_SETUP; break; case CM_oCBUS_PEER_VERSION_PENDING_BIST_STAT: case CM_oCBUS_PEER_IS_MHL3_BIST_STAT: default: hw_context->cbus_mode = CM_NO_CONNECTION; } /* restore default value after BIST */ mhl_tx_write_reg(hw_context, REG_COC_CTL7, 0x06); hw_context->mhl_peer_version_stat = 0; si_set_cbus_mode_leds(hw_context->cbus_mode); /* Pull the upstream HPD line low to prevent EDID and HDCP activity. */ drive_hpd_low(hw_context); mhl_tx_write_reg(hw_context, REG_M3_CTRL, VAL_M3_CTRL_PEER_VERSION_PENDING_VALUE); mhl_tx_write_reg(hw_context, REG_MHL_COC_CTL1, 0x07); /* For proper MHL discovery and tolerance of impedance * measurement, set 5E3[7:4]=0x01 - this turns off the * CBUS pull up for discovery states and 20K for IDLE * state before impedance is measured. Fixes CBUS CTS * cases that measure +/-20% tolerance of 1K MHL * impedance, such as 3.3.5.1, 3.3.5.5, 3.3.6.4 */ mhl_tx_write_reg(hw_context, REG_DISC_CTRL4, 0x10); mhl_tx_write_reg(hw_context, REG_DISC_CTRL8, 0x00); /* DO NOT enable wake/discovery pulses until successful RGND == 1K */ mhl_tx_write_reg(hw_context, REG_DISC_CTRL9, BIT_DISC_CTRL9_WAKE_DRVFLT | BIT_DISC_CTRL9_WAKE_PULSE_BYPASS); /* Disable MSC heartbeat */ disable_heartbeat(hw_context); /* Set up discovery registers to attempt oCBUS discovery */ mhl_tx_write_reg(hw_context, REG_DISC_CTRL1, 0x25); if (do_interrupt_clear) clear_and_disable_on_disconnect(hw_context); /* clear this flag to fix DS hot plug issue */ hw_context->cbus_status = 0; hw_context->current_cbus_req.command = 0x00; hw_context->hawb_write_pending = false; hw_context->cbus1_state = CBUS1_IDLE_RCV_DISABLED; } /* * int_4_isr * MHL device discovery interrupt handler * 1. When impedance is measured as 1k, RGND interrupt is asserted. * 2. Chip sends out wake pulses and discovery pulses. * Then asserts MHL_EST if CBUS stays high to meet MHL timings. * 3. If discovery fails, NON_MHL_EST is asserted. * 4. If MHL cable is removed, CBUS_DIS is asserted. * (Need to check this bit all the time) */ static int int_4_isr(struct drv_hw_context *hw_context, uint8_t int_4_status) { int ret_val = 0; MHL_TX_DBG_WARN("cbus_mode: %s\n", si_mhl_tx_drv_get_cbus_mode_str(hw_context->cbus_mode)) if ((BIT_CBUS_MHL12_DISCON_INT & int_4_status) || (BIT_CBUS_MHL3_DISCON_INT & int_4_status) || (BIT_NOT_MHL_EST_INT & int_4_status)) { MHL_TX_DBG_ERR("%sGot CBUS_DIS. MHL disconnection%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); switch (hw_context->cbus_mode) { case CM_TRANSITIONAL_TO_eCBUS_S_BIST: case CM_TRANSITIONAL_TO_eCBUS_D_BIST: case CM_TRANSITIONAL_TO_eCBUS_S_CAL_BIST: case CM_TRANSITIONAL_TO_eCBUS_D_CAL_BIST: case CM_eCBUS_S_BIST: case CM_eCBUS_D_BIST: case CM_BIST_DONE_PENDING_DISCONNECT: case CM_eCBUS_S_AV_BIST: case CM_eCBUS_D_AV_BIST: { struct mhl_dev_context *dev_context; dev_context = container_of((void *)hw_context, struct mhl_dev_context, drv_context); si_mhl_tx_bist_cleanup(dev_context); } break; default: ; } /* For proper MHL discovery and tolerance of impedance * measurement, set 5E3[7:4]=0x01 - this turns off the * CBUS pull up for discovery states and 20K for IDLE * state before impedance is measured. Fixes CBUS CTS * cases that measure +/-20% tolerance of 1K MHL * impedance, such as 3.3.5.1, 3.3.5.5, 3.3.6.4 */ mhl_tx_write_reg(hw_context, REG_DISC_CTRL4, 0x10); /* Setup termination etc. */ hw_context->intr_info->flags |= DRV_INTR_DISCONNECT; if (BIT_CBUS_MHL12_DISCON_INT & int_4_status) { disconnect_mhl(hw_context, true); switch_to_idle(hw_context, false); } else { /* must be BIT_NOT_MHL_EST_INT */ disconnect_mhl(hw_context, false); switch_to_idle(hw_context, true); } ret_val = 0xFF; /* INTR already cleared in disconnect_mhl */ if (hw_context->current_cbus_req.command) { hw_context->current_cbus_req.command = 0x00; hw_context->intr_info->flags |= DRV_INTR_MSC_DONE; hw_context->intr_info->msc_done_data = 0; } } else if (int_4_status & BIT_RGND_READY_INT) { int disc_stat2; disc_stat2 = mhl_tx_read_reg(hw_context, REG_DISC_STAT2) & MSK_DISC_STAT2_RGND; MHL_TX_DBG_ERR("Cable (RGND) impedance measured (%s)\n", rgnd_value_string[disc_stat2]); if (VAL_RGND_1K == disc_stat2) { MHL_TX_DBG_WARN("Cable impedance = 1k (MHL Device)\n"); mhl_tx_write_reg(hw_context, REG_DISC_CTRL9, BIT_DISC_CTRL9_WAKE_DRVFLT | BIT_DISC_CTRL9_DISC_PULSE_PROCEED); #ifdef ENABLE_VBUS_SENSE /* BZ31845 */ /* disconnect_mhl sets VBUS_OFF, so we know, * at this point, that if XO3_SINK_VBUS_SENSE * indicates high, then we must be getting * VBUS power from the sink/dongle */ msleep(MHL_T_src_vbus_cbus_stable_min); if (get_config(hw_context, XO3_SINK_VBUS_SENSE)) { MHL_TX_DBG_WARN("%ssink drives VBUS%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); /* limit incoming current */ mhl_tx_vbus_control(VBUS_OFF); mhl_tx_vbus_current_ctl(I_VBUS_PRE_DISCOVERY); } else { MHL_TX_DBG_WARN("%ssource drives VBUS%s\n", ANSI_ESC_YELLOW_TEXT, ANSI_ESC_RESET_TEXT); mhl_tx_vbus_control(VBUS_ON); } #endif /* For proper MHL discovery and tolerance of * impedance measurement, set 5E3[7:4]=0x90. * This sets the CBUS pull up for discovery * states as 5k and 20K for IDLE state after * impedance has been measured. Fixes CBUS * CTS cases that measure +/-20% tolerance of * 1K MHL impedance. Such as 3.3.5.1, 3.3.5.5, * 3.3.6.4 */ mhl_tx_write_reg(hw_context, REG_DISC_CTRL4, 0x90); /* speculatively enable the MHL3 clock so * that by the time the wake/discover * sequence is complete, the MHL3 clock * will be stabilized LONG BEFORE the * transition to eCBUS mode. */ /* enable HSIC earlier to enhance stability */ /* enable remaining discovery interrupts */ enable_intr(hw_context, INTR_DISC, BIT_MHL3_EST_INT_MASK | BIT_MHL_EST_INT_MASK | BIT_NOT_MHL_EST_INT_MASK | BIT_CBUS_MHL3_DISCON_INT_MASK | BIT_CBUS_MHL12_DISCON_INT_MASK | BIT_RGND_READY_INT_MASK); mhl_tx_write_reg(hw_context, REG_MHL_PLL_CTL0, (VAL_MHL_PLL_CTL0_HDMI_CLK_RATIO_1X | BIT_MHL_PLL_CTL0_CRYSTAL_CLK_SEL | BIT_MHL_PLL_CTL0_ZONE_MASK_OE)); mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL0, 0xC0); mhl_tx_write_reg(hw_context, REG_M3_CTRL, VAL_M3_CTRL_PEER_VERSION_PENDING_VALUE); mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL1, 0xA2); mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL2, 0x03); mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL3, 0x35); mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL5, 0x02); mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL6, 0x02); mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL7, 0x08); mhl_tx_write_reg(hw_context, REG_COC_CTLC, 0xFF); mhl_tx_write_reg(hw_context, REG_DPD, BIT_DPD_PWRON_PLL | BIT_DPD_PDNTX12 | BIT_DPD_OSC_EN | BIT_DPD_PWRON_HSIC); enable_intr(hw_context, INTR_COC, BIT_COC_PLL_LOCK_STATUS_CHANGE | BIT_COC_CALIBRATION_DONE); /* Enable MSC interrupt to handle initial exchanges */ enable_intr(hw_context, INTR_MERR, ( #ifdef PRINT_DDC_ABORTS BIT_CBUS_DDC_ABORT | #endif BIT_CBUS_MSC_ABORT_RCVD | BIT_CBUS_CMD_ABORT)); enable_intr(hw_context, INTR_MSC, (BIT_CBUS_MSC_MT_DONE | BIT_CBUS_HPD_CHG | BIT_CBUS_MSC_MR_WRITE_STAT | BIT_CBUS_MSC_MR_MSC_MSG | BIT_CBUS_MSC_MR_WRITE_BURST | BIT_CBUS_MSC_MR_SET_INT | BIT_CBUS_MSC_MT_DONE_NACK)); } else { mhl_tx_write_reg(hw_context, REG_DISC_CTRL9, BIT_DISC_CTRL9_WAKE_DRVFLT | BIT_DISC_CTRL9_NOMHL_EST | BIT_DISC_CTRL9_WAKE_PULSE_BYPASS); /* enable remaining discovery INTRs, except MHL_EST */ #if (INCLUDE_SII6031 == 1) { struct mhl_dev_context *dev_context; dev_context = container_of((void *)hw_context, struct mhl_dev_context, drv_context); mhl_tx_notify_otg(dev_context, false); } #endif enable_intr(hw_context, INTR_DISC, BIT_NOT_MHL_EST_INT_MASK | BIT_CBUS_MHL3_DISCON_INT_MASK | BIT_CBUS_MHL12_DISCON_INT_MASK | BIT_RGND_READY_INT_MASK); } } else if (int_4_status & BIT_MHL_EST_INT) { uint8_t msc_compat = BIT_CBUS_MSC_COMPATIBILITY_CONTROL_ENABLE_XDEVCAP; /* * ENABLE_DISCOVERY ensures wake up / discovery pulses ar sent * and as result sink/dongle would respond CBUS high. * 8620: ENABLE_DISCOVERY is always set. On successful * discovery, 8620 will assert MHL_EST. */ switch (hw_context->cbus_mode) { case CM_NO_CONNECTION: hw_context->cbus_mode = CM_oCBUS_PEER_VERSION_PENDING; break; case CM_NO_CONNECTION_BIST_SETUP: hw_context->cbus_mode = CM_oCBUS_PEER_VERSION_PENDING_BIST_SETUP; break; case CM_NO_CONNECTION_BIST_STAT: hw_context->cbus_mode = CM_oCBUS_PEER_VERSION_PENDING_BIST_STAT; break; default: ; } si_set_cbus_mode_leds(hw_context->cbus_mode); /* For proper MHL discovery and tolerance of impedance * measurement, set 5E3[7:4]=0x01 - this turns off the * CBUS pull up for discovery states and 20K for IDLE * state after MHL sink has been discovered. Fixes * CBUS CTS cases that measure +/-20% tolerance of 1K * MHL impedance, Such as 3.3.5.1, 3.3.5.5, 3.3.6.4 */ mhl_tx_write_reg(hw_context, REG_DISC_CTRL4, 0x10); /* speculatively enable XDEVCAP reads * so that MHL 3.0 sinks and dongles can * read xdevcap registers in oCBUS mode */ mhl_tx_write_reg(hw_context, REG_CBUS_MSC_COMPATIBILITY_CONTROL, msc_compat); init_regs(hw_context); /* * Setting this flag triggers sending DCAP_RDY. * Tested RK-9296 - it works fine. */ hw_context->intr_info->flags |= DRV_INTR_CONNECT; } return ret_val; } static int g2wb_err_isr(struct drv_hw_context *hw_context, uint8_t intr_stat) { if (intr_stat) { if (BIT_MDT_RCV_TIMEOUT & intr_stat) { MHL_TX_DBG_WARN("%sBIT_MDT_RCV_TIMEOUT%s\n", ANSI_ESC_YELLOW_TEXT, ANSI_ESC_RESET_TEXT); } if (BIT_MDT_RCV_SM_ABORT_PKT_RCVD & intr_stat) { MHL_TX_DBG_ERR("%sBIT_MDT_RCV_SM_ABORT_PKT_RCVD%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); } if (BIT_MDT_RCV_SM_ERROR & intr_stat) { MHL_TX_DBG_ERR("%sBIT_MDT_RCV_SM_ERROR%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); } if (BIT_MDT_XMIT_TIMEOUT & intr_stat) { MHL_TX_DBG_ERR("%sBIT_MDT_XMIT_TIMEOUT %s\n", ANSI_ESC_YELLOW_TEXT, ANSI_ESC_RESET_TEXT); } if (BIT_MDT_XMIT_SM_ABORT_PKT_RCVD & intr_stat) { MHL_TX_DBG_ERR ("%sBIT_MDT_XMIT_SM_ABORT_PKT_RCVD " "- status: 0x%02x%s\n", ANSI_ESC_RED_TEXT, mhl_tx_read_reg(hw_context, REG_MDT_SM_STAT), ANSI_ESC_RESET_TEXT); } if (BIT_MDT_XMIT_SM_ERROR & intr_stat) { MHL_TX_DBG_ERR("%sBIT_MDT_XMIT_SM_ERROR%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); } } return 0; } static void check_bist_request_stat_pending(struct drv_hw_context *hw_context) { struct cbus_req *cur_req = &hw_context->current_cbus_req; struct mhl_dev_context *dev_context; dev_context = container_of((void *)hw_context, struct mhl_dev_context, drv_context); if (MHL_MSC_MSG == cur_req->command) { if (MHL_MSC_MSG_BIST_REQUEST_STAT == cur_req->msg_data[0]) { MHL_TX_DBG_WARN("BIST_REQUEST_STAT completed\n") cur_req->command = 0x00; si_mhl_tx_msc_command_done(dev_context, 0x00); } } } static int g2wb_isr(struct drv_hw_context *hw_context, uint8_t intr_stat) { int ret_val = 0; if (intr_stat) { if (BIT_MDT_XFIFO_EMPTY & intr_stat) { struct cbus_req *peek_req; struct mhl_dev_context *dev_context; dev_context = container_of((void *)hw_context, struct mhl_dev_context, drv_context); MHL_TX_DBG_WARN( "%sHAWB XFIFO empty%s XFIFO_STAT: 0x%02x\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT, mhl_tx_read_reg(hw_context, REG_MDT_XFIFO_STAT)); peek_req = peek_next_cbus_transaction(dev_context); if (NULL == peek_req) { disable_gen2_write_burst_xmit(hw_context); disable_gen2_write_burst_rcv(hw_context); } else if (MHL_WRITE_BURST != peek_req->command) { disable_gen2_write_burst_xmit(hw_context); disable_gen2_write_burst_rcv(hw_context); } } if (BIT_MDT_RFIFO_DATA_RDY & intr_stat) { uint8_t length; uint8_t mdt_buffer[20]; int rfifo_stat; /* Read all bytes */ mhl_tx_read_reg_block(hw_context, REG_MDT_RCV_READ_PORT, 17, mdt_buffer); MHL_TX_DBG_WARN ("%sgot G2WB incoming 0x%02x%02x%s = %02X\n", ANSI_ESC_GREEN_TEXT, mdt_buffer[1], mdt_buffer[2], ANSI_ESC_RESET_TEXT, intr_stat); /* first byte contains the length of data */ length = mdt_buffer[0]; /* * There is no way to know how much * of the scratch pad was written so read * it all. The app. will have to parse * the data to know how much of it is valid. */ memcpy(hw_context->write_burst_data, &mdt_buffer[1], 16); /* Signal upper layer of this arrival */ hw_context->intr_info->flags |= DRV_INTR_WRITE_BURST; /* * Clear current level in the FIFO. * Moves pointer to the next keep RSM enabled */ mhl_tx_write_reg(hw_context, REG_MDT_RCV_CONTROL, BIT_MDT_RCV_CONTROL_MDT_RFIFO_CLR_CUR | BIT_MDT_RCV_CONTROL_MDT_RCV_EN | hw_context->delayed_hawb_enable_reg_val); /* don't let the caller clear * BIT_MDT_RFIFO_DATA_RDY until * the receive buffer is empty */ rfifo_stat = mhl_tx_read_reg(hw_context, REG_MDT_RFIFO_STAT); if (rfifo_stat & MSK_MDT_RFIFO_STAT_MDT_RFIFO_CNT) { ret_val = BIT_MDT_RFIFO_DATA_RDY; } else { switch (hw_context->cbus1_state) { case CBUS1_IDLE_RCV_ENABLED: case CBUS1_IDLE_RCV_DISABLED: break; case CBUS1_IDLE_RCV_PEND: hw_context->cbus1_state = CBUS1_IDLE_RCV_ENABLED; break; case CBUS1_MSC_PEND_DLY_RCV_EN: MHL_TX_DBG_ERR( "%sdelayed HAWB recv%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT) check_bist_request_stat_pending( hw_context); break; case CBUS1_MSC_PEND_DLY_RCV_DIS: break; case CBUS1_XMIT_PEND_XMIT_RCV_EN: break; case CBUS1_XMIT_PEND_XMIT_RCV_PEND: hw_context->cbus1_state = CBUS1_XMIT_PEND_XMIT_RCV_EN; break; } } } if (BIT_MDT_IDLE_AFTER_HAWB_DISABLE & intr_stat) { MHL_TX_GENERIC_DBG_PRINT(DBG_MSG_LEVEL_WARN, "%shawb_idle%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT) if (MHL_WRITE_BURST == hw_context->current_cbus_req.command) { hw_context->current_cbus_req.command = 0x00; hw_context->intr_info->flags |= DRV_INTR_MSC_DONE; hw_context->intr_info->msc_done_data = 0; hw_context->hawb_write_pending = false; hw_context->cbus1_state = CBUS1_IDLE_RCV_ENABLED; } } } return ret_val; } static void enable_intr(struct drv_hw_context *hw_context, uint8_t intr_num, uint8_t intr_mask) { g_intr_tbl[intr_num].mask = intr_mask; mhl_tx_write_reg(hw_context, g_intr_tbl[intr_num].mask_addr, intr_mask); } /* * Enable interrupts and turn on the engine */ static void si_mhl_tx_drv_enable_emsc_block(struct drv_hw_context *hw_context) { uint8_t intStatus; MHL_TX_DBG_INFO("Enabling EMSC and EMSC interrupts\n"); mhl_tx_modify_reg(hw_context, REG_SPIBURSTSTAT, BIT_SPIBURSTSTAT_SPI_SRST, BIT_SPIBURSTSTAT_SPI_SRST); mhl_tx_modify_reg(hw_context, REG_GENCTL, BIT_GENCTL_EMSC_EN | BIT_GENCTL_CLR_EMSC_RFIFO | BIT_GENCTL_CLR_EMSC_XFIFO, BIT_GENCTL_EMSC_EN | BIT_GENCTL_CLR_EMSC_RFIFO | BIT_GENCTL_CLR_EMSC_XFIFO); mhl_tx_modify_reg(hw_context, REG_GENCTL, BIT_GENCTL_CLR_EMSC_RFIFO | BIT_GENCTL_CLR_EMSC_XFIFO, 0); mhl_tx_modify_reg(hw_context, REG_COMMECNT, BIT_COMMECNT_I2C_TO_EMSC_EN, use_spi ? 0 : BIT_COMMECNT_I2C_TO_EMSC_EN); intStatus = mhl_tx_read_reg(hw_context, REG_EMSCINTR); mhl_tx_write_reg(hw_context, REG_EMSCINTR, intStatus); enable_intr(hw_context, INTR_BLOCK, BIT_EMSCINTR_SPI_DVLD); } void si_mhl_tx_drv_device_isr(struct drv_hw_context *hw_context, struct interrupt_info *intr_info) { uint8_t intr_num; uint8_t aggregated_intr_status[NUM_AGGREGATED_INTR_REGS]; hw_context->intr_info = intr_info; MHL_TX_DBG_INFO("%sgot INTR COC_STAT_0:0x%02x%s\n", ANSI_ESC_GREEN_TEXT, mhl_tx_read_reg(hw_context, REG_COC_STAT_0), ANSI_ESC_RESET_TEXT); MHL_TX_DBG_INFO("%sdiv_ctl_main:0x%02x hdcp2x_tp1:0x%02x%s\n", ANSI_ESC_GREEN_TEXT, mhl_tx_read_reg(hw_context, REG_DIV_CTL_MAIN), mhl_tx_read_reg(hw_context, REG_HDCP2X_TP1), ANSI_ESC_RESET_TEXT); mhl_tx_read_reg_block(hw_context, REG_FAST_INTR_STAT, sizeof(aggregated_intr_status), aggregated_intr_status); MHL_TX_DBG_INFO("%s aggr intr status:" " %02x %02x %02x %02x %02x %02x %02x%s\n", ANSI_ESC_GREEN_TEXT, aggregated_intr_status[FAST_INTR_STAT], aggregated_intr_status[L1_INTR_STAT_0], aggregated_intr_status[L1_INTR_STAT_1], aggregated_intr_status[L1_INTR_STAT_2], aggregated_intr_status[L1_INTR_STAT_3], aggregated_intr_status[L1_INTR_STAT_4], aggregated_intr_status[L1_INTR_STAT_5], ANSI_ESC_RESET_TEXT); /* Skip checking interrupts if GPIO pin is not asserted anymore */ for (intr_num = 0; (intr_num < MAX_INTR) && (is_interrupt_asserted()); intr_num++) { if (g_intr_tbl[intr_num].mask) { uint8_t aggregated_index, aggregated_id_bit, aggregated_status; aggregated_index = g_intr_tbl[intr_num].aggr_stat_index; aggregated_id_bit = g_intr_tbl[intr_num].aggr_stat_id_bit; aggregated_status = aggregated_intr_status[aggregated_index]; if (aggregated_status & aggregated_id_bit) { int reg_value; uint8_t intr_stat; reg_value = mhl_tx_read_reg(hw_context, g_intr_tbl [intr_num]. stat_addr); if (reg_value < 0) return; intr_stat = (uint8_t) reg_value; /* Process enabled interrupts. Ignore others */ intr_stat = intr_stat & g_intr_tbl[intr_num].mask; if (intr_stat) { int already_cleared; MHL_TX_DBG_INFO("INTR-%s = %02X\n", g_intr_tbl[intr_num]. name, intr_stat); already_cleared = g_intr_tbl[intr_num].isr(hw_context, intr_stat); if (already_cleared >= 0) { intr_stat &= ~already_cleared; if (intr_stat) { mhl_tx_write_reg (hw_context, g_intr_tbl [intr_num]. stat_addr, intr_stat); } } } } } } } /* default callbacks */ int cb_enum_begin(void *context) { MHL_TX_DBG_INFO("%senum begin%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); return 0; } int cb_enum_item(void *context, uint16_t columns, uint16_t rows, uint8_t bits_per_pixel, uint32_t vertical_refresh_rate_in_milliHz, uint16_t burst_id, union video_burst_descriptor_u *p_descriptor) { MHL_TX_DBG_INFO("%senum item%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); return 0; } int cb_enum_end(void *context) { MHL_TX_DBG_INFO("%senum end%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); return 0; } void cb_hpd_driven_low(void *context) { MHL_TX_DBG_WARN("%sdriven_low%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); } enum hpd_high_callback_status cb_hpd_driven_high(void *context, uint8_t *p_edid, size_t edid_length, uint8_t *p_emsc_edid, size_t emsc_edid_length, struct MHL3_hev_dtd_item_t *p_hev_dtd, size_t num_hev_dtds, struct MHL3_hev_vic_item_t *p_hev_vic, size_t num_hev_vics, struct MHL3_3d_dtd_item_t *p_3d_dtd_items, size_t num_3d_dtds, struct MHL3_3d_vic_item_t *p_3d_vic, size_t num_3d_vics, union avif_or_cea_861_dtd_u *p_avif_or_dtd, size_t avif_or_dtd_max_length, union vsif_mhl3_or_hdmi_u *p_vsif, size_t vsif_max_length) { MHL_TX_DBG_WARN("%sdriven_high%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); return HH_VIDEO_NOT_RDY; } static void input_field_rate_measurement_callback(void *callback_param) { struct drv_hw_context *hw_context = (struct drv_hw_context *)callback_param; start_video(hw_context); } /* * si_mhl_tx_chip_initialize * * Chip specific initialization. * This function resets and initializes the transmitter. * MHL Detection interrupt setups up the chip for video. */ int si_mhl_tx_chip_initialize(struct drv_hw_context *hw_context) { int ret_val; int status = -1; hw_context->pp_16bpp_override = pp_16bpp_automatic; set_pin(TX_FW_WAKE, 1); /* inverter on board */ platform_mhl_tx_hw_reset(TX_HW_RESET_PERIOD, TX_HW_RESET_DELAY); /* Power up the device. Keep HSIC and Rx cores powered down. */ mhl_tx_write_reg(hw_context, REG_DPD, BIT_DPD_PWRON_PLL | BIT_DPD_PDNTX12 | BIT_DPD_OSC_EN); if (!use_spi) { /* * SWWA BZ35174: Duplicate write to ensure proper * I2C interface functionality. Additional reset added to * address a fault in some application processor i2c driver's * ability to recover from an initial i2c failure. */ platform_mhl_tx_hw_reset(TX_HW_RESET_PERIOD, TX_HW_RESET_DELAY); mhl_tx_write_reg(hw_context, REG_DPD, BIT_DPD_PWRON_PLL | BIT_DPD_PDNTX12 | BIT_DPD_OSC_EN); } ret_val = get_device_rev(hw_context); hw_context->chip_rev_id = (uint8_t) ret_val; if ((0 == hw_context->chip_rev_id) || (ret_val == -1)) { MHL_TX_DBG_ERR("%sDevice missing or revision not supported%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); return status; } /* SWWA BZ35298: Dummy write to TX DDC to ensure proper * DDC interface functionality. */ mhl_tx_write_reg(hw_context, REG_DDC_MANUAL, BIT_DDC_MANUAL_MAN_DDC | 0x03); mhl_tx_write_reg(hw_context, REG_HDCP1X_LB_BIST, BIT_HDCP1X_LB_BIST_EN); mhl_tx_write_reg(hw_context, REG_DDC_MANUAL, 0x03); mhl_tx_write_reg(hw_context, REG_HDCP1X_LB_BIST, 0x00); MHL_TX_DBG_ERR("72:06 -- 0x%02x\n", mhl_tx_read_reg(hw_context, REG_OTP_DBYTE510)) si_set_cbus_mode_leds(CM_NO_CONNECTION); ret_val = get_device_id(hw_context); if (ret_val > 0) { struct mhl_dev_context *dev_context = container_of((void *)hw_context, struct mhl_dev_context, drv_context); hw_context->chip_device_id = (uint16_t) ret_val; MHL_TX_DBG_ERR("%x%s: Found SiI%04X rev: %01X.%01X%s\n", hw_context, ANSI_ESC_GREEN_TEXT, hw_context->chip_device_id, hw_context->chip_rev_id >> 4, (hw_context->chip_rev_id & 0x0F), ANSI_ESC_RESET_TEXT); #ifndef SWWA_BZ30759 /* Based on module parameter "crystal_khz=xxxxx". */ program_ext_clock_regs(hw_context, crystal_khz); #endif /* * Store transmitter's KSV in case it's requested by * someone else (probably the video source driver) */ mhl_tx_read_reg_block(hw_context, REG_AKSV_1, 5, hw_context->aksv); /* Initialize these before calling disconnect_mhl() */ hw_context->callbacks.display_timing_enum_begin = cb_enum_begin; hw_context->callbacks.display_timing_enum_item = cb_enum_item; hw_context->callbacks.display_timing_enum_end = cb_enum_end; hw_context->callbacks.hpd_driven_low = cb_hpd_driven_low; hw_context->callbacks.hpd_driven_high = cb_hpd_driven_high; /* Move to disconnected state. * Let RGND/MHL connection event start the driver */ disconnect_mhl(hw_context, true); switch_to_idle(hw_context, false); status = mhl_tx_create_timer(dev_context, input_field_rate_measurement_callback, hw_context, &hw_context->input_field_rate_measurement_timer); if (status != 0) { MHL_TX_DBG_ERR( "Failed to allocate FIELD_RATE timer\n"); } else { /* Don't allow timer to start prematurely */ MHL_TX_DBG_INFO( "stopping timer for FIELD_RATE measurement\n"); mhl_tx_stop_timer(dev_context, hw_context->input_field_rate_measurement_timer); } } return status; } void si_mhl_tx_drv_shutdown(struct drv_hw_context *hw_context) { set_pin(TX_FW_WAKE, 1); } int si_mhl_tx_drv_connection_is_mhl3(struct mhl_dev_context *dev_context) { struct drv_hw_context *hw_context = (struct drv_hw_context *)&dev_context->drv_context; return IN_MHL3_MODE(hw_context) ? 1 : 0; } int si_mhl_tx_drv_get_highest_tmds_link_speed(struct mhl_dev_context *dev_context) { int link_speed = MHL_XDC_TMDS_000; if (dev_context->xdev_cap_cache.mxdc.tmds_speeds & MHL_XDC_TMDS_600) { link_speed = MHL_XDC_TMDS_600; } else if (dev_context->xdev_cap_cache.mxdc.tmds_speeds & MHL_XDC_TMDS_300) { link_speed = MHL_XDC_TMDS_300; } else if (dev_context->xdev_cap_cache.mxdc.tmds_speeds & MHL_XDC_TMDS_150) { link_speed = MHL_XDC_TMDS_150; } return link_speed; } static void si_mhl_tx_drv_set_lowest_tmds_link_speed(struct mhl_dev_context *dev_context, uint32_t pixel_clock_frequency, uint8_t bits_per_pixel) { uint32_t link_clock_frequency; uint32_t top_clock_frequency = 147000000; /* init to lowest max */ uint8_t reg_val = 0; bool found_fit = false; bool fits_1_5, fits_3_0, fits_6_0; uint8_t av_link_param = 0; struct drv_hw_context *hw_context = (struct drv_hw_context *)&dev_context->drv_context; link_clock_frequency = pixel_clock_frequency * ((uint32_t) (bits_per_pixel >> 3)); fits_1_5 = false; fits_3_0 = false; fits_6_0 = false; /* Find lowest fit by finding all link speeds into which * this mode will fit, highest first. * Apply override if we can. */ /* If BIST is in progress, use the parameters in BIST_SETUP */ switch (hw_context->cbus_mode) { case CM_eCBUS_S_BIST: case CM_eCBUS_S_AV_BIST: switch (dev_context->bist_setup.avlink_data_rate) { case 1: /* 1.5 Gbps */ MHL_TX_DBG_ERR("AV LINK_DATA_RATE 1.5Gbps\n"); reg_val = VAL_TX_ZONE_CTL3_TX_ZONE_1_5GBPS; break; case 2: /* 3.0 Gbps */ MHL_TX_DBG_ERR("AV LINK_DATA_RATE 3.0Gbps\n"); reg_val = VAL_TX_ZONE_CTL3_TX_ZONE_3GBPS; break; case 3: /* 6.0 Gbps */ MHL_TX_DBG_ERR("AV LINK_DATA_RATE 6.0Gbps\n"); reg_val = VAL_TX_ZONE_CTL3_TX_ZONE_6GBPS; break; default: MHL_TX_DBG_ERR("Unsupported AVLINK_DATA_RATE %02X\n", (dev_context->bist_setup.avlink_data_rate)); return ; } break; default: if (link_clock_frequency <= 600000000) { MHL_TX_DBG_WARN( "Mode fit TMDS Link Speed = 6.0Gbps (%d)\n", link_clock_frequency); if (dev_context->xdev_cap_cache.mxdc.tmds_speeds & MHL_XDC_TMDS_600) { MHL_TX_DBG_INFO( "XDEVCAP TMDS Link Speed = 6.0Gbps is " "supported\n"); found_fit = true; fits_6_0 = true; /* 6000000 * 98; */ top_clock_frequency = 588000000; reg_val = VAL_TX_ZONE_CTL3_TX_ZONE_6GBPS; } } if (link_clock_frequency <= 300000000) { MHL_TX_DBG_WARN( "Mode fits TMDS Link Speed = 3.0Gbps (%d)\n", link_clock_frequency); if (dev_context->xdev_cap_cache.mxdc.tmds_speeds & MHL_XDC_TMDS_300) { MHL_TX_DBG_INFO( "XDEVCAP TMDS Link Speed = 3.0Gbps is " "supported\n"); found_fit = true; fits_3_0 = true; /* 3000000 * 98; */ top_clock_frequency = 294000000; reg_val = VAL_TX_ZONE_CTL3_TX_ZONE_3GBPS; } } if (link_clock_frequency <= 150000000) { MHL_TX_DBG_WARN( "Mode fits TMDS Link Speed = 1.5Gbps (%d)\n", link_clock_frequency); if (dev_context->xdev_cap_cache.mxdc.tmds_speeds & MHL_XDC_TMDS_150) { MHL_TX_DBG_INFO( "XDEVCAP TMDS Link Speed = 1.5Gbps is " "supported\n"); found_fit = true; fits_1_5 = true; /* 1500000 * 98; */ top_clock_frequency = 147000000; reg_val = VAL_TX_ZONE_CTL3_TX_ZONE_1_5GBPS; } } if (!found_fit) { MHL_TX_DBG_ERR( "Cannot fit mode to any supported TMDS Link " "Speeds\n"); MHL_TX_DBG_INFO("Forcing TMDS Link Speed = 6.0Gbps\n"); /* 6000000 * 98; */ top_clock_frequency = 588000000; reg_val = VAL_TX_ZONE_CTL3_TX_ZONE_6GBPS; } switch (platform_get_flags() & PLATFORM_FLAG_LINK_SPEED) { case PLATFORM_FLAG_1_5GBPS: if (fits_1_5) { MHL_TX_DBG_WARN("Module parameter forcing " "TMDS Link Speed = 1.5Gbps\n"); /* 1500000 * 98; */ top_clock_frequency = 147000000; reg_val = VAL_TX_ZONE_CTL3_TX_ZONE_1_5GBPS; } break; case PLATFORM_FLAG_3GBPS: if (fits_3_0) { MHL_TX_DBG_WARN("Module parameter forcing " "TMDS Link Speed = 3.0Gbps\n"); /* 3000000 * 98; */ top_clock_frequency = 294000000; reg_val = VAL_TX_ZONE_CTL3_TX_ZONE_3GBPS; } break; case PLATFORM_FLAG_6GBPS: if (fits_6_0) { MHL_TX_DBG_WARN("Module parameter forcing " "TMDS Link Speed = 6.0Gbps\n"); /* 6000000 * 98; */ top_clock_frequency = 588000000; reg_val = VAL_TX_ZONE_CTL3_TX_ZONE_6GBPS; } break; } } /* set tmds link speed */ mhl_tx_write_reg(hw_context, REG_MHL3_TX_ZONE_CTL, reg_val); /* set unlimited packet size only if not enough overhead */ MHL_TX_DBG_WARN("lcf = %d, tcf = %d\n", link_clock_frequency, top_clock_frequency); if (link_clock_frequency >= top_clock_frequency) { MHL_TX_DBG_ERR("3E1[3] <- 1 UNLIMITED MODE ON\n"); mhl_tx_modify_reg(hw_context, REG_M3_P0CTRL, BIT_M3_P0CTRL_MHL3_P0_PORT_EN | BIT_M3_P0CTRL_MHL3_P0_UNLIMIT_EN, BIT_M3_P0CTRL_MHL3_P0_PORT_EN | BIT_M3_P0CTRL_MHL3_P0_UNLIMIT_EN_ON); } else { MHL_TX_DBG_ERR("3E1[3] <- 0 UNLIMITED MODE OFF\n"); mhl_tx_modify_reg(hw_context, REG_M3_P0CTRL, BIT_M3_P0CTRL_MHL3_P0_PORT_EN | BIT_M3_P0CTRL_MHL3_P0_UNLIMIT_EN, BIT_M3_P0CTRL_MHL3_P0_PORT_EN | BIT_M3_P0CTRL_MHL3_P0_UNLIMIT_EN_OFF); } /* set write stat indicating this change */ /* 0b000 = 1.5Gbps, 0b001 = 3Gbps, 0b010 = 6.0Gbps */ switch (reg_val) { case VAL_TX_ZONE_CTL3_TX_ZONE_6GBPS: mhl_tx_modify_reg(dev_context, REG_M3_POSTM, MSK_M3_POSTM_RRP_DECODE, 0x40); av_link_param = 0x02; break; case VAL_TX_ZONE_CTL3_TX_ZONE_3GBPS: mhl_tx_modify_reg(dev_context, REG_M3_POSTM, MSK_M3_POSTM_RRP_DECODE, 0x40); av_link_param = 0x01; break; case VAL_TX_ZONE_CTL3_TX_ZONE_1_5GBPS: mhl_tx_modify_reg(dev_context, REG_M3_POSTM, MSK_M3_POSTM_RRP_DECODE, 0x38); av_link_param = 0x00; break; } switch (hw_context->cbus_mode) { case CM_eCBUS_S_BIST: case CM_eCBUS_D_BIST: case CM_eCBUS_S_AV_BIST: case CM_eCBUS_D_AV_BIST: break; default: si_mhl_tx_set_status(dev_context, true, MHL_STATUS_REG_AV_LINK_MODE_CONTROL, av_link_param); } } bool si_mhl_tx_drv_support_e_cbus_d(struct drv_hw_context *hw_context) { return false; } int si_mhl_tx_drv_cbus_ready_for_edid(struct mhl_dev_context *dev_context) { struct drv_hw_context *hw_context = (struct drv_hw_context *)&dev_context->drv_context; switch (hw_context->cbus_mode) { case CM_oCBUS_PEER_IS_MHL1_2: case CM_eCBUS_S: case CM_eCBUS_D: case CM_eCBUS_S_AV_BIST: case CM_eCBUS_D_AV_BIST: return 1; case CM_eCBUS_S_BIST: case CM_eCBUS_D_BIST: if (BIST_TRIGGER_ECBUS_TX_RX_MASK & dev_context->bist_trigger_info) return 0; return 1; default: return 0; } } uint16_t si_mhl_tx_drv_get_blk_rcv_buf_size(void) { return LOCAL_BLK_RCV_BUFFER_SIZE; } /* setup_sans_cbus1 */ void setup_sans_cbus1(struct mhl_dev_context *dev_context) { struct drv_hw_context *hw_context = (struct drv_hw_context *)(&dev_context->drv_context); enum hpd_control_mode mode; uint8_t dummy_edid[256] = { 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x4C, 0xAB, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x23, 0x17, 0x01, 0x03, 0x81, 0x56, 0x30, 0x78, 0x8A, 0xA5, 0x8E, 0xA6, 0x54, 0x4A, 0x9C, 0x26, 0x12, 0x45, 0x46, 0xAD, 0xCE, 0x00, 0x81, 0x40, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, 0x74, 0x00, 0x30, 0xF2, 0x70, 0x5A, 0x80, 0xB0, 0x58, 0x8A, 0x00, 0x56, 0xE1, 0x31, 0x00, 0x00, 0x1E, 0x9A, 0x29, 0xA0, 0xD0, 0x51, 0x84, 0x22, 0x30, 0x50, 0x98, 0x36, 0x00, 0x60, 0xE1, 0x31, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x32, 0x4B, 0x18, 0x3C, 0x0B, 0x00, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x53, 0x45, 0x33, 0x39, 0x55, 0x59, 0x30, 0x34, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x01, 0x65, 0x02, 0x03, 0x2A, 0x71, 0x4F, 0x06, 0x07, 0x02, 0x03, 0x15, 0x96, 0x11, 0x12, 0x13, 0x04, 0x14, 0x05, 0x1F, 0x90, 0x20, 0x23, 0x09, 0x07, 0x07, 0x83, 0x01, 0x00, 0x00, 0x6D, 0x03, 0x0C, 0x00, 0x30, 0x00, 0x00, 0x3C, 0x20, 0x40, 0x68, 0x01, 0x02, 0x03, 0x8C, 0x0A, 0xD0, 0x90, 0x20, 0x40, 0x31, 0x20, 0x0C, 0x40, 0x55, 0x00, 0x56, 0xE1, 0x31, 0x00, 0x00, 0x18, 0x01, 0x1D, 0x80, 0x18, 0x71, 0x1C, 0x16, 0x20, 0x58, 0x2C, 0x25, 0x00, 0x56, 0xE1, 0x31, 0x00, 0x00, 0x9E, 0x01, 0x1D, 0x80, 0xD0, 0x72, 0x1C, 0x16, 0x20, 0x10, 0x2C, 0x25, 0x80, 0x56, 0xE1, 0x31, 0x00, 0x00, 0x9E, 0x01, 0x1D, 0x00, 0xBC, 0x52, 0xD0, 0x1E, 0x20, 0xB8, 0x28, 0x55, 0x40, 0x56, 0xE1, 0x31, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80 }; uint16_t length = sizeof(dummy_edid); int cstat_p3; enable_intr(hw_context, INTR_EDID, 0); mhl_tx_write_reg(hw_context, REG_EDID_CTRL, BIT_EDID_CTRL_EDID_FIFO_ADDR_AUTO); mhl_tx_write_reg(hw_context, REG_HDCP2X_POLL_CS, 0x71); mhl_tx_write_reg(hw_context, REG_HDCP2X_CTRL_0, 0x02); enable_intr(hw_context, INTR_EDID, 0); init_rx_regs(hw_context); /* choose EDID instead of devcap to appear at the FIFO */ mhl_tx_write_reg(hw_context, REG_EDID_CTRL, VAL_EDID_CTRL_EDID_PRIME_VALID_DISABLE | VAL_EDID_CTRL_DEVCAP_SELECT_EDID | VAL_EDID_CTRL_EDID_FIFO_ADDR_AUTO_ENABLE | VAL_EDID_CTRL_EDID_MODE_EN_ENABLE); mhl_tx_write_reg(hw_context, REG_EDID_FIFO_ADDR, 0); mhl_tx_write_reg_block(hw_context, REG_EDID_FIFO_WR_DATA, sizeof(dummy_edid), dummy_edid); mhl_tx_write_reg(hw_context, REG_EDID_CTRL, VAL_EDID_CTRL_EDID_PRIME_VALID_ENABLE | VAL_EDID_CTRL_DEVCAP_SELECT_EDID | VAL_EDID_CTRL_EDID_FIFO_ADDR_AUTO_ENABLE | VAL_EDID_CTRL_EDID_MODE_EN_ENABLE); enable_intr(hw_context, INTR_SCDT, BIT_INTR_SCDT_CHANGE); /* sample REG_TMDS_CSTAT_P3 before driving upstream HDP high */ cstat_p3 = mhl_tx_read_reg(hw_context, REG_TMDS_CSTAT_P3); /* disable auto-clear */ cstat_p3 |= BIT_TMDS_CSTAT_P3_DISABLE_AUTO_AVIF_CLEAR; #ifdef MANUAL_INFO_FRAME_CLEAR_AT_HPD_DRIVEN_HIGH cstat_p3 |= BIT_TMDS_CSTAT_P3_AVIF_MANUAL_CLEAR_STROBE; #endif mhl_tx_write_reg(hw_context, REG_TMDS_CSTAT_P3, cstat_p3); drive_hpd_low(hw_context); msleep(100); /* clear fifo over/under flow, if any */ mhl_tx_write_reg(hw_context, REG_INTR5, 0xFF); mode = platform_get_hpd_control_mode(); if (HPD_CTRL_OPEN_DRAIN == mode) mhl_tx_write_reg(hw_context, REG_HPD_CTRL, BITS_HPD_CTRL_OPEN_DRAIN_HIGH | BIT_HPD_CTRL_HPD_DS_SIGNAL); else if (HPD_CTRL_PUSH_PULL == mode) mhl_tx_write_reg(hw_context, REG_HPD_CTRL, BITS_HPD_CTRL_PUSH_PULL_HIGH | BIT_HPD_CTRL_HPD_DS_SIGNAL); else MHL_TX_DBG_ERR("%sUnexpected HPD mode!%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); MHL_TX_DBG_ERR("%sHPD high for BIST%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); /* Enable SCDT interrupt to detect stable incoming clock */ enable_intr(hw_context, INTR_SCDT, BIT_INTR_SCDT_CHANGE); do_hpd_driven_high_callback(hw_context, dummy_edid, length); } void si_mhl_tx_drv_start_avlink_bist(struct mhl_dev_context *dev_context, struct bist_setup_info *test_info) { struct drv_hw_context *hw_context = (struct drv_hw_context *)(&dev_context->drv_context); uint32_t frame_count = 0; uint8_t start_cmd; uint8_t bist_mode_sel; switch (test_info->avlink_video_mode) { case 4: /* 1280 X 720 (720P) */ MHL_TX_DBG_ERR("AV LINK_VIDEO_MODE 720p60\n"); bist_mode_sel = 5; break; case 3: /* 720 X 480 (480P) */ default: MHL_TX_DBG_ERR("AV LINK_VIDEO_MODE 480p60\n"); bist_mode_sel = 6; break; /* remaining cases are pre-checked in invalid_bist_parms() */ } mhl_tx_write_reg(hw_context, REG_BIST_VIDEO_MODE, bist_mode_sel); switch (test_info->avlink_data_rate) { case 1: /* 1.5 Gbps */ MHL_TX_DBG_ERR("AV LINK_DATA_RATE 1.5Gbps\n"); bist_mode_sel = VAL_TX_ZONE_CTL3_TX_ZONE_1_5GBPS; mhl_tx_modify_reg(hw_context, REG_M3_POSTM, MSK_M3_POSTM_RRP_DECODE, 0x38); break; case 2: /* 3.0 Gbps */ MHL_TX_DBG_ERR("AV LINK_DATA_RATE 3.0Gbps\n"); bist_mode_sel = VAL_TX_ZONE_CTL3_TX_ZONE_3GBPS; mhl_tx_modify_reg(hw_context, REG_M3_POSTM, MSK_M3_POSTM_RRP_DECODE, 0x40); break; case 3: /* 6.0 Gbps */ default: MHL_TX_DBG_ERR("AV LINK_DATA_RATE 6.0Gbps\n"); bist_mode_sel = VAL_TX_ZONE_CTL3_TX_ZONE_6GBPS; mhl_tx_modify_reg(hw_context, REG_M3_POSTM, MSK_M3_POSTM_RRP_DECODE, 0x40); break; /* remaining cases are pre-checked in invalid_bist_parms() */ } mhl_tx_write_reg(hw_context, REG_MHL3_TX_ZONE_CTL, bist_mode_sel); switch (test_info->avlink_pattern) { case BIST_AVLINK_PATTERN_UNSPECIFIED: case BIST_AVLINK_PATTERN_PRBS: MHL_TX_DBG_ERR("AV LINK_PATTERN %sPRBS%s %s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT, si_mhl_tx_drv_get_cbus_mode_str( hw_context->cbus_mode)) mhl_tx_write_reg(hw_context, REG_BIST_TEST_SEL, 0x03); break; case BIST_AVLINK_PATTERN_FIXED_8: MHL_TX_DBG_ERR("AV LINK_PATTERN %sFixed8%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); mhl_tx_write_reg(hw_context, REG_BIST_8BIT_PATTERN, (uint8_t) test_info->avlink_fixed_pat); mhl_tx_write_reg(hw_context, REG_BIST_TEST_SEL, 0x09); break; case BIST_AVLINK_PATTERN_FIXED_10: MHL_TX_DBG_ERR("AV LINK_PATTERN %sFixed10%s\n", ANSI_ESC_GREEN_TEXT, ANSI_ESC_RESET_TEXT); mhl_tx_write_reg(hw_context, REG_BIST_TEST_SEL, 0x0A); if (test_info->avlink_fixed_pat & 0x8000) { /* alternate with bitwise inverse */ mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_INST_LOW, 0x8B); } else{ /* same 10-bit pattern every cycle */ mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_INST_LOW, 0x81); } mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_INST_HIGH, 0x07); mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_PAT_LOW, (uint8_t) test_info->avlink_fixed_pat); { uint8_t STX_1, STX_0, STX, E98_pre, pat_lsb, pat_msb; pat_lsb = (uint8_t) test_info->avlink_fixed_pat; pat_msb = (uint8_t) (test_info->avlink_fixed_pat >> 8); STX_1 = ((pat_lsb >> 1) ^ (pat_lsb >> 3) ^ (pat_lsb >> 4) ^ (pat_lsb >> 5) ^ (pat_lsb >> 7)) & 0x01; STX_0 = ((pat_lsb >> 0) ^ (pat_lsb >> 2) ^ (pat_lsb >> 4) ^ (pat_lsb >> 6)) & 0x01; STX = (STX_1 << 1) | STX_0; E98_pre = ((pat_msb) ^ STX) & 0x3; mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_PAT_HIGH, E98_pre); } /* Turn on TX BIST SEL, don't enable TX BIST */ mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_CNTLSTA, 0x01); break; /* remaining cases are pre-checked in invalid_bist_parms() */ default: MHL_TX_DBG_ERR("%sUnrecognized test pattern detected!%s\n", ANSI_ESC_RED_TEXT, ANSI_ESC_RESET_TEXT); } if (test_info->avlink_duration) { if (test_info->avlink_pattern == BIST_AVLINK_PATTERN_FIXED_8) frame_count = test_info->avlink_duration * 32; } mhl_tx_write_reg(hw_context, REG_BIST_DURATION_0, (uint8_t) frame_count); frame_count >>= 8; mhl_tx_write_reg(hw_context, REG_BIST_DURATION_1, (uint8_t) frame_count); frame_count >>= 8; mhl_tx_write_reg(hw_context, REG_BIST_DURATION_2, (uint8_t) frame_count); start_cmd = BIT_BIST_EN | BIT_BIST_ALWAYS_ON | BIT_BIST_TRANS; mhl_tx_write_reg(hw_context, REG_BIST_CTRL, start_cmd); video_sans_cbus1(dev_context); } void si_mhl_tx_drv_stop_avlink_bist(struct drv_hw_context *hw_context) { MHL_TX_DBG_ERR("AV_LINK BIST stop\n") mhl_tx_write_reg(hw_context, REG_BIST_CTRL, 0x00); /* Stop IPBIST Fix10. */ mhl_tx_modify_reg(hw_context, REG_TX_IP_BIST_CNTLSTA, BIT_TX_IP_BIST_CNTLSTA_TXBIST_SEL | BIT_TX_IP_BIST_CNTLSTA_TXBIST_EN, 0x00); mhl_tx_write_reg(hw_context, REG_M3_SCTRL, 0x41); #ifdef CoC_FSM_MONITORING #ifdef BIST_MONITORING mhl_tx_modify_reg(hw_context, REG_GPIO_CTRL1, BIT_CTRL1_GPIO_I_6, 0); #endif #endif } void si_mhl_tx_drv_start_ecbus_bist(struct drv_hw_context *hw_context, struct bist_setup_info *test_info) { struct mhl_dev_context *dev_context; dev_context = container_of((void *)hw_context, struct mhl_dev_context, drv_context); #ifdef CoC_FSM_MONITORING #ifdef BIST_MONITORING mhl_tx_modify_reg(hw_context, REG_GPIO_CTRL1, BIT_CTRL1_GPIO_I_7, BIT_CTRL1_GPIO_I_7); #endif #endif MHL_TX_DBG_ERR("\n") if (dev_context->bist_trigger_info & BIST_TRIGGER_TEST_E_CBUS_D) { hw_context->cbus_mode = CM_eCBUS_D_BIST; si_set_cbus_mode_leds(hw_context->cbus_mode); } else { MHL_TX_DBG_ERR("\n") hw_context->cbus_mode = CM_eCBUS_S_BIST; si_set_cbus_mode_leds(hw_context->cbus_mode); } } int32_t si_mhl_tx_drv_get_ecbus_bist_status( struct mhl_dev_context *dev_context, uint8_t *rx_run_done, uint8_t *tx_run_done) { struct drv_hw_context *hw_context = (struct drv_hw_context *)&dev_context->drv_context; int err_cnt = BIST_LOCAL_COUNT_INVALID; if (dev_context->bist_trigger_info & BIST_TRIGGER_TEST_E_CBUS_D) { mhl_tx_write_reg(hw_context, REG_DOC_CTL7, 0x00); err_cnt = mhl_tx_read_reg(hw_context, REG_DOC_STAT_9) & 0x0F; err_cnt <<= 8; err_cnt |= mhl_tx_read_reg(hw_context, REG_DOC_STAT_8); } else { uint8_t coc_status[6]; int i; /* Trigger to update error counter */ mhl_tx_modify_reg(hw_context, REG_COC_CTL15, MSK_COC_CTL15_COC_CONTROL15_6_4, 0x00); memset(coc_status, 0, ARRAY_SIZE(coc_status)); mhl_tx_read_reg_block(hw_context, REG_COC_STAT_0, sizeof(coc_status), coc_status); *tx_run_done = coc_status[1] & BIT_COC_STAT_1_TX_RUN_DONE; *rx_run_done = coc_status[2] & BIT_COC_STAT_2_RX_RUN_DONE; for (err_cnt = 0, i = 3; i < sizeof(coc_status); ++i) { err_cnt <<= 8; err_cnt |= coc_status[i]; } MHL_TX_DBG_INFO("err_cnt: 0x%x\n" "coc_status[0]: 0x%02x\n" "coc_status[1]: 0x%02x\n" "coc_status[2]: 0x%02x\n", err_cnt, coc_status[0], coc_status[1], coc_status[2]) memcpy(hw_context->prev_bist_coc_status, coc_status, sizeof(hw_context->prev_bist_coc_status)); } return err_cnt; } void si_mhl_tx_drv_stop_ecbus_bist(struct drv_hw_context *hw_context, struct bist_setup_info *test_info) { struct mhl_dev_context *dev_context; dev_context = container_of((void *)hw_context, struct mhl_dev_context, drv_context); if (dev_context->bist_trigger_info & BIST_TRIGGER_TEST_E_CBUS_D) { MHL_TX_DBG_INFO ("eCBUS State Machine: " "0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X\n", mhl_tx_read_reg(hw_context, REG_COC_STAT_0), mhl_tx_read_reg(hw_context, REG_COC_STAT_1), mhl_tx_read_reg(hw_context, REG_COC_STAT_2), mhl_tx_read_reg(hw_context, REG_COC_STAT_3), mhl_tx_read_reg(hw_context, REG_COC_STAT_4), mhl_tx_read_reg(hw_context, REG_COC_STAT_5)); } else { MHL_TX_DBG_INFO ("eCBUS State Machine: " "0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X\n", mhl_tx_read_reg(hw_context, REG_COC_STAT_0), mhl_tx_read_reg(hw_context, REG_COC_STAT_1), mhl_tx_read_reg(hw_context, REG_COC_STAT_2), mhl_tx_read_reg(hw_context, REG_COC_STAT_3), mhl_tx_read_reg(hw_context, REG_COC_STAT_4), mhl_tx_read_reg(hw_context, REG_COC_STAT_5)); mhl_tx_write_reg(hw_context, REG_COC_CTL11, 0x08); mhl_tx_write_reg(hw_context, REG_TTXNUMB, 0x0C); } #ifdef CoC_FSM_MONITORING #ifdef BIST_MONITORING mhl_tx_modify_reg(hw_context, REG_GPIO_CTRL1, BIT_CTRL1_GPIO_I_7, 0); #endif #endif } int si_mhl_tx_drv_start_impedance_bist(struct drv_hw_context *hw_context, struct bist_setup_info *test_info) { int status = 0; switch (test_info->impedance_mode) { case BIST_IMPEDANCE_MODE_AVLINK_TX_LOW: case BIST_IMPEDANCE_MODE_AVLINK_TX_HIGH: mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_INST_LOW, 0x01); mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_INST_HIGH, 0x07); if (test_info->impedance_mode == BIST_IMPEDANCE_MODE_AVLINK_TX_LOW) { mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_PAT_LOW, 0x00); mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_PAT_HIGH, 0x00); } else { mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_PAT_LOW, 0xFF); mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_PAT_HIGH, 0x01); } mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_CNTLSTA, BIT_TX_IP_BIST_CNTLSTA_TXBIST_SEL | BIT_TX_IP_BIST_CNTLSTA_TXBIST_EN); break; case BIST_IMPEDANCE_MODE_ECBUS_S_TX_LOW: case BIST_IMPEDANCE_MODE_ECBUS_S_TX_HIGH: mhl_tx_write_reg(hw_context, REG_TEST_TXCTRL, 0x81); mhl_tx_write_reg(hw_context, REG_MHL_PLL_CTL2, 0x00); mhl_tx_write_reg(hw_context, REG_M3_CTRL, 0x0E); mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL2, 0x0B); mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL5, 0x3D); mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL0, 0xF0); mhl_tx_write_reg(hw_context, REG_MHL_COC_CTL0, 0xC5); mhl_tx_write_reg(hw_context, REG_COC_CTL0, 0x18); if (test_info->impedance_mode == BIST_IMPEDANCE_MODE_ECBUS_S_TX_LOW) { mhl_tx_write_reg(hw_context, REG_COC_CTL1, 0x00); mhl_tx_write_reg(hw_context, REG_COC_CTL2, 0x00); } else { mhl_tx_write_reg(hw_context, REG_COC_CTL1, 0x28); mhl_tx_write_reg(hw_context, REG_COC_CTL2, 0x28); } mhl_tx_write_reg(hw_context, REG_TTXNUMB, 0x05); mhl_tx_write_reg(hw_context, REG_COC_CTLB, 0xFF); mhl_tx_write_reg(hw_context, REG_COC_CTLC, 0xFF); mhl_tx_write_reg(hw_context, REG_COC_CTLD, 0x00); mhl_tx_write_reg(hw_context, REG_COC_CTLE, 0x18); mhl_tx_write_reg(hw_context, REG_ALICE0_ZONE_CTRL, 0xE8); mhl_tx_write_reg(hw_context, REG_BGR_BIAS, 0x87); mhl_tx_write_reg(hw_context, REG_ALICE0_ZONE_CTRL, 0xD0); mhl_tx_write_reg(hw_context, REG_COC_CTL3, 0x3F); mhl_tx_write_reg(hw_context, REG_COC_CTL6, 0x10); mhl_tx_write_reg(hw_context, REG_MHL_COC_CTL1, 0xCD); mhl_tx_write_reg(hw_context, REG_MHL_COC_CTL4, 0x2A); mhl_tx_write_reg(hw_context, REG_MHL_PLL_CTL2, 0x80); mhl_tx_write_reg(hw_context, REG_M3_SCTRL, 0x40); mhl_tx_write_reg(hw_context, REG_M3_P0CTRL, BIT_M3_P0CTRL_MHL3_P0_PORT_EN); mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_INST_LOW, 0x01); mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_INST_HIGH, 0x03); mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_PAT_LOW, 0xFF); mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_PAT_HIGH, 0x03); mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_CONF_LOW, 0x08); mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_CONF_HIGH, 0x00); mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_CNTLSTA, BIT_TX_IP_BIST_CNTLSTA_TXBIST_SEL | BIT_TX_IP_BIST_CNTLSTA_TXBIST_EN); mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_CNTLSTA, BIT_TX_IP_BIST_CNTLSTA_TXBIST_SEL | BIT_TX_IP_BIST_CNTLSTA_TXBIST_EN | BIT_TX_IP_BIST_CNTLSTA_TXBIST_RUN | BIT_TX_IP_BIST_CNTLSTA_TXBIST_ON); mhl_tx_write_reg(hw_context, REG_COC_CTL11, 0xF0); mhl_tx_write_reg(hw_context, REG_COC_CTL7, 0x01); mhl_tx_write_reg(hw_context, REG_COC_CTL14, 0x0C); mhl_tx_write_reg(hw_context, REG_COC_CTL15, 0x80); break; case BIST_IMPEDANCE_MODE_ECBUS_D_TX_LOW: case BIST_IMPEDANCE_MODE_ECBUS_D_TX_HIGH: mhl_tx_write_reg(hw_context, REG_TEST_TXCTRL, 0x01); mhl_tx_write_reg(hw_context, REG_MHL_PLL_CTL0, 0x02); mhl_tx_write_reg(hw_context, REG_M3_CTRL, 0x0E); mhl_tx_write_reg(hw_context, REG_MHL_TOP_CTL, 0x82); mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL0, 0xF3); mhl_tx_write_reg(hw_context, REG_MHL_COC_CTL1, 0x07); mhl_tx_write_reg(hw_context, REG_MHL_DOC_CTL0, 0x81); mhl_tx_write_reg(hw_context, REG_MHL3_TX_ZONE_CTL, 0x00); mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL2, 0x0B); mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL5, 0x3B); mhl_tx_write_reg(hw_context, REG_DOC_CTL0, 0x02); mhl_tx_write_reg(hw_context, REG_DOC_CTL7, 0x06); mhl_tx_write_reg(hw_context, REG_DOC_CTLE, 0x0F); mhl_tx_write_reg(hw_context, REG_DOC_CFG4, 0x00); mhl_tx_write_reg(hw_context, REG_DOC_CTL6, 0x80); mhl_tx_write_reg(hw_context, REG_DOC_CFG4, 0x08); mhl_tx_write_reg(hw_context, REG_DOC_CTL6, 0x80); mhl_tx_write_reg(hw_context, REG_DOC_CFG4, 0x09); mhl_tx_write_reg(hw_context, REG_DOC_CTL6, 0x80); mhl_tx_write_reg(hw_context, REG_DOC_CFG4, 0x0A); mhl_tx_write_reg(hw_context, REG_DOC_CTL6, 0x80); mhl_tx_write_reg(hw_context, REG_DOC_CFG4, 0x0B); mhl_tx_write_reg(hw_context, REG_DOC_CTL6, 0x80); mhl_tx_write_reg(hw_context, REG_DOC_CFG4, 0x02); mhl_tx_write_reg(hw_context, REG_DOC_CTL6, 0x80); if (test_info->impedance_mode == BIST_IMPEDANCE_MODE_ECBUS_D_TX_HIGH) mhl_tx_write_reg(hw_context, REG_DOC_CTL6, 0x60); mhl_tx_write_reg(hw_context, REG_DOC_CTL7, 0x1E); mhl_tx_write_reg(hw_context, REG_DOC_CTLE, 0x0F); mhl_tx_write_reg(hw_context, REG_DOC_CTL0, 0x60); break; default: MHL_TX_DBG_ERR("Invalid value 0x%02x specified in " "IMPEDANCE_MODE field\n", test_info->impedance_mode); status = -EINVAL; } return status; } void si_mhl_tx_drv_stop_impedance_bist(struct drv_hw_context *hw_context, struct bist_setup_info *test_info) { switch (test_info->impedance_mode) { case BIST_IMPEDANCE_MODE_AVLINK_TX_LOW: case BIST_IMPEDANCE_MODE_AVLINK_TX_HIGH: mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_CNTLSTA, 0); break; case BIST_IMPEDANCE_MODE_ECBUS_S_TX_LOW: case BIST_IMPEDANCE_MODE_ECBUS_S_TX_HIGH: /* Restore previous values */ mhl_tx_write_reg(hw_context, REG_TEST_TXCTRL, 0x80); mhl_tx_write_reg(hw_context, REG_MHL_PLL_CTL2, 0x00); mhl_tx_write_reg(hw_context, REG_M3_CTRL, 0x07); mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL2, 0x2F); mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL5, 0x3F); mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL0, 0xBC); mhl_tx_write_reg(hw_context, REG_MHL_COC_CTL0, 0xC3); mhl_tx_write_reg(hw_context, REG_COC_CTL0, 0x5C); mhl_tx_write_reg(hw_context, REG_COC_CTL1, 0x0A); mhl_tx_write_reg(hw_context, REG_COC_CTL2, 0x14); mhl_tx_write_reg(hw_context, REG_TTXNUMB, 0x04); mhl_tx_write_reg(hw_context, REG_COC_CTLB, 0xFF); mhl_tx_write_reg(hw_context, REG_COC_CTLC, 0xFF); mhl_tx_write_reg(hw_context, REG_COC_CTLD, 0x21); mhl_tx_write_reg(hw_context, REG_COC_CTLE, 0x18); mhl_tx_write_reg(hw_context, REG_ALICE0_ZONE_CTRL, 0xE8); mhl_tx_write_reg(hw_context, REG_BGR_BIAS, 0x87); mhl_tx_write_reg(hw_context, REG_COC_CTL3, 0x40); mhl_tx_write_reg(hw_context, REG_COC_CTL6, 0x00); mhl_tx_write_reg(hw_context, REG_MHL_COC_CTL1, 0xC7); mhl_tx_write_reg(hw_context, REG_MHL_COC_CTL4, 0x2A); mhl_tx_write_reg(hw_context, REG_MHL_PLL_CTL2, 0x80); mhl_tx_write_reg(hw_context, REG_M3_SCTRL, 0x41); mhl_tx_write_reg(hw_context, REG_M3_P0CTRL, BIT_M3_P0CTRL_MHL3_P0_HDCP_EN); mhl_tx_write_reg(hw_context, REG_TX_IP_BIST_CNTLSTA, 0x00); mhl_tx_write_reg(hw_context, REG_COC_CTL7, 0x00); mhl_tx_write_reg(hw_context, REG_COC_CTL14, 0x00); break; case BIST_IMPEDANCE_MODE_ECBUS_D_TX_LOW: case BIST_IMPEDANCE_MODE_ECBUS_D_TX_HIGH: mhl_tx_write_reg(hw_context, REG_TEST_TXCTRL, 0x80); mhl_tx_write_reg(hw_context, REG_MHL_PLL_CTL0, 0x07); mhl_tx_write_reg(hw_context, REG_M3_CTRL, 0x07); mhl_tx_write_reg(hw_context, REG_MHL_TOP_CTL, 0x00); mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL0, 0xBC); mhl_tx_write_reg(hw_context, REG_MHL_DOC_CTL0, 0x00); mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL2, 0x2F); mhl_tx_write_reg(hw_context, REG_MHL_DP_CTL5, 0x3F); mhl_tx_write_reg(hw_context, REG_DOC_CTL0, 0x40); mhl_tx_write_reg(hw_context, REG_DOC_CTL7, 0x00); mhl_tx_write_reg(hw_context, REG_DOC_CTLE, 0x00); } } int si_mhl_tx_drv_sample_edid_buffer(struct drv_hw_context *hw_context, uint8_t *edid_buf) { mhl_tx_write_reg(hw_context, REG_EDID_FIFO_ADDR, 0); mhl_tx_read_reg_block(hw_context, REG_EDID_FIFO_RD_DATA, 256, edid_buf); return 0; } int si_mhl_tx_drv_get_pp_16bpp_override(struct mhl_dev_context *dev_context) { struct drv_hw_context *hw_context = (struct drv_hw_context *)&dev_context->drv_context; return (int)hw_context->pp_16bpp_override; } void si_mhl_tx_drv_set_pp_16bpp_override(struct mhl_dev_context *dev_context, int override) { struct drv_hw_context *hw_context = (struct drv_hw_context *)&dev_context->drv_context; hw_context->pp_16bpp_override = (enum pp_16bpp_override_t)override; } int si_mhl_tx_drv_get_hpd_status(struct mhl_dev_context *dev_context) { struct drv_hw_context *hw_context = (struct drv_hw_context *)&dev_context->drv_context; int ret_val; ret_val = mhl_tx_read_reg(hw_context, REG_CBUS_STATUS); if (ret_val < 0) return 0; else if (ret_val & BIT_CBUS_STATUS_CBUS_HPD) return 1; else return 0; } uint32_t si_mhl_tx_drv_get_hdcp2_status(struct mhl_dev_context *dev_context) { struct drv_hw_context *hw_context = (struct drv_hw_context *)&dev_context->drv_context; uint32_t ret_val; uint8_t ro_gp0; uint8_t ro_auth[2]; /* Disable high-value content / enable mute */ ro_gp0 = mhl_tx_read_reg(hw_context, REG_HDCP2X_GP_OUT0); mhl_tx_read_reg_block(hw_context, REG_HDCP2X_AUTH_STAT, sizeof(ro_auth), ro_auth); ret_val = (ro_gp0 << 16) |(ro_auth[1] << 8) | ro_auth[0]; return ret_val; }