/* * only 2 interrupts may occur: screen plug/unplug and EDID read */ static irqreturn_t tda998x_irq_thread(int irq, void *data) { struct tda998x_priv *priv = data; u8 sta, cec, lvl, flag0, flag1, flag2; if (!priv) return IRQ_HANDLED; sta = cec_read(priv, REG_CEC_INTSTATUS); cec = cec_read(priv, REG_CEC_RXSHPDINT); lvl = cec_read(priv, REG_CEC_RXSHPDLEV); flag0 = reg_read(priv, REG_INT_FLAGS_0); flag1 = reg_read(priv, REG_INT_FLAGS_1); flag2 = reg_read(priv, REG_INT_FLAGS_2); DRM_DEBUG_DRIVER( "tda irq sta %02x cec %02x lvl %02x f0 %02x f1 %02x f2 %02x\n", sta, cec, lvl, flag0, flag1, flag2); if ((flag2 & INT_FLAGS_2_EDID_BLK_RD) && priv->wq_edid_wait) { priv->wq_edid_wait = 0; wake_up(&priv->wq_edid); } else if (cec != 0) { /* HPD change */ if (priv->encoder && priv->encoder->dev) drm_helper_hpd_irq_event(priv->encoder->dev); } return IRQ_HANDLED; }
/* * only 2 interrupts may occur: screen plug/unplug and EDID read */ static irqreturn_t tda998x_irq_thread(int irq, void *data) { struct tda998x_priv *priv = data; u8 sta, cec, lvl, flag0, flag1, flag2; bool handled = false; sta = cec_read(priv, REG_CEC_INTSTATUS); cec = cec_read(priv, REG_CEC_RXSHPDINT); lvl = cec_read(priv, REG_CEC_RXSHPDLEV); flag0 = reg_read(priv, REG_INT_FLAGS_0); flag1 = reg_read(priv, REG_INT_FLAGS_1); flag2 = reg_read(priv, REG_INT_FLAGS_2); DRM_DEBUG_DRIVER( "tda irq sta %02x cec %02x lvl %02x f0 %02x f1 %02x f2 %02x\n", sta, cec, lvl, flag0, flag1, flag2); if (cec & CEC_RXSHPDINT_HPD) { if (lvl & CEC_RXSHPDLEV_HPD) tda998x_edid_delay_start(priv); else schedule_work(&priv->detect_work); handled = true; } if ((flag2 & INT_FLAGS_2_EDID_BLK_RD) && priv->wq_edid_wait) { priv->wq_edid_wait = 0; wake_up(&priv->wq_edid); handled = true; } return IRQ_RETVAL(handled); }
static int tegra_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, u32 signal_free_time_ms, struct cec_msg *msg) { bool retry_xfer = signal_free_time_ms == CEC_SIGNAL_FREE_TIME_RETRY; struct tegra_cec *cec = adap->priv; unsigned int i; u32 mode = 0; u32 mask; if (cec_msg_is_broadcast(msg)) mode = TEGRA_CEC_TX_REG_BCAST; cec->tx_buf_cur = 0; cec->tx_buf_cnt = msg->len; for (i = 0; i < msg->len; i++) { cec->tx_buf[i] = mode | msg->msg[i]; if (i == 0) cec->tx_buf[i] |= TEGRA_CEC_TX_REG_START_BIT; if (i == msg->len - 1) cec->tx_buf[i] |= TEGRA_CEC_TX_REG_EOM; if (i == 0 && retry_xfer) cec->tx_buf[i] |= TEGRA_CEC_TX_REG_RETRY; } mask = cec_read(cec, TEGRA_CEC_INT_MASK); cec_write(cec, TEGRA_CEC_INT_MASK, mask | TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY); return 0; }
static enum drm_connector_status tda998x_encoder_detect(struct tda998x_priv *priv) { uint8_t val = cec_read(priv, REG_CEC_RXSHPDLEV); return (val & CEC_RXSHPDLEV_HPD) ? connector_status_connected : connector_status_disconnected; }
static enum drm_connector_status tda998x_connector_detect(struct drm_connector *connector, bool force) { struct tda998x_priv *priv = conn_to_tda998x_priv(connector); u8 val = cec_read(priv, REG_CEC_RXSHPDLEV); return (val & CEC_RXSHPDLEV_HPD) ? connector_status_connected : connector_status_disconnected; }
static void tegra_cec_error_recovery(struct tegra_cec *cec) { u32 hw_ctrl; hw_ctrl = cec_read(cec, TEGRA_CEC_HW_CONTROL); cec_write(cec, TEGRA_CEC_HW_CONTROL, 0); cec_write(cec, TEGRA_CEC_INT_STAT, 0xffffffff); cec_write(cec, TEGRA_CEC_HW_CONTROL, hw_ctrl); }
static int tegra_cec_adap_monitor_all_enable(struct cec_adapter *adap, bool enable) { struct tegra_cec *cec = adap->priv; u32 reg = cec_read(cec, TEGRA_CEC_HW_CONTROL); if (enable) reg |= TEGRA_CEC_HWCTRL_RX_SNOOP; else reg &= ~TEGRA_CEC_HWCTRL_RX_SNOOP; cec_write(cec, TEGRA_CEC_HW_CONTROL, reg); return 0; }
static int tegra_cec_adap_log_addr(struct cec_adapter *adap, u8 logical_addr) { struct tegra_cec *cec = adap->priv; u32 state = cec_read(cec, TEGRA_CEC_HW_CONTROL); if (logical_addr == CEC_LOG_ADDR_INVALID) state &= ~TEGRA_CEC_HWCTRL_RX_LADDR_MASK; else state |= TEGRA_CEC_HWCTRL_RX_LADDR((1 << logical_addr)); cec_write(cec, TEGRA_CEC_HW_CONTROL, state); return 0; }
static irqreturn_t tegra_cec_irq_handler(int irq, void *data) { struct device *dev = data; struct tegra_cec *cec = dev_get_drvdata(dev); u32 status, mask; status = cec_read(cec, TEGRA_CEC_INT_STAT); mask = cec_read(cec, TEGRA_CEC_INT_MASK); status &= mask; if (!status) return IRQ_HANDLED; if (status & TEGRA_CEC_INT_STAT_TX_REGISTER_UNDERRUN) { dev_err(dev, "TX underrun, interrupt timing issue!\n"); tegra_cec_error_recovery(cec); cec_write(cec, TEGRA_CEC_INT_MASK, mask & ~TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY); cec->tx_done = true; cec->tx_status = CEC_TX_STATUS_ERROR; return IRQ_WAKE_THREAD; } if ((status & TEGRA_CEC_INT_STAT_TX_ARBITRATION_FAILED) || (status & TEGRA_CEC_INT_STAT_TX_BUS_ANOMALY_DETECTED)) { tegra_cec_error_recovery(cec); cec_write(cec, TEGRA_CEC_INT_MASK, mask & ~TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY); cec->tx_done = true; if (status & TEGRA_CEC_INT_STAT_TX_BUS_ANOMALY_DETECTED) cec->tx_status = CEC_TX_STATUS_LOW_DRIVE; else cec->tx_status = CEC_TX_STATUS_ARB_LOST; return IRQ_WAKE_THREAD; } if (status & TEGRA_CEC_INT_STAT_TX_FRAME_TRANSMITTED) { cec_write(cec, TEGRA_CEC_INT_STAT, TEGRA_CEC_INT_STAT_TX_FRAME_TRANSMITTED); if (status & TEGRA_CEC_INT_STAT_TX_FRAME_OR_BLOCK_NAKD) { tegra_cec_error_recovery(cec); cec->tx_done = true; cec->tx_status = CEC_TX_STATUS_NACK; } else { cec->tx_done = true; cec->tx_status = CEC_TX_STATUS_OK; } return IRQ_WAKE_THREAD; } if (status & TEGRA_CEC_INT_STAT_TX_FRAME_OR_BLOCK_NAKD) dev_warn(dev, "TX NAKed on the fly!\n"); if (status & TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY) { if (cec->tx_buf_cur == cec->tx_buf_cnt) { cec_write(cec, TEGRA_CEC_INT_MASK, mask & ~TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY); } else { cec_write(cec, TEGRA_CEC_TX_REGISTER, cec->tx_buf[cec->tx_buf_cur++]); cec_write(cec, TEGRA_CEC_INT_STAT, TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY); } } if (status & (TEGRA_CEC_INT_STAT_RX_REGISTER_OVERRUN | TEGRA_CEC_INT_STAT_RX_BUS_ANOMALY_DETECTED | TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED | TEGRA_CEC_INT_STAT_RX_BUS_ERROR_DETECTED)) { cec_write(cec, TEGRA_CEC_INT_STAT, (TEGRA_CEC_INT_STAT_RX_REGISTER_OVERRUN | TEGRA_CEC_INT_STAT_RX_BUS_ANOMALY_DETECTED | TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED | TEGRA_CEC_INT_STAT_RX_BUS_ERROR_DETECTED)); } else if (status & TEGRA_CEC_INT_STAT_RX_REGISTER_FULL) { u32 v; cec_write(cec, TEGRA_CEC_INT_STAT, TEGRA_CEC_INT_STAT_RX_REGISTER_FULL); v = cec_read(cec, TEGRA_CEC_RX_REGISTER); if (cec->rx_buf_cnt < CEC_MAX_MSG_SIZE) cec->rx_buf[cec->rx_buf_cnt++] = v & 0xff; if (v & TEGRA_CEC_RX_REGISTER_EOM) { cec->rx_done = true; return IRQ_WAKE_THREAD; } } return IRQ_HANDLED; }