Beispiel #1
0
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);
}
Beispiel #2
0
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;
}
Beispiel #3
0
static void tda998x_destroy(struct tda998x_priv *priv)
{
	/* disable all IRQs and free the IRQ handler */
	cec_write(priv, REG_CEC_RXSHPDINTENA, 0);
	reg_clear(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);
	if (priv->hdmi->irq) {
		free_irq(priv->hdmi->irq, priv);
		cancel_delayed_work_sync(&priv->dwork);
	}

	i2c_unregister_device(priv->cec);
}
Beispiel #4
0
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;
}
Beispiel #5
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 int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv)
{
	struct device_node *np = client->dev.of_node;
	u32 video;
	int rev_lo, rev_hi, ret;
	unsigned short cec_addr;

	priv->vip_cntrl_0 = VIP_CNTRL_0_SWAP_A(2) | VIP_CNTRL_0_SWAP_B(3);
	priv->vip_cntrl_1 = VIP_CNTRL_1_SWAP_C(0) | VIP_CNTRL_1_SWAP_D(1);
	priv->vip_cntrl_2 = VIP_CNTRL_2_SWAP_E(4) | VIP_CNTRL_2_SWAP_F(5);

	priv->current_page = 0xff;
	priv->hdmi = client;
	/* CEC I2C address bound to TDA998x I2C addr by configuration pins */
	cec_addr = 0x34 + (client->addr & 0x03);
	priv->cec = i2c_new_dummy(client->adapter, cec_addr);
	if (!priv->cec)
		return -ENODEV;

	priv->dpms = DRM_MODE_DPMS_OFF;

	mutex_init(&priv->mutex);	/* protect the page access */
	init_waitqueue_head(&priv->edid_delay_waitq);
	setup_timer(&priv->edid_delay_timer, tda998x_edid_delay_done,
		    (unsigned long)priv);
	INIT_WORK(&priv->detect_work, tda998x_detect_work);

	/* wake up the device: */
	cec_write(priv, REG_CEC_ENAMODS,
			CEC_ENAMODS_EN_RXSENS | CEC_ENAMODS_EN_HDMI);

	tda998x_reset(priv);

	/* read version: */
	rev_lo = reg_read(priv, REG_VERSION_LSB);
	rev_hi = reg_read(priv, REG_VERSION_MSB);
	if (rev_lo < 0 || rev_hi < 0) {
		ret = rev_lo < 0 ? rev_lo : rev_hi;
		goto fail;
	}

	priv->rev = rev_lo | rev_hi << 8;

	/* mask off feature bits: */
	priv->rev &= ~0x30; /* not-hdcp and not-scalar bit */

	switch (priv->rev) {
	case TDA9989N2:
		dev_info(&client->dev, "found TDA9989 n2");
		break;
	case TDA19989:
		dev_info(&client->dev, "found TDA19989");
		break;
	case TDA19989N2:
		dev_info(&client->dev, "found TDA19989 n2");
		break;
	case TDA19988:
		dev_info(&client->dev, "found TDA19988");
		break;
	default:
		dev_err(&client->dev, "found unsupported device: %04x\n",
			priv->rev);
		goto fail;
	}

	/* after reset, enable DDC: */
	reg_write(priv, REG_DDC_DISABLE, 0x00);

	/* set clock on DDC channel: */
	reg_write(priv, REG_TX3, 39);

	/* if necessary, disable multi-master: */
	if (priv->rev == TDA19989)
		reg_set(priv, REG_I2C_MASTER, I2C_MASTER_DIS_MM);

	cec_write(priv, REG_CEC_FRO_IM_CLK_CTRL,
			CEC_FRO_IM_CLK_CTRL_GHOST_DIS | CEC_FRO_IM_CLK_CTRL_IMCLK_SEL);

	/* initialize the optional IRQ */
	if (client->irq) {
		int irqf_trigger;

		/* init read EDID waitqueue and HDP work */
		init_waitqueue_head(&priv->wq_edid);

		/* clear pending interrupts */
		reg_read(priv, REG_INT_FLAGS_0);
		reg_read(priv, REG_INT_FLAGS_1);
		reg_read(priv, REG_INT_FLAGS_2);

		irqf_trigger =
			irqd_get_trigger_type(irq_get_irq_data(client->irq));
		ret = request_threaded_irq(client->irq, NULL,
					   tda998x_irq_thread,
					   irqf_trigger | IRQF_ONESHOT,
					   "tda998x", priv);
		if (ret) {
			dev_err(&client->dev,
				"failed to request IRQ#%u: %d\n",
				client->irq, ret);
			goto fail;
		}

		/* enable HPD irq */
		cec_write(priv, REG_CEC_RXSHPDINTENA, CEC_RXSHPDLEV_HPD);
	}

	/* enable EDID read irq: */
	reg_set(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);

	if (!np)
		return 0;		/* non-DT */

	/* get the optional video properties */
	ret = of_property_read_u32(np, "video-ports", &video);
	if (ret == 0) {
		priv->vip_cntrl_0 = video >> 16;
		priv->vip_cntrl_1 = video >> 8;
		priv->vip_cntrl_2 = video;
	}
Beispiel #7
0
static int tegra_cec_adap_enable(struct cec_adapter *adap, bool enable)
{
	struct tegra_cec *cec = adap->priv;

	cec->rx_buf_cnt = 0;
	cec->tx_buf_cnt = 0;
	cec->tx_buf_cur = 0;

	cec_write(cec, TEGRA_CEC_HW_CONTROL, 0);
	cec_write(cec, TEGRA_CEC_INT_MASK, 0);
	cec_write(cec, TEGRA_CEC_INT_STAT, 0xffffffff);
	cec_write(cec, TEGRA_CEC_SW_CONTROL, 0);

	if (!enable)
		return 0;

	cec_write(cec, TEGRA_CEC_INPUT_FILTER, (1U << 31) | 0x20);

	cec_write(cec, TEGRA_CEC_RX_TIMING_0,
		  (0x7a << TEGRA_CEC_RX_TIM0_START_BIT_MAX_LO_TIME_SHIFT) |
		  (0x6d << TEGRA_CEC_RX_TIM0_START_BIT_MIN_LO_TIME_SHIFT) |
		  (0x93 << TEGRA_CEC_RX_TIM0_START_BIT_MAX_DURATION_SHIFT) |
		  (0x86 << TEGRA_CEC_RX_TIM0_START_BIT_MIN_DURATION_SHIFT));

	cec_write(cec, TEGRA_CEC_RX_TIMING_1,
		  (0x35 << TEGRA_CEC_RX_TIM1_DATA_BIT_MAX_LO_TIME_SHIFT) |
		  (0x21 << TEGRA_CEC_RX_TIM1_DATA_BIT_SAMPLE_TIME_SHIFT) |
		  (0x56 << TEGRA_CEC_RX_TIM1_DATA_BIT_MAX_DURATION_SHIFT) |
		  (0x40 << TEGRA_CEC_RX_TIM1_DATA_BIT_MIN_DURATION_SHIFT));

	cec_write(cec, TEGRA_CEC_RX_TIMING_2,
		  (0x50 << TEGRA_CEC_RX_TIM2_END_OF_BLOCK_TIME_SHIFT));

	cec_write(cec, TEGRA_CEC_TX_TIMING_0,
		  (0x74 << TEGRA_CEC_TX_TIM0_START_BIT_LO_TIME_SHIFT) |
		  (0x8d << TEGRA_CEC_TX_TIM0_START_BIT_DURATION_SHIFT) |
		  (0x08 << TEGRA_CEC_TX_TIM0_BUS_XITION_TIME_SHIFT) |
		  (0x71 << TEGRA_CEC_TX_TIM0_BUS_ERROR_LO_TIME_SHIFT));

	cec_write(cec, TEGRA_CEC_TX_TIMING_1,
		  (0x2f << TEGRA_CEC_TX_TIM1_LO_DATA_BIT_LO_TIME_SHIFT) |
		  (0x13 << TEGRA_CEC_TX_TIM1_HI_DATA_BIT_LO_TIME_SHIFT) |
		  (0x4b << TEGRA_CEC_TX_TIM1_DATA_BIT_DURATION_SHIFT) |
		  (0x21 << TEGRA_CEC_TX_TIM1_ACK_NAK_BIT_SAMPLE_TIME_SHIFT));

	cec_write(cec, TEGRA_CEC_TX_TIMING_2,
		  (0x07 << TEGRA_CEC_TX_TIM2_BUS_IDLE_TIME_ADDITIONAL_FRAME_SHIFT) |
		  (0x05 << TEGRA_CEC_TX_TIM2_BUS_IDLE_TIME_NEW_FRAME_SHIFT) |
		  (0x03 << TEGRA_CEC_TX_TIM2_BUS_IDLE_TIME_RETRY_FRAME_SHIFT));

	cec_write(cec, TEGRA_CEC_INT_MASK,
		  TEGRA_CEC_INT_MASK_TX_REGISTER_UNDERRUN |
		  TEGRA_CEC_INT_MASK_TX_FRAME_OR_BLOCK_NAKD |
		  TEGRA_CEC_INT_MASK_TX_ARBITRATION_FAILED |
		  TEGRA_CEC_INT_MASK_TX_BUS_ANOMALY_DETECTED |
		  TEGRA_CEC_INT_MASK_TX_FRAME_TRANSMITTED |
		  TEGRA_CEC_INT_MASK_RX_REGISTER_FULL |
		  TEGRA_CEC_INT_MASK_RX_REGISTER_OVERRUN);

	cec_write(cec, TEGRA_CEC_HW_CONTROL, TEGRA_CEC_HWCTRL_TX_RX_MODE);
	return 0;
}
Beispiel #8
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;
}