static void mhl_drive_hpd(struct mhl_tx_ctrl *mhl_ctrl, uint8_t to_state) { struct i2c_client *client = mhl_ctrl->i2c_handle; pr_debug("%s: To state=[0x%x]\n", __func__, to_state); if (to_state == HPD_UP) { /* * Drive HPD to UP state * * The below two reg configs combined * enable TMDS output. */ /* Enable TMDS on TMDS_CCTRL */ MHL_SII_REG_NAME_MOD(REG_TMDS_CCTRL, BIT4, BIT4); /* * Set HPD_OUT_OVR_EN = HPD State * EDID read and Un-force HPD (from low) * propogate to src let HPD float by clearing * HPD OUT OVRRD EN */ MHL_SII_REG_NAME_MOD(REG_INT_CTRL, BIT4, 0x00); } else { /* * Drive HPD to DOWN state * Disable TMDS Output on REG_TMDS_CCTRL * Enable/Disable TMDS output (MHL TMDS output only) */ MHL_SII_REG_NAME_MOD(REG_INT_CTRL, BIT4, BIT4); MHL_SII_REG_NAME_MOD(REG_TMDS_CCTRL, BIT4, 0x00); } return; }
static void release_usb_switch_open(struct mhl_tx_ctrl *mhl_ctrl) { struct i2c_client *client = mhl_ctrl->i2c_handle; msleep(50); MHL_SII_REG_NAME_MOD(REG_DISC_CTRL6, BIT6, 0x00); MHL_SII_REG_NAME_MOD(REG_DISC_CTRL1, BIT0, BIT0); }
static void switch_mode(struct mhl_tx_ctrl *mhl_ctrl, enum mhl_st_type to_mode, bool hpd_off) { struct i2c_client *client = mhl_ctrl->i2c_handle; unsigned long flags; int rc; struct msm_hdmi_mhl_ops *hdmi_mhl_ops = mhl_ctrl->hdmi_mhl_ops; pr_debug("%s: tx pwr on\n", __func__); spin_lock_irqsave(&mhl_ctrl->lock, flags); mhl_ctrl->tx_powered_off = false; spin_unlock_irqrestore(&mhl_ctrl->lock, flags); switch (to_mode) { case POWER_STATE_D0_NO_MHL: mhl_ctrl->cur_state = to_mode; mhl_init_reg_settings(mhl_ctrl, true); /* REG_DISC_CTRL1 */ MHL_SII_REG_NAME_MOD(REG_DISC_CTRL1, BIT1 | BIT0, BIT0); /* TPI_DEVICE_POWER_STATE_CTRL_REG */ mhl_i2c_reg_modify(client, TX_PAGE_TPI, 0x001E, BIT1 | BIT0, 0x00); break; case POWER_STATE_D0_MHL: mhl_ctrl->cur_state = to_mode; break; case POWER_STATE_D3: if (mhl_ctrl->cur_state == POWER_STATE_D3) { pr_debug("%s: mhl tx already in low power mode\n", __func__); break; } /* Force HPD to 0 when not in MHL mode. */ mhl_drive_hpd(mhl_ctrl, HPD_DOWN); mhl_tmds_ctrl(mhl_ctrl, TMDS_DISABLE); /* * Change TMDS termination to high impedance * on disconnection. */ MHL_SII_REG_NAME_WR(REG_MHLTX_CTL1, 0xD0); msleep(50); if (!mhl_ctrl->disc_enabled) MHL_SII_REG_NAME_MOD(REG_DISC_CTRL1, BIT1 | BIT0, 0x00); if (hdmi_mhl_ops && hpd_off) { rc = hdmi_mhl_ops->set_upstream_hpd( mhl_ctrl->pdata->hdmi_pdev, 0); pr_debug("%s: hdmi unset hpd %s\n", __func__, rc ? "failed" : "passed"); } mhl_ctrl->cur_state = POWER_STATE_D3; break; default: break; } }
void mhl_tmds_ctrl(struct mhl_tx_ctrl *mhl_ctrl, uint8_t on) { struct i2c_client *client = mhl_ctrl->i2c_handle; if (on) { MHL_SII_REG_NAME_MOD(REG_TMDS_CCTRL, BIT4, BIT4); mhl_drive_hpd(mhl_ctrl, HPD_UP); } else { MHL_SII_REG_NAME_MOD(REG_TMDS_CCTRL, BIT4, 0x00); } }
static void force_usb_switch_open(struct mhl_tx_ctrl *mhl_ctrl) { struct i2c_client *client = mhl_ctrl->i2c_handle; /*disable discovery*/ MHL_SII_REG_NAME_MOD(REG_DISC_CTRL1, BIT0, 0); /* force USB ID switch to open*/ MHL_SII_REG_NAME_MOD(REG_DISC_CTRL6, BIT6, BIT6); MHL_SII_REG_NAME_WR(REG_DISC_CTRL3, 0x86); /* force HPD to 0 when not in mhl mode. */ MHL_SII_REG_NAME_MOD(REG_INT_CTRL, BIT5 | BIT4, BIT4); }
static void cbus_reset(struct mhl_tx_ctrl *mhl_ctrl) { uint8_t i; struct i2c_client *client = mhl_ctrl->i2c_handle; /* * REG_SRST */ MHL_SII_REG_NAME_MOD(REG_SRST, BIT3, BIT3); msleep(20); MHL_SII_REG_NAME_MOD(REG_SRST, BIT3, 0x00); /* * REG_INTR1 and REG_INTR4 */ MHL_SII_REG_NAME_WR(REG_INTR1_MASK, BIT6); MHL_SII_REG_NAME_WR(REG_INTR4_MASK, BIT0 | BIT2 | BIT3 | BIT4 | BIT5 | BIT6); if (mhl_ctrl->chip_rev_id < 1) MHL_SII_REG_NAME_WR(REG_INTR5_MASK, BIT3 | BIT4); else MHL_SII_REG_NAME_WR(REG_INTR5_MASK, 0x00); /* Unmask CBUS1 Intrs */ MHL_SII_REG_NAME_WR(REG_CBUS_INTR_ENABLE, BIT2 | BIT3 | BIT4 | BIT5 | BIT6); /* Unmask CBUS2 Intrs */ MHL_SII_REG_NAME_WR(REG_CBUS_MSC_INT2_ENABLE, BIT2 | BIT3); for (i = 0; i < 4; i++) { /* * Enable WRITE_STAT interrupt for writes to * all 4 MSC Status registers. */ MHL_SII_CBUS_WR((0xE0 + i), 0xFF); /* * Enable SET_INT interrupt for writes to * all 4 MSC Interrupt registers. */ MHL_SII_CBUS_WR((0xF0 + i), 0xFF); } }
static void cbus_reset(struct i2c_client *client) { uint8_t i; /* * REG_SRST */ MHL_SII_REG_NAME_MOD(REG_SRST, BIT3, BIT3); msleep(20); MHL_SII_REG_NAME_MOD(REG_SRST, BIT3, 0x00); /* * REG_INTR1 and REG_INTR4 */ MHL_SII_REG_NAME_WR(REG_INTR1_MASK, BIT6); MHL_SII_REG_NAME_WR(REG_INTR4_MASK, BIT0 | BIT2 | BIT3 | BIT4 | BIT5 | BIT6); MHL_SII_REG_NAME_WR(REG_INTR5_MASK, 0x00); /* Unmask CBUS1 Intrs */ MHL_SII_CBUS_WR(0x0009, BIT2 | BIT3 | BIT4 | BIT5 | BIT6); /* Unmask CBUS2 Intrs */ MHL_SII_CBUS_WR(0x001F, BIT2 | BIT3); for (i = 0; i < 4; i++) { /* * Enable WRITE_STAT interrupt for writes to * all 4 MSC Status registers. */ MHL_SII_CBUS_WR((0xE0 + i), 0xFF); /* * Enable SET_INT interrupt for writes to * all 4 MSC Interrupt registers. */ MHL_SII_CBUS_WR((0xF0 + i), 0xFF); } return; }
static void switch_mode(struct mhl_tx_ctrl *mhl_ctrl, enum mhl_st_type to_mode) { struct i2c_client *client = mhl_ctrl->i2c_handle; switch (to_mode) { case POWER_STATE_D0_NO_MHL: mhl_ctrl->cur_state = to_mode; mhl_init_reg_settings(mhl_ctrl, true); /* REG_DISC_CTRL1 */ MHL_SII_REG_NAME_MOD(REG_DISC_CTRL1, BIT1 | BIT0, BIT0); /* TPI_DEVICE_POWER_STATE_CTRL_REG */ mhl_i2c_reg_modify(client, TX_PAGE_TPI, 0x001E, BIT1 | BIT0, 0x00); break; case POWER_STATE_D0_MHL: mhl_ctrl->cur_state = to_mode; break; case POWER_STATE_D3: if (mhl_ctrl->cur_state == POWER_STATE_D3) break; /* Force HPD to 0 when not in MHL mode. */ mhl_drive_hpd(mhl_ctrl, HPD_DOWN); /* * Change TMDS termination to high impedance * on disconnection. */ MHL_SII_REG_NAME_WR(REG_MHLTX_CTL1, 0xD0); msleep(50); if (!mhl_ctrl->disc_enabled) MHL_SII_REG_NAME_MOD(REG_DISC_CTRL1, BIT1 | BIT0, 0x00); MHL_SII_PAGE3_MOD(0x003D, BIT0, 0x00); mhl_ctrl->cur_state = POWER_STATE_D3; break; default: break; } }
void mhl_drive_hpd(struct mhl_tx_ctrl *mhl_ctrl, uint8_t to_state) { struct i2c_client *client = mhl_ctrl->i2c_handle; unsigned long flags; pr_debug("%s: To state=[0x%x]\n", __func__, to_state); if (to_state == HPD_UP) { /* * Drive HPD to UP state * Set HPD_OUT_OVR_EN = HPD State * EDID read and Un-force HPD (from low) * propogate to src let HPD float by clearing * HPD OUT OVRRD EN */ spin_lock_irqsave(&mhl_ctrl->lock, flags); mhl_ctrl->tx_powered_off = false; spin_unlock_irqrestore(&mhl_ctrl->lock, flags); MHL_SII_REG_NAME_MOD(REG_INT_CTRL, BIT4, 0); } else { /* Drive HPD to DOWN state */ MHL_SII_REG_NAME_MOD(REG_INT_CTRL, (BIT4 | BIT5), BIT4); } }
static int dev_detect_isr(struct mhl_tx_ctrl *mhl_ctrl) { uint8_t status, reg; struct i2c_client *client = mhl_ctrl->i2c_handle; /* INTR_STATUS4 */ status = MHL_SII_REG_NAME_RD(REG_INTR4); pr_debug("%s: reg int4 st=%02X\n", __func__, status); if ((0x00 == status) &&\ (mhl_ctrl->cur_state == POWER_STATE_D3)) { pr_err("%s: invalid intr\n", __func__); return 0; } if (0xFF == status) { pr_debug("%s: invalid intr 0xff\n", __func__); MHL_SII_REG_NAME_WR(REG_INTR4, status); return 0; } if ((status & BIT0) && (mhl_ctrl->chip_rev_id < 1)) { pr_debug("%s: scdt intr\n", __func__); scdt_st_chg(client); } if (status & BIT1) pr_debug("mhl: int4 bit1 set\n"); /* mhl_est interrupt */ if (status & BIT2) { pr_debug("%s: mhl_est st=%02X\n", __func__, (int) status); mhl_msm_connection(mhl_ctrl); } else if (status & BIT3) { pr_debug("%s: uUSB-a type dev detct\n", __func__); /* Short RGND */ MHL_SII_REG_NAME_MOD(REG_DISC_STAT2, BIT0 | BIT1, 0x00); mhl_msm_disconnection(mhl_ctrl); power_supply_changed(&mhl_ctrl->mhl_psy); if (mhl_ctrl->notify_usb_online) mhl_ctrl->notify_usb_online(0); return -EACCES; } if (status & BIT5) { /* clr intr - reg int4 */ pr_debug("%s: mhl discon: int4 st=%02X\n", __func__, (int)status); reg = MHL_SII_REG_NAME_RD(REG_INTR4); MHL_SII_REG_NAME_WR(REG_INTR4, reg); mhl_msm_disconnection(mhl_ctrl); power_supply_changed(&mhl_ctrl->mhl_psy); if (mhl_ctrl->notify_usb_online) mhl_ctrl->notify_usb_online(0); return -EACCES; } if ((mhl_ctrl->cur_state != POWER_STATE_D0_NO_MHL) &&\ (status & BIT6)) { /* rgnd rdy Intr */ pr_debug("%s: rgnd ready intr\n", __func__); switch_mode(mhl_ctrl, POWER_STATE_D0_NO_MHL, true); mhl_msm_read_rgnd_int(mhl_ctrl); } /* Can't succeed at these in D3 */ if ((mhl_ctrl->cur_state != POWER_STATE_D3) &&\ (status & BIT4)) { /* cbus lockout interrupt? * Hardware detection mechanism figures that * CBUS line is latched and raises this intr * where we force usb switch open and release */ pr_warn("%s: cbus locked out!\n", __func__); force_usb_switch_open(mhl_ctrl); release_usb_switch_open(mhl_ctrl); } MHL_SII_REG_NAME_WR(REG_INTR4, status); return 0; }
/* * Configure the initial reg settings */ static void mhl_init_reg_settings(struct mhl_tx_ctrl *mhl_ctrl, bool mhl_disc_en) { uint8_t regval; /* * ============================================ * POWER UP * ============================================ */ struct i2c_client *client = mhl_ctrl->i2c_handle; /* Power up 1.2V core */ MHL_SII_PAGE1_WR(0x003D, 0x3F); /* Enable Tx PLL Clock */ MHL_SII_PAGE2_WR(0x0011, 0x01); /* Enable Tx Clock Path and Equalizer */ MHL_SII_PAGE2_WR(0x0012, 0x11); /* Tx Source Termination ON */ MHL_SII_REG_NAME_WR(REG_MHLTX_CTL1, 0x10); /* Enable 1X MHL Clock output */ MHL_SII_REG_NAME_WR(REG_MHLTX_CTL6, 0xBC); /* Tx Differential Driver Config */ MHL_SII_REG_NAME_WR(REG_MHLTX_CTL2, 0x3C); MHL_SII_REG_NAME_WR(REG_MHLTX_CTL4, 0xC8); /* PLL Bandwidth Control */ MHL_SII_REG_NAME_WR(REG_MHLTX_CTL7, 0x03); MHL_SII_REG_NAME_WR(REG_MHLTX_CTL8, 0x0A); /* * ============================================ * Analog PLL Control * ============================================ */ /* Enable Rx PLL clock */ MHL_SII_REG_NAME_WR(REG_TMDS_CCTRL, 0x08); MHL_SII_PAGE0_WR(0x00F8, 0x8C); MHL_SII_PAGE0_WR(0x0085, 0x02); MHL_SII_PAGE2_WR(0x0000, 0x00); regval = MHL_SII_PAGE2_RD(0x0005); regval &= ~BIT5; MHL_SII_PAGE2_WR(0x0005, regval); MHL_SII_PAGE2_WR(0x0013, 0x60); /* PLL Cal ref sel */ MHL_SII_PAGE2_WR(0x0017, 0x03); /* VCO Cal */ MHL_SII_PAGE2_WR(0x001A, 0x20); /* Auto EQ */ MHL_SII_PAGE2_WR(0x0022, 0xE0); MHL_SII_PAGE2_WR(0x0023, 0xC0); MHL_SII_PAGE2_WR(0x0024, 0xA0); MHL_SII_PAGE2_WR(0x0025, 0x80); MHL_SII_PAGE2_WR(0x0026, 0x60); MHL_SII_PAGE2_WR(0x0027, 0x40); MHL_SII_PAGE2_WR(0x0028, 0x20); MHL_SII_PAGE2_WR(0x0029, 0x00); /* Rx PLL Bandwidth 4MHz */ MHL_SII_PAGE2_WR(0x0031, 0x0A); /* Rx PLL Bandwidth value from I2C */ MHL_SII_PAGE2_WR(0x0045, 0x06); MHL_SII_PAGE2_WR(0x004B, 0x06); MHL_SII_PAGE2_WR(0x004C, 0x60); /* Manual zone control */ MHL_SII_PAGE2_WR(0x004C, 0xE0); /* PLL Mode value */ MHL_SII_PAGE2_WR(0x004D, 0x00); MHL_SII_PAGE0_WR(0x0008, 0x35); /* * Discovery Control and Status regs * Setting De-glitch time to 50 ms (default) * Switch Control Disabled */ MHL_SII_REG_NAME_WR(REG_DISC_CTRL2, 0xAD); /* 1.8V CBUS VTH */ MHL_SII_REG_NAME_WR(REG_DISC_CTRL5, 0x57); /* RGND and single Discovery attempt */ MHL_SII_REG_NAME_WR(REG_DISC_CTRL6, 0x11); /* Ignore VBUS */ MHL_SII_REG_NAME_WR(REG_DISC_CTRL8, 0x82); /* Enable CBUS Discovery */ if (mhl_disc_en) { MHL_SII_REG_NAME_WR(REG_DISC_CTRL9, 0x24); /* Enable MHL Discovery */ MHL_SII_REG_NAME_WR(REG_DISC_CTRL1, 0x27); /* Pull-up resistance off for IDLE state */ MHL_SII_REG_NAME_WR(REG_DISC_CTRL4, 0x8C); } else { MHL_SII_REG_NAME_WR(REG_DISC_CTRL9, 0x26); /* Disable MHL Discovery */ MHL_SII_REG_NAME_WR(REG_DISC_CTRL1, 0x26); MHL_SII_REG_NAME_WR(REG_DISC_CTRL4, 0x8C); } MHL_SII_REG_NAME_WR(REG_DISC_CTRL7, 0x20); /* MHL CBUS Discovery - immediate comm. */ MHL_SII_REG_NAME_WR(REG_DISC_CTRL3, 0x86); MHL_SII_PAGE3_WR(0x3C, 0x80); if (mhl_ctrl->cur_state != POWER_STATE_D3) MHL_SII_REG_NAME_MOD(REG_INT_CTRL, BIT6 | BIT5 | BIT4, BIT4); /* Enable Auto Soft RESET */ MHL_SII_REG_NAME_WR(REG_SRST, 0x084); /* HDMI Transcode mode enable */ MHL_SII_PAGE0_WR(0x000D, 0x1C); cbus_reset(mhl_ctrl); init_cbus_regs(client); }