static int mhl_tx_clear_reg(struct sii9244_data *sii9244, unsigned int offset, u8 mask) { int ret; u8 value; ret = mhl_tx_read_reg(sii9244, offset, &value); if (ret < 0) return ret; value &= ~mask; return mhl_tx_write_reg(sii9244, offset, value); }
int mhl_tx_modify_reg(void *drv_context, u8 page, u8 offset, u8 mask, u8 value) { int reg_value; int write_status; reg_value = mhl_tx_read_reg(drv_context, page, offset); if (reg_value < 0) return reg_value; reg_value &= ~mask; reg_value |= mask & value; write_status = mhl_tx_write_reg(drv_context, page, offset, reg_value); if (write_status < 0) return write_status; else return reg_value; }
static int mhl_tx_clear_reg(struct sii9234_data *sii9234, unsigned int offset, u8 mask) { int ret; u8 value; ret = mhl_tx_read_reg(sii9234, offset, &value); if (ret < 0) { pr_err("[ERROR] sii9234 : %s(0x%02x, 0x%02x)\n", __func__, offset, mask); return ret; } value &= ~mask; ret = mhl_tx_write_reg(sii9234, offset, value); if (ret < 0) pr_err("[ERROR] sii9234 : %s(0x%02x, 0x%02x)\n", __func__, offset, mask); return ret; }
static irqreturn_t sii9244_irq_thread(int irq, void *data) { struct sii9244_data *sii9244 = data; int ret; u8 intr1, intr4, value; u8 intr1_en, intr4_en; bool release_otg = false; mutex_lock(&sii9244->lock); mhl_tx_read_reg(sii9244, MHL_TX_INTR1_REG, &intr1); mhl_tx_read_reg(sii9244, MHL_TX_INTR4_REG, &intr4); mhl_tx_read_reg(sii9244, MHL_TX_INTR1_ENABLE_REG, &intr1_en); mhl_tx_read_reg(sii9244, MHL_TX_INTR4_ENABLE_REG, &intr4_en); pr_debug("sii9244: irq %02x/%02x %02x/%02x\n", intr1, intr1_en, intr4, intr4_en); if (intr4 & RGND_READY_INT) { ret = mhl_tx_read_reg(sii9244, MHL_TX_STAT2_REG, &value); if (ret < 0) { dev_err(&sii9244->pdata->mhl_tx_client->dev, "STAT2 reg, err %d\n", ret); goto err_exit; } switch (value & RGND_INTP_MASK) { case RGND_INTP_OPEN: pr_debug("RGND Open\n"); sii9244->rgnd = RGND_OPEN; break; case RGND_INTP_1K: pr_debug("RGND 1K\n"); ret = mhl_tx_write_reg(sii9244, MHL_TX_DISC_CTRL1_REG, 0x25); ret = mhl_send_wake_pulses(sii9244); sii9244->rgnd = RGND_1K; break; case RGND_INTP_2K: pr_debug("RGND 2K\n"); ret = mhl_send_wake_pulses(sii9244); sii9244->rgnd = RGND_2K; break; case RGND_INTP_SHORT: pr_debug("RGND Short\n"); sii9244->rgnd = RGND_SHORT; break; }; } if (intr4 & CBUS_LKOUT_INT) { pr_debug("sii9244: CBUS Lockout Interrupt\n"); sii9244->state = STATE_CBUS_LOCKOUT; } if (intr4 & MHL_DISC_FAIL_INT) sii9244->state = STATE_DISCOVERY_FAILED; if (intr4 & MHL_EST_INT) { /* discovery override */ ret = mhl_tx_write_reg(sii9244, MHL_TX_MHLTX_CTL1_REG, 0x10); /* increase DDC translation layer timer (byte mode) */ cbus_write_reg(sii9244, 0x07, 0x32); cbus_set_reg(sii9244, 0x44, 1<<1); /* Keep the discovery enabled. Need RGND interrupt */ ret = mhl_tx_set_reg(sii9244, MHL_TX_DISC_CTRL1_REG, (1<<0)); sii9244->state = STATE_ESTABLISHED; } if (intr1 & HPD_CHANGE_INT) { ret = cbus_read_reg(sii9244, MSC_REQ_ABORT_REASON_REG, &value); if (value & SET_HPD_DOWNSTREAM) { /* Downstream HPD Highi */ /* Do we need to send HPD upstream using * Register 0x79(page0)? Is HPD need to be overriden?? * TODO: See if we need code for overriding HPD OUT * as per Page 0,0x79 Register */ /* Enable TMDS */ ret = mhl_tx_set_reg(sii9244, MHL_TX_TMDS_CCTRL, (1<<4)); pr_debug("sii9244: MHL HPD High, enabled TMDS\n"); ret = mhl_tx_set_reg(sii9244, MHL_TX_INT_CTRL_REG, (1<<4) | (1<<5)); } else { /*Downstream HPD Low*/ /* Similar to above comments. * TODO:Do we need to override HPD OUT value * and do we need to disable TMDS here? */ /* Disable TMDS */ ret = mhl_tx_clear_reg(sii9244, MHL_TX_TMDS_CCTRL, (1<<4)); pr_debug("sii9244 MHL HPD low, disabled TMDS\n"); ret = mhl_tx_clear_reg(sii9244, MHL_TX_INT_CTRL_REG, (1<<4) | (1<<5)); } } if (intr1 & RSEN_CHANGE_INT) { ret = mhl_tx_read_reg(sii9244, MHL_TX_SYSSTAT_REG, &value); sii9244->rsen = value & RSEN_STATUS; if (value & RSEN_STATUS) { pr_info("sii9244: MHL cable connected.. RESN High\n"); } else { pr_info("sii9244: RSEN lost\n"); /* Once RSEN loss is confirmed,we need to check * based on cable status and chip power status,whether * it is SINK Loss(HDMI cable not connected, TV Off) * or MHL cable disconnection * TODO: Define the below mhl_disconnection() */ /* mhl_disconnection(); */ /* Notify Disconnection to OTG */ if (sii9244->claimed == true) { disable_irq_nosync(sii9244->irq); release_otg = true; } sii9244_power_down(sii9244); } } err_exit: mhl_tx_write_reg(sii9244, MHL_TX_INTR1_REG, intr1); mhl_tx_write_reg(sii9244, MHL_TX_INTR4_REG, intr4); mutex_unlock(&sii9244->lock); pr_debug("sii9244: wake_up\n"); wake_up(&sii9244->wq); if (release_otg) otg_id_notify(); return IRQ_HANDLED; }
static int sii9244_detection_callback(struct otg_id_notifier_block *nb) { struct sii9244_data *sii9244 = container_of(nb, struct sii9244_data, otg_id_nb); int ret; u8 value; int handled = OTG_ID_UNHANDLED; pr_debug("sii9244: detection started\n"); mutex_lock(&sii9244->lock); sii9244->rgnd = RGND_UNKNOWN; sii9244->state = STATE_DISCONNECTED; sii9244->rsen = false; /* Set the board configuration so the sii9244 has access to the * external connector. */ sii9244->pdata->enable(1); sii9244->pdata->power(1); ret = sii9244_power_init(sii9244); if (ret < 0) goto unhandled; sii9244_hdmi_init(sii9244); sii9244_mhl_tx_ctl_int(sii9244); /* Enable HDCP Compliance safety*/ ret = mhl_tx_write_reg(sii9244, 0x2B, 0x01); if (ret < 0) goto unhandled; /* CBUS discovery cycle time for each drive and float = 150us*/ ret = mhl_tx_read_reg(sii9244, MHL_TX_DISC_CTRL1_REG, &value); if (ret < 0) goto unhandled; value &= ~(1<<2); value |= (1<<3); ret = mhl_tx_write_reg(sii9244, MHL_TX_DISC_CTRL1_REG, value); if (ret < 0) goto unhandled; /* Clear bit 6 (reg_skip_rgnd) */ ret = mhl_tx_write_reg(sii9244, MHL_TX_DISC_CTRL2_REG, (1<<7) /* Reserved Bit */ | 2 << ATT_THRESH_SHIFT | DEGLITCH_TIME_128MS); if (ret < 0) goto unhandled; /* Changed from 66 to 65 for 94[1:0] = 01 = 5k reg_cbusmhl_pup_sel */ /* 1.8V CBUS VTH & GND threshold */ ret = mhl_tx_write_reg(sii9244, MHL_TX_DISC_CTRL5_REG, 0x75); if (ret < 0) goto unhandled; /* set bit 2 and 3, which is Initiator Timeout */ ret = cbus_read_reg(sii9244, CBUS_LINK_CONTROL_2_REG, &value); if (ret < 0) goto unhandled; value |= 0x0C; ret = cbus_write_reg(sii9244, CBUS_LINK_CONTROL_2_REG, value); if (ret < 0) goto unhandled; ret = mhl_tx_write_reg(sii9244, MHL_TX_MHLTX_CTL6_REG, 0xA0); if (ret < 0) goto unhandled; /* RGND & single discovery attempt (RGND blocking) */ ret = mhl_tx_write_reg(sii9244, MHL_TX_DISC_CTRL6_REG, BLOCK_RGND_INT | DVRFLT_SEL | SINGLE_ATT); if (ret < 0) goto unhandled; /* Use VBUS path of discovery state machine*/ ret = mhl_tx_write_reg(sii9244, MHL_TX_DISC_CTRL8_REG, 0); if (ret < 0) goto unhandled; ret = mhl_tx_set_reg(sii9244, MHL_TX_DISC_CTRL6_REG, USB_ID_OVR); if (ret < 0) goto unhandled; /* To allow RGND engine to operate correctly. * When moving the chip from D2 to D0 (power up, init regs) * the values should be * 94[1:0] = 01 reg_cbusmhl_pup_sel[1:0] should be set for 5k * 93[7:6] = 10 reg_cbusdisc_pup_sel[1:0] should be * set for 10k (default) * 93[5:4] = 00 reg_cbusidle_pup_sel[1:0] = open (default) */ ret = mhl_tx_set_reg(sii9244, MHL_TX_DISC_CTRL3_REG, 0x86); if (ret < 0) goto unhandled; /* change from CC to 8C to match 5K*/ ret = mhl_tx_set_reg(sii9244, MHL_TX_DISC_CTRL4_REG, 0x8C); if (ret < 0) goto unhandled; /* Configure the interrupt as active high */ ret = mhl_tx_clear_reg(sii9244, MHL_TX_INT_CTRL_REG, (1<<2) | (1<<1)); if (ret < 0) goto unhandled; msleep(25); ret = mhl_tx_clear_reg(sii9244, MHL_TX_DISC_CTRL6_REG, USB_ID_OVR); if (ret < 0) goto unhandled; ret = mhl_tx_write_reg(sii9244, MHL_TX_DISC_CTRL1_REG, 0x27); if (ret < 0) goto unhandled; /* Reset CBUS */ ret = mhl_tx_set_reg(sii9244, 0x05, 0x03); if (ret < 0) goto unhandled; usleep_range(2000, 3000); ret = mhl_tx_clear_reg(sii9244, 0x05, 0x03); if (ret < 0) goto unhandled; /* Adjust interrupt mask everytime reset is performed.*/ ret = cbus_write_reg(sii9244, 0x09, 0); if (ret < 0) goto unhandled; ret = cbus_write_reg(sii9244, 0x1F, 0); if (ret < 0) goto unhandled; /* Enable Auto soft reset on SCDT = 0*/ ret = mhl_tx_write_reg(sii9244, 0x05, 0x04); if (ret < 0) goto unhandled; /* HDMI Transcode mode enable*/ ret = mhl_tx_write_reg(sii9244, 0x0D, 0x1C); if (ret < 0) goto unhandled; ret = mhl_tx_write_reg(sii9244, MHL_TX_INTR4_ENABLE_REG, RGND_READY_MASK | CBUS_LKOUT_MASK | MHL_DISC_FAIL_MASK | MHL_EST_MASK); if (ret < 0) goto unhandled; ret = mhl_tx_write_reg(sii9244, MHL_TX_INTR1_ENABLE_REG, (1<<5) | (1<<6)); if (ret < 0) goto unhandled; pr_debug("sii9244: waiting for RGND measurement\n"); enable_irq(sii9244->irq); /* SiI9244 Programmer's Reference Section 2.4.3 * State : RGND Ready */ mutex_unlock(&sii9244->lock); ret = wait_event_timeout(sii9244->wq, ((sii9244->rgnd != RGND_UNKNOWN) || mhl_state_is_error(sii9244->state)), msecs_to_jiffies(2000)); mutex_lock(&sii9244->lock); if (sii9244->rgnd == RGND_UNKNOWN || mhl_state_is_error(sii9244->state)) goto unhandled; if (sii9244->rgnd != RGND_1K) goto unhandled; mutex_unlock(&sii9244->lock); pr_debug("sii9244: waiting for detection\n"); ret = wait_event_timeout(sii9244->wq, sii9244->state != STATE_DISCONNECTED, msecs_to_jiffies(500)); mutex_lock(&sii9244->lock); if (sii9244->state == STATE_DISCONNECTED) goto unhandled; if (sii9244->state == STATE_DISCOVERY_FAILED) { handled = OTG_ID_PROXY_WAIT; goto unhandled; } if (mhl_state_is_error(sii9244->state)) goto unhandled; mutex_unlock(&sii9244->lock); wait_event_timeout(sii9244->wq, sii9244->rsen, msecs_to_jiffies(400)); mutex_lock(&sii9244->lock); if (!sii9244->rsen) goto unhandled; pr_info("si9234: connection established\n"); sii9244->claimed = true; sii9234_vbus_present(true); mutex_unlock(&sii9244->lock); return OTG_ID_HANDLED; unhandled: pr_info("sii9244: Detection failed"); if (sii9244->state == STATE_DISCONNECTED) pr_cont(" (timeout)"); else if (sii9244->state == STATE_DISCOVERY_FAILED) pr_cont(" (discovery failed)"); else if (sii9244->state == STATE_CBUS_LOCKOUT) pr_cont(" (cbus_lockout)"); pr_cont("\n"); disable_irq_nosync(sii9244->irq); sii9244_power_down(sii9244); mutex_unlock(&sii9244->lock); return handled; }
static int sii9234_30pin_init_for_9290(struct sii9234_data *sii9234) { u8 value; int ret = 0; pr_info("[MHD: %s]++\n", __func__); /* init registers */ ret = sii9234_30pin_reg_init_for_9290(sii9234); if (ret < 0) goto unhandled; /* start tpi */ ret = mhl_tx_write_reg(sii9234, 0xC7, 0x00); if (ret < 0) goto unhandled; /* enable interrupts */ ret = mhl_tx_write_reg(sii9234, 0xBC, 0x01); if (ret < 0) goto unhandled; ret = mhl_tx_write_reg(sii9234, 0xBD, 0x78); if (ret < 0) goto unhandled; ret = mhl_tx_write_reg(sii9234, 0xBE, 0x01); if (ret < 0) goto unhandled; /* mhd rx connected */ ret = mhl_tx_write_reg(sii9234, 0xBC, 0x01); if (ret < 0) goto unhandled; ret = mhl_tx_write_reg(sii9234, 0xBD, 0xA0); if (ret < 0) goto unhandled; ret = mhl_tx_write_reg(sii9234, 0xBE, 0x10); if (ret < 0) goto unhandled; ret = cbus_write_reg(sii9234, 0x07, 0x30 | 0x0E); if (ret < 0) goto unhandled; ret = cbus_write_reg(sii9234, 0x47, 0x03); if (ret < 0) goto unhandled; ret = cbus_write_reg(sii9234, 0x21, 0x01); if (ret < 0) goto unhandled; /* enable mhd tx */ ret = mhl_tx_clear_reg(sii9234, 0x1A, 1<<4); if (ret < 0) goto unhandled; /* set mhd power active mode */ ret = mhl_tx_clear_reg(sii9234, 0x1E, 1<<1 | 1<<0); if (ret < 0) goto unhandled; ret = mhl_tx_write_reg(sii9234, 0xBC, 0x01); if (ret < 0) goto unhandled; ret = mhl_tx_write_reg(sii9234, 0xBD, 0xA0); if (ret < 0) goto unhandled; ret = mhl_tx_read_reg(sii9234, 0xBE, &value); if (ret < 0) goto unhandled; if ((value & (1<<7 | 1<<6)) != 0x00) { /* Assert Mobile HD FIFO Reset */ ret = mhl_tx_write_reg(sii9234, 0xBC, 0x01); if (ret < 0) goto unhandled; ret = mhl_tx_write_reg(sii9234, 0xBD, 0x05); if (ret < 0) goto unhandled; ret = mhl_tx_write_reg(sii9234, 0xBE, (1<<4 | 0x04)); if (ret < 0) goto unhandled; mdelay(1); /* Deassert Mobile HD FIFO Reset */ ret = mhl_tx_write_reg(sii9234, 0xBC, 0x01); if (ret < 0) goto unhandled; ret = mhl_tx_write_reg(sii9234, 0xBD, 0x05); if (ret < 0) goto unhandled; ret = mhl_tx_write_reg(sii9234, 0xBE, 0x04); if (ret < 0) goto unhandled; } /* This is tricky but there's no way to handle other accessories * but sending UNHANDLED. * return MHL_CON_HANDLED; */ pr_info("[MHD: %s]--\n", __func__); return 0; unhandled: return -1; }
static int sii9234_30pin_reg_init_for_9290(struct sii9234_data *sii9234) { int ret = 0; u8 value; pr_info("[: %s]++\n", __func__); ret = tpi_write_reg(sii9234, 0x3D, 0x3F); if (ret < 0) return ret; ret = hdmi_rx_write_reg(sii9234, 0x11, 0x01); if (ret < 0) return ret; ret = hdmi_rx_write_reg(sii9234, 0x12, 0x15); if (ret < 0) return ret; ret = mhl_tx_write_reg(sii9234, 0x08, 0x35); if (ret < 0) return ret; ret = hdmi_rx_write_reg(sii9234, 0x00, 0x00); if (ret < 0) return ret; ret = hdmi_rx_write_reg(sii9234, 0x13, 0x60); if (ret < 0) return ret; ret = hdmi_rx_write_reg(sii9234, 0x14, 0xF0); if (ret < 0) return ret; ret = hdmi_rx_write_reg(sii9234, 0x4B, 0x06); if (ret < 0) return ret; /* Analog PLL Control */ ret = hdmi_rx_write_reg(sii9234, 0x17, 0x07); if (ret < 0) return ret; ret = hdmi_rx_write_reg(sii9234, 0x1A, 0x20); if (ret < 0) return ret; ret = hdmi_rx_write_reg(sii9234, 0x22, 0xE0); if (ret < 0) return ret; ret = hdmi_rx_write_reg(sii9234, 0x23, 0xC0); if (ret < 0) return ret; ret = hdmi_rx_write_reg(sii9234, 0x24, 0xA0); if (ret < 0) return ret; ret = hdmi_rx_write_reg(sii9234, 0x25, 0x80); if (ret < 0) return ret; ret = hdmi_rx_write_reg(sii9234, 0x26, 0x60); if (ret < 0) return ret; ret = hdmi_rx_write_reg(sii9234, 0x27, 0x40); if (ret < 0) return ret; ret = hdmi_rx_write_reg(sii9234, 0x28, 0x20); if (ret < 0) return ret; ret = hdmi_rx_write_reg(sii9234, 0x29, 0x00); if (ret < 0) return ret; ret = hdmi_rx_write_reg(sii9234, 0x4D, 0x02); if (ret < 0) return ret; ret = hdmi_rx_write_reg(sii9234, 0x4C, 0xA0); if (ret < 0) return ret; ret = mhl_tx_write_reg(sii9234, 0x80, 0x34); if (ret < 0) return ret; ret = hdmi_rx_write_reg(sii9234, 0x31, 0x0B); if (ret < 0) return ret; ret = hdmi_rx_write_reg(sii9234, 0x45, 0x06); if (ret < 0) return ret; ret = mhl_tx_write_reg(sii9234, 0xA0, 0xD0); if (ret < 0) return ret; ret = mhl_tx_write_reg(sii9234, 0xA1, 0xFC); if (ret < 0) return ret; ret = mhl_tx_write_reg(sii9234, 0xA3 /*MHL_TX_MHLTX_CTL4_REG*/, sii9234->pdata->swing_level); if (ret < 0) return ret; ret = mhl_tx_write_reg(sii9234, 0xA6, 0x00); if (ret < 0) return ret; ret = mhl_tx_write_reg(sii9234, 0x2B, 0x01); if (ret < 0) return ret; /* CBUS & Discovery */ ret = mhl_tx_read_reg(sii9234, 0x90/*MHL_TX_DISC_CTRL1_REG*/, &value); if (ret < 0) return ret; value &= ~(1<<2); value |= (1<<3); ret = mhl_tx_write_reg(sii9234, 0x90 /*MHL_TX_DISC_CTRL1_REG*/, value); if (ret < 0) return ret; ret = mhl_tx_write_reg(sii9234, 0x91, 0xE5); if (ret < 0) return ret; ret = mhl_tx_write_reg(sii9234, 0x94, 0x66); if (ret < 0) return ret; ret = cbus_read_reg(sii9234, 0x31, &value); if (ret < 0) return ret; value |= 0x0C; if (ret < 0) return ret; ret = cbus_write_reg(sii9234, 0x31, value); if (ret < 0) return ret; ret = mhl_tx_write_reg(sii9234, 0xA5, 0x80); if (ret < 0) return ret; ret = mhl_tx_write_reg(sii9234, 0x95, 0x31); if (ret < 0) return ret; ret = mhl_tx_write_reg(sii9234, 0x96, 0x22); if (ret < 0) return ret; ret = mhl_tx_read_reg(sii9234, 0x95/*MHL_TX_DISC_CTRL6_REG*/, &value); if (ret < 0) return ret; value |= (1<<6); ret = mhl_tx_write_reg(sii9234, 0x95/*MHL_TX_DISC_CTRL6_REG*/, value); if (ret < 0) return ret; ret = mhl_tx_write_reg(sii9234, 0x92, 0x46); if (ret < 0) return ret; ret = mhl_tx_write_reg(sii9234, 0x93, 0xDC); if (ret < 0) return ret; /*0x79=MHL_TX_INT_CTRL_REG*/ ret = mhl_tx_clear_reg(sii9234, 0x79, (1<<2) | (1<<1)); if (ret < 0) return ret; mdelay(25); /*0x95=MHL_TX_DISC_CTRL6_REG*/ ret = mhl_tx_clear_reg(sii9234, 0x95, (1<<6)/*USB_ID_OVR*/); if (ret < 0) return ret; ret = mhl_tx_write_reg(sii9234, 0x90, 0x27); if (ret < 0) return ret; ret = sii9234_cbus_init_for_9290(sii9234); if (ret < 0) return ret; ret = mhl_tx_write_reg(sii9234, 0x05, 0x4); if (ret < 0) return ret; ret = mhl_tx_write_reg(sii9234, 0x0D, 0x1C); pr_info("[MHD: %s]--\n", __func__); return ret; }