Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/mediatek: mtk_hdmi_ddc_v2: Fix multi-byte writes

Currently, the mtk_hdmi_ddc_v2 driver sends a i2c message by calling
the mtk_ddc_wr_one function for each byte of the payload to setup
SI2C_CTRL and DDC_CTRL registers, and perform a sequential write
transfer of one byte at a time to the target device. This leads to
incorrect transfers as the target address (at least) is also sent each
time.

So, rename mtk_ddc_wr_one function to mtk_ddcm_write_hdmi to match the
read function name (mtk_ddcm_read_hdmi) and modify its behaviour to
send all payload data in a single sequential write transfer by filling
the transfer fifo first then starting the transfer with a size equal to
the payload size and not one anymore.

Fixes: 8d0f79886273 ("drm/mediatek: Introduce HDMI/DDC v2 for MT8195/MT8188")
Signed-off-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
Link: https://patchwork.kernel.org/project/dri-devel/patch/20251205-mtk-hdmi-ddc-v2-fixes-v1-2-260dd0d320f4@collabora.com/
Signed-off-by: Chun-Kuang Hu <chunkuang.hu@kernel.org>

authored by

Louis-Alexis Eyraud and committed by
Chun-Kuang Hu
1384cc00 2788c969

+23 -25
+23 -25
drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c
··· 66 66 return 0; 67 67 } 68 68 69 - static int mtk_ddc_wr_one(struct mtk_hdmi_ddc *ddc, u16 addr_id, 70 - u16 offset_id, u8 *wr_data) 69 + static int mtk_ddcm_write_hdmi(struct mtk_hdmi_ddc *ddc, u16 addr_id, 70 + u16 offset_id, u16 data_cnt, u8 *wr_data) 71 71 { 72 72 u32 val; 73 - int ret; 73 + int ret, i; 74 + 75 + /* Don't allow transfer with a size over than the transfer fifo size 76 + * (16 byte) 77 + */ 78 + if (data_cnt > 16) { 79 + dev_err(ddc->dev, "Invalid DDCM write request\n"); 80 + return -EINVAL; 81 + } 74 82 75 83 /* If down, rise bus for write operation */ 76 84 mtk_ddc_check_and_rise_low_bus(ddc); ··· 86 78 regmap_update_bits(ddc->regs, HPD_DDC_CTRL, HPD_DDC_DELAY_CNT, 87 79 FIELD_PREP(HPD_DDC_DELAY_CNT, DDC2_DLY_CNT)); 88 80 81 + /* In case there is no payload data, just do a single write for the 82 + * address only 83 + */ 89 84 if (wr_data) { 90 - regmap_write(ddc->regs, SI2C_CTRL, 91 - FIELD_PREP(SI2C_ADDR, SI2C_ADDR_READ) | 92 - FIELD_PREP(SI2C_WDATA, *wr_data) | 93 - SI2C_WR); 85 + /* Fill transfer fifo with payload data */ 86 + for (i = 0; i < data_cnt; i++) { 87 + regmap_write(ddc->regs, SI2C_CTRL, 88 + FIELD_PREP(SI2C_ADDR, SI2C_ADDR_READ) | 89 + FIELD_PREP(SI2C_WDATA, wr_data[i]) | 90 + SI2C_WR); 91 + } 94 92 } 95 - 96 93 regmap_write(ddc->regs, DDC_CTRL, 97 94 FIELD_PREP(DDC_CTRL_CMD, DDC_CMD_SEQ_WRITE) | 98 - FIELD_PREP(DDC_CTRL_DIN_CNT, wr_data == NULL ? 0 : 1) | 95 + FIELD_PREP(DDC_CTRL_DIN_CNT, wr_data == NULL ? 0 : data_cnt) | 99 96 FIELD_PREP(DDC_CTRL_OFFSET, offset_id) | 100 97 FIELD_PREP(DDC_CTRL_ADDR, addr_id)); 101 98 usleep_range(1000, 1250); ··· 273 260 static int mtk_hdmi_ddc_fg_data_write(struct mtk_hdmi_ddc *ddc, u16 b_dev, 274 261 u8 data_addr, u16 data_cnt, u8 *pr_data) 275 262 { 276 - int i, ret; 277 - 278 263 regmap_set_bits(ddc->regs, HDCP2X_POL_CTRL, HDCP2X_DIS_POLL_EN); 279 - /* 280 - * In case there is no payload data, just do a single write for the 281 - * address only 282 - */ 283 - if (data_cnt == 0) 284 - return mtk_ddc_wr_one(ddc, b_dev, data_addr, NULL); 285 264 286 - i = 0; 287 - do { 288 - ret = mtk_ddc_wr_one(ddc, b_dev, data_addr + i, pr_data + i); 289 - if (ret) 290 - return ret; 291 - } while (++i < data_cnt); 292 - 293 - return 0; 265 + return mtk_ddcm_write_hdmi(ddc, b_dev, data_addr, data_cnt, pr_data); 294 266 } 295 267 296 268 static int mtk_hdmi_ddc_v2_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)