drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c

Source file repositories/reference/linux-study-clean/drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c

File Facts

System
Linux kernel
Corpus path
drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c
Extension
.c
Size
10830 bytes
Lines
405
Domain
Driver Families
Bucket
drivers/gpu
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct mtk_hdmi_ddc {
	struct device *dev;
	struct regmap *regs;
	struct clk *clk;
	struct i2c_adapter adap;
};

static int mtk_ddc_check_and_rise_low_bus(struct mtk_hdmi_ddc *ddc)
{
	u32 val;

	regmap_read(ddc->regs, HDCP2X_DDCM_STATUS, &val);
	if (val & DDC_I2C_BUS_LOW) {
		regmap_update_bits(ddc->regs, DDC_CTRL, DDC_CTRL_CMD,
				   FIELD_PREP(DDC_CTRL_CMD, DDC_CMD_CLOCK_SCL));
		usleep_range(250, 300);
	}

	if (val & DDC_I2C_NO_ACK) {
		u32 ddc_ctrl, hpd_ddc_ctrl, hpd_ddc_status;

		regmap_read(ddc->regs, DDC_CTRL, &ddc_ctrl);
		regmap_read(ddc->regs, HPD_DDC_CTRL, &hpd_ddc_ctrl);
		regmap_read(ddc->regs, HPD_DDC_STATUS, &hpd_ddc_status);
	}

	if (val & DDC_I2C_NO_ACK)
		return -EIO;

	return 0;
}

static int mtk_ddcm_write_hdmi(struct mtk_hdmi_ddc *ddc, u16 addr_id,
			       u16 offset_id, u16 data_cnt, u8 *wr_data)
{
	u32 val;
	int ret, i;

	/* Don't allow transfer with a size over than the transfer fifo size
	 * (16 byte)
	 */
	if (data_cnt > 16) {
		dev_err(ddc->dev, "Invalid DDCM write request\n");
		return -EINVAL;
	}

	/* If down, rise bus for write operation */
	mtk_ddc_check_and_rise_low_bus(ddc);

	regmap_update_bits(ddc->regs, HPD_DDC_CTRL, HPD_DDC_DELAY_CNT,
			   FIELD_PREP(HPD_DDC_DELAY_CNT, DDC2_DLY_CNT));

	/* In case there is no payload data, just do a single write for the
	 * address only
	 */
	if (wr_data) {
		/* Fill transfer fifo with payload data */
		for (i = 0; i < data_cnt; i++) {
			regmap_write(ddc->regs, SI2C_CTRL,
				     FIELD_PREP(SI2C_ADDR, SI2C_ADDR_READ) |
				     FIELD_PREP(SI2C_WDATA, wr_data[i]) |
				     SI2C_WR);
		}
	}
	regmap_write(ddc->regs, DDC_CTRL,
		     FIELD_PREP(DDC_CTRL_CMD, DDC_CMD_SEQ_WRITE) |
		     FIELD_PREP(DDC_CTRL_DIN_CNT, wr_data == NULL ? 0 : data_cnt) |
		     FIELD_PREP(DDC_CTRL_OFFSET, offset_id) |
		     FIELD_PREP(DDC_CTRL_ADDR, addr_id));
	usleep_range(1000, 1250);

	ret = regmap_read_poll_timeout(ddc->regs, HPD_DDC_STATUS, val,
				       !(val & DDC_I2C_IN_PROG), 500, 1000);
	if (ret) {
		dev_err(ddc->dev, "DDC I2C write timeout\n");

		/* Abort transfer if it is still in progress */
		regmap_update_bits(ddc->regs, DDC_CTRL, DDC_CTRL_CMD,
				   FIELD_PREP(DDC_CTRL_CMD, DDC_CMD_ABORT_XFER));

		return ret;
	}

	/* The I2C bus might be down after WR operation: rise it again */
	ret = mtk_ddc_check_and_rise_low_bus(ddc);
	if (ret) {
		dev_err(ddc->dev, "Error during write operation: No ACK\n");
		return ret;
	}

Annotation

Implementation Notes