18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/delay.h>
78c2ecf20Sopenharmony_ci#include <drm/drm_print.h>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include "dp_reg.h"
108c2ecf20Sopenharmony_ci#include "dp_aux.h"
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#define DP_AUX_ENUM_STR(x)		#x
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_cistruct dp_aux_private {
158c2ecf20Sopenharmony_ci	struct device *dev;
168c2ecf20Sopenharmony_ci	struct dp_catalog *catalog;
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci	struct mutex mutex;
198c2ecf20Sopenharmony_ci	struct completion comp;
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci	u32 aux_error_num;
228c2ecf20Sopenharmony_ci	u32 retry_cnt;
238c2ecf20Sopenharmony_ci	bool cmd_busy;
248c2ecf20Sopenharmony_ci	bool native;
258c2ecf20Sopenharmony_ci	bool read;
268c2ecf20Sopenharmony_ci	bool no_send_addr;
278c2ecf20Sopenharmony_ci	bool no_send_stop;
288c2ecf20Sopenharmony_ci	u32 offset;
298c2ecf20Sopenharmony_ci	u32 segment;
308c2ecf20Sopenharmony_ci	u32 isr;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	struct drm_dp_aux dp_aux;
338c2ecf20Sopenharmony_ci};
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic const char *dp_aux_get_error(u32 aux_error)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	switch (aux_error) {
388c2ecf20Sopenharmony_ci	case DP_AUX_ERR_NONE:
398c2ecf20Sopenharmony_ci		return DP_AUX_ENUM_STR(DP_AUX_ERR_NONE);
408c2ecf20Sopenharmony_ci	case DP_AUX_ERR_ADDR:
418c2ecf20Sopenharmony_ci		return DP_AUX_ENUM_STR(DP_AUX_ERR_ADDR);
428c2ecf20Sopenharmony_ci	case DP_AUX_ERR_TOUT:
438c2ecf20Sopenharmony_ci		return DP_AUX_ENUM_STR(DP_AUX_ERR_TOUT);
448c2ecf20Sopenharmony_ci	case DP_AUX_ERR_NACK:
458c2ecf20Sopenharmony_ci		return DP_AUX_ENUM_STR(DP_AUX_ERR_NACK);
468c2ecf20Sopenharmony_ci	case DP_AUX_ERR_DEFER:
478c2ecf20Sopenharmony_ci		return DP_AUX_ENUM_STR(DP_AUX_ERR_DEFER);
488c2ecf20Sopenharmony_ci	case DP_AUX_ERR_NACK_DEFER:
498c2ecf20Sopenharmony_ci		return DP_AUX_ENUM_STR(DP_AUX_ERR_NACK_DEFER);
508c2ecf20Sopenharmony_ci	default:
518c2ecf20Sopenharmony_ci		return "unknown";
528c2ecf20Sopenharmony_ci	}
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic u32 dp_aux_write(struct dp_aux_private *aux,
568c2ecf20Sopenharmony_ci			struct drm_dp_aux_msg *msg)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	u32 data[4], reg, len;
598c2ecf20Sopenharmony_ci	u8 *msgdata = msg->buffer;
608c2ecf20Sopenharmony_ci	int const AUX_CMD_FIFO_LEN = 128;
618c2ecf20Sopenharmony_ci	int i = 0;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	if (aux->read)
648c2ecf20Sopenharmony_ci		len = 4;
658c2ecf20Sopenharmony_ci	else
668c2ecf20Sopenharmony_ci		len = msg->size + 4;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	/*
698c2ecf20Sopenharmony_ci	 * cmd fifo only has depth of 144 bytes
708c2ecf20Sopenharmony_ci	 * limit buf length to 128 bytes here
718c2ecf20Sopenharmony_ci	 */
728c2ecf20Sopenharmony_ci	if (len > AUX_CMD_FIFO_LEN) {
738c2ecf20Sopenharmony_ci		DRM_ERROR("buf size greater than allowed size of 128 bytes\n");
748c2ecf20Sopenharmony_ci		return 0;
758c2ecf20Sopenharmony_ci	}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	/* Pack cmd and write to HW */
788c2ecf20Sopenharmony_ci	data[0] = (msg->address >> 16) & 0xf; /* addr[19:16] */
798c2ecf20Sopenharmony_ci	if (aux->read)
808c2ecf20Sopenharmony_ci		data[0] |=  BIT(4); /* R/W */
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	data[1] = (msg->address >> 8) & 0xff;	/* addr[15:8] */
838c2ecf20Sopenharmony_ci	data[2] = msg->address & 0xff;		/* addr[7:0] */
848c2ecf20Sopenharmony_ci	data[3] = (msg->size - 1) & 0xff;	/* len[7:0] */
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	for (i = 0; i < len; i++) {
878c2ecf20Sopenharmony_ci		reg = (i < 4) ? data[i] : msgdata[i - 4];
888c2ecf20Sopenharmony_ci		/* index = 0, write */
898c2ecf20Sopenharmony_ci		reg = (((reg) << DP_AUX_DATA_OFFSET)
908c2ecf20Sopenharmony_ci		       & DP_AUX_DATA_MASK) | DP_AUX_DATA_WRITE;
918c2ecf20Sopenharmony_ci		if (i == 0)
928c2ecf20Sopenharmony_ci			reg |= DP_AUX_DATA_INDEX_WRITE;
938c2ecf20Sopenharmony_ci		aux->catalog->aux_data = reg;
948c2ecf20Sopenharmony_ci		dp_catalog_aux_write_data(aux->catalog);
958c2ecf20Sopenharmony_ci	}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	dp_catalog_aux_clear_trans(aux->catalog, false);
988c2ecf20Sopenharmony_ci	dp_catalog_aux_clear_hw_interrupts(aux->catalog);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	reg = 0; /* Transaction number == 1 */
1018c2ecf20Sopenharmony_ci	if (!aux->native) { /* i2c */
1028c2ecf20Sopenharmony_ci		reg |= DP_AUX_TRANS_CTRL_I2C;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci		if (aux->no_send_addr)
1058c2ecf20Sopenharmony_ci			reg |= DP_AUX_TRANS_CTRL_NO_SEND_ADDR;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci		if (aux->no_send_stop)
1088c2ecf20Sopenharmony_ci			reg |= DP_AUX_TRANS_CTRL_NO_SEND_STOP;
1098c2ecf20Sopenharmony_ci	}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	reg |= DP_AUX_TRANS_CTRL_GO;
1128c2ecf20Sopenharmony_ci	aux->catalog->aux_data = reg;
1138c2ecf20Sopenharmony_ci	dp_catalog_aux_write_trans(aux->catalog);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	return len;
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic int dp_aux_cmd_fifo_tx(struct dp_aux_private *aux,
1198c2ecf20Sopenharmony_ci			      struct drm_dp_aux_msg *msg)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	u32 ret, len, timeout;
1228c2ecf20Sopenharmony_ci	int aux_timeout_ms = HZ/4;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	reinit_completion(&aux->comp);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	len = dp_aux_write(aux, msg);
1278c2ecf20Sopenharmony_ci	if (len == 0) {
1288c2ecf20Sopenharmony_ci		DRM_ERROR("DP AUX write failed\n");
1298c2ecf20Sopenharmony_ci		return -EINVAL;
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	timeout = wait_for_completion_timeout(&aux->comp, aux_timeout_ms);
1338c2ecf20Sopenharmony_ci	if (!timeout) {
1348c2ecf20Sopenharmony_ci		DRM_ERROR("aux %s timeout\n", (aux->read ? "read" : "write"));
1358c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	if (aux->aux_error_num == DP_AUX_ERR_NONE) {
1398c2ecf20Sopenharmony_ci		ret = len;
1408c2ecf20Sopenharmony_ci	} else {
1418c2ecf20Sopenharmony_ci		DRM_ERROR_RATELIMITED("aux err: %s\n",
1428c2ecf20Sopenharmony_ci			dp_aux_get_error(aux->aux_error_num));
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci		ret = -EINVAL;
1458c2ecf20Sopenharmony_ci	}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	return ret;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic void dp_aux_cmd_fifo_rx(struct dp_aux_private *aux,
1518c2ecf20Sopenharmony_ci		struct drm_dp_aux_msg *msg)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	u32 data;
1548c2ecf20Sopenharmony_ci	u8 *dp;
1558c2ecf20Sopenharmony_ci	u32 i, actual_i;
1568c2ecf20Sopenharmony_ci	u32 len = msg->size;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	dp_catalog_aux_clear_trans(aux->catalog, true);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	data = DP_AUX_DATA_INDEX_WRITE; /* INDEX_WRITE */
1618c2ecf20Sopenharmony_ci	data |= DP_AUX_DATA_READ;  /* read */
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	aux->catalog->aux_data = data;
1648c2ecf20Sopenharmony_ci	dp_catalog_aux_write_data(aux->catalog);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	dp = msg->buffer;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	/* discard first byte */
1698c2ecf20Sopenharmony_ci	data = dp_catalog_aux_read_data(aux->catalog);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	for (i = 0; i < len; i++) {
1728c2ecf20Sopenharmony_ci		data = dp_catalog_aux_read_data(aux->catalog);
1738c2ecf20Sopenharmony_ci		*dp++ = (u8)((data >> DP_AUX_DATA_OFFSET) & 0xff);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci		actual_i = (data >> DP_AUX_DATA_INDEX_OFFSET) & 0xFF;
1768c2ecf20Sopenharmony_ci		if (i != actual_i)
1778c2ecf20Sopenharmony_ci			DRM_ERROR("Index mismatch: expected %d, found %d\n",
1788c2ecf20Sopenharmony_ci				i, actual_i);
1798c2ecf20Sopenharmony_ci	}
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cistatic void dp_aux_native_handler(struct dp_aux_private *aux)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	u32 isr = aux->isr;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	if (isr & DP_INTR_AUX_I2C_DONE)
1878c2ecf20Sopenharmony_ci		aux->aux_error_num = DP_AUX_ERR_NONE;
1888c2ecf20Sopenharmony_ci	else if (isr & DP_INTR_WRONG_ADDR)
1898c2ecf20Sopenharmony_ci		aux->aux_error_num = DP_AUX_ERR_ADDR;
1908c2ecf20Sopenharmony_ci	else if (isr & DP_INTR_TIMEOUT)
1918c2ecf20Sopenharmony_ci		aux->aux_error_num = DP_AUX_ERR_TOUT;
1928c2ecf20Sopenharmony_ci	if (isr & DP_INTR_NACK_DEFER)
1938c2ecf20Sopenharmony_ci		aux->aux_error_num = DP_AUX_ERR_NACK;
1948c2ecf20Sopenharmony_ci	if (isr & DP_INTR_AUX_ERROR) {
1958c2ecf20Sopenharmony_ci		aux->aux_error_num = DP_AUX_ERR_PHY;
1968c2ecf20Sopenharmony_ci		dp_catalog_aux_clear_hw_interrupts(aux->catalog);
1978c2ecf20Sopenharmony_ci	}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	complete(&aux->comp);
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistatic void dp_aux_i2c_handler(struct dp_aux_private *aux)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	u32 isr = aux->isr;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	if (isr & DP_INTR_AUX_I2C_DONE) {
2078c2ecf20Sopenharmony_ci		if (isr & (DP_INTR_I2C_NACK | DP_INTR_I2C_DEFER))
2088c2ecf20Sopenharmony_ci			aux->aux_error_num = DP_AUX_ERR_NACK;
2098c2ecf20Sopenharmony_ci		else
2108c2ecf20Sopenharmony_ci			aux->aux_error_num = DP_AUX_ERR_NONE;
2118c2ecf20Sopenharmony_ci	} else {
2128c2ecf20Sopenharmony_ci		if (isr & DP_INTR_WRONG_ADDR)
2138c2ecf20Sopenharmony_ci			aux->aux_error_num = DP_AUX_ERR_ADDR;
2148c2ecf20Sopenharmony_ci		else if (isr & DP_INTR_TIMEOUT)
2158c2ecf20Sopenharmony_ci			aux->aux_error_num = DP_AUX_ERR_TOUT;
2168c2ecf20Sopenharmony_ci		if (isr & DP_INTR_NACK_DEFER)
2178c2ecf20Sopenharmony_ci			aux->aux_error_num = DP_AUX_ERR_NACK_DEFER;
2188c2ecf20Sopenharmony_ci		if (isr & DP_INTR_I2C_NACK)
2198c2ecf20Sopenharmony_ci			aux->aux_error_num = DP_AUX_ERR_NACK;
2208c2ecf20Sopenharmony_ci		if (isr & DP_INTR_I2C_DEFER)
2218c2ecf20Sopenharmony_ci			aux->aux_error_num = DP_AUX_ERR_DEFER;
2228c2ecf20Sopenharmony_ci		if (isr & DP_INTR_AUX_ERROR) {
2238c2ecf20Sopenharmony_ci			aux->aux_error_num = DP_AUX_ERR_PHY;
2248c2ecf20Sopenharmony_ci			dp_catalog_aux_clear_hw_interrupts(aux->catalog);
2258c2ecf20Sopenharmony_ci		}
2268c2ecf20Sopenharmony_ci	}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	complete(&aux->comp);
2298c2ecf20Sopenharmony_ci}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_cistatic void dp_aux_update_offset_and_segment(struct dp_aux_private *aux,
2328c2ecf20Sopenharmony_ci					     struct drm_dp_aux_msg *input_msg)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	u32 edid_address = 0x50;
2358c2ecf20Sopenharmony_ci	u32 segment_address = 0x30;
2368c2ecf20Sopenharmony_ci	bool i2c_read = input_msg->request &
2378c2ecf20Sopenharmony_ci		(DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
2388c2ecf20Sopenharmony_ci	u8 *data;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	if (aux->native || i2c_read || ((input_msg->address != edid_address) &&
2418c2ecf20Sopenharmony_ci		(input_msg->address != segment_address)))
2428c2ecf20Sopenharmony_ci		return;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	data = input_msg->buffer;
2468c2ecf20Sopenharmony_ci	if (input_msg->address == segment_address)
2478c2ecf20Sopenharmony_ci		aux->segment = *data;
2488c2ecf20Sopenharmony_ci	else
2498c2ecf20Sopenharmony_ci		aux->offset = *data;
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci/**
2538c2ecf20Sopenharmony_ci * dp_aux_transfer_helper() - helper function for EDID read transactions
2548c2ecf20Sopenharmony_ci *
2558c2ecf20Sopenharmony_ci * @aux: DP AUX private structure
2568c2ecf20Sopenharmony_ci * @input_msg: input message from DRM upstream APIs
2578c2ecf20Sopenharmony_ci * @send_seg: send the segment to sink
2588c2ecf20Sopenharmony_ci *
2598c2ecf20Sopenharmony_ci * return: void
2608c2ecf20Sopenharmony_ci *
2618c2ecf20Sopenharmony_ci * This helper function is used to fix EDID reads for non-compliant
2628c2ecf20Sopenharmony_ci * sinks that do not handle the i2c middle-of-transaction flag correctly.
2638c2ecf20Sopenharmony_ci */
2648c2ecf20Sopenharmony_cistatic void dp_aux_transfer_helper(struct dp_aux_private *aux,
2658c2ecf20Sopenharmony_ci				   struct drm_dp_aux_msg *input_msg,
2668c2ecf20Sopenharmony_ci				   bool send_seg)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	struct drm_dp_aux_msg helper_msg;
2698c2ecf20Sopenharmony_ci	u32 message_size = 0x10;
2708c2ecf20Sopenharmony_ci	u32 segment_address = 0x30;
2718c2ecf20Sopenharmony_ci	u32 const edid_block_length = 0x80;
2728c2ecf20Sopenharmony_ci	bool i2c_mot = input_msg->request & DP_AUX_I2C_MOT;
2738c2ecf20Sopenharmony_ci	bool i2c_read = input_msg->request &
2748c2ecf20Sopenharmony_ci		(DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	if (!i2c_mot || !i2c_read || (input_msg->size == 0))
2778c2ecf20Sopenharmony_ci		return;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	/*
2808c2ecf20Sopenharmony_ci	 * Sending the segment value and EDID offset will be performed
2818c2ecf20Sopenharmony_ci	 * from the DRM upstream EDID driver for each block. Avoid
2828c2ecf20Sopenharmony_ci	 * duplicate AUX transactions related to this while reading the
2838c2ecf20Sopenharmony_ci	 * first 16 bytes of each block.
2848c2ecf20Sopenharmony_ci	 */
2858c2ecf20Sopenharmony_ci	if (!(aux->offset % edid_block_length) || !send_seg)
2868c2ecf20Sopenharmony_ci		goto end;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	aux->read = false;
2898c2ecf20Sopenharmony_ci	aux->cmd_busy = true;
2908c2ecf20Sopenharmony_ci	aux->no_send_addr = true;
2918c2ecf20Sopenharmony_ci	aux->no_send_stop = true;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	/*
2948c2ecf20Sopenharmony_ci	 * Send the segment address for every i2c read in which the
2958c2ecf20Sopenharmony_ci	 * middle-of-tranaction flag is set. This is required to support EDID
2968c2ecf20Sopenharmony_ci	 * reads of more than 2 blocks as the segment address is reset to 0
2978c2ecf20Sopenharmony_ci	 * since we are overriding the middle-of-transaction flag for read
2988c2ecf20Sopenharmony_ci	 * transactions.
2998c2ecf20Sopenharmony_ci	 */
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	if (aux->segment) {
3028c2ecf20Sopenharmony_ci		memset(&helper_msg, 0, sizeof(helper_msg));
3038c2ecf20Sopenharmony_ci		helper_msg.address = segment_address;
3048c2ecf20Sopenharmony_ci		helper_msg.buffer = &aux->segment;
3058c2ecf20Sopenharmony_ci		helper_msg.size = 1;
3068c2ecf20Sopenharmony_ci		dp_aux_cmd_fifo_tx(aux, &helper_msg);
3078c2ecf20Sopenharmony_ci	}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	/*
3108c2ecf20Sopenharmony_ci	 * Send the offset address for every i2c read in which the
3118c2ecf20Sopenharmony_ci	 * middle-of-transaction flag is set. This will ensure that the sink
3128c2ecf20Sopenharmony_ci	 * will update its read pointer and return the correct portion of the
3138c2ecf20Sopenharmony_ci	 * EDID buffer in the subsequent i2c read trasntion triggered in the
3148c2ecf20Sopenharmony_ci	 * native AUX transfer function.
3158c2ecf20Sopenharmony_ci	 */
3168c2ecf20Sopenharmony_ci	memset(&helper_msg, 0, sizeof(helper_msg));
3178c2ecf20Sopenharmony_ci	helper_msg.address = input_msg->address;
3188c2ecf20Sopenharmony_ci	helper_msg.buffer = &aux->offset;
3198c2ecf20Sopenharmony_ci	helper_msg.size = 1;
3208c2ecf20Sopenharmony_ci	dp_aux_cmd_fifo_tx(aux, &helper_msg);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ciend:
3238c2ecf20Sopenharmony_ci	aux->offset += message_size;
3248c2ecf20Sopenharmony_ci	if (aux->offset == 0x80 || aux->offset == 0x100)
3258c2ecf20Sopenharmony_ci		aux->segment = 0x0; /* reset segment at end of block */
3268c2ecf20Sopenharmony_ci}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci/*
3298c2ecf20Sopenharmony_ci * This function does the real job to process an AUX transaction.
3308c2ecf20Sopenharmony_ci * It will call aux_reset() function to reset the AUX channel,
3318c2ecf20Sopenharmony_ci * if the waiting is timeout.
3328c2ecf20Sopenharmony_ci */
3338c2ecf20Sopenharmony_cistatic ssize_t dp_aux_transfer(struct drm_dp_aux *dp_aux,
3348c2ecf20Sopenharmony_ci			       struct drm_dp_aux_msg *msg)
3358c2ecf20Sopenharmony_ci{
3368c2ecf20Sopenharmony_ci	ssize_t ret;
3378c2ecf20Sopenharmony_ci	int const aux_cmd_native_max = 16;
3388c2ecf20Sopenharmony_ci	int const aux_cmd_i2c_max = 128;
3398c2ecf20Sopenharmony_ci	int const retry_count = 5;
3408c2ecf20Sopenharmony_ci	struct dp_aux_private *aux = container_of(dp_aux,
3418c2ecf20Sopenharmony_ci		struct dp_aux_private, dp_aux);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	mutex_lock(&aux->mutex);
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	aux->native = msg->request & (DP_AUX_NATIVE_WRITE & DP_AUX_NATIVE_READ);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	/* Ignore address only message */
3488c2ecf20Sopenharmony_ci	if ((msg->size == 0) || (msg->buffer == NULL)) {
3498c2ecf20Sopenharmony_ci		msg->reply = aux->native ?
3508c2ecf20Sopenharmony_ci			DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
3518c2ecf20Sopenharmony_ci		ret = msg->size;
3528c2ecf20Sopenharmony_ci		goto unlock_exit;
3538c2ecf20Sopenharmony_ci	}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	/* msg sanity check */
3568c2ecf20Sopenharmony_ci	if ((aux->native && (msg->size > aux_cmd_native_max)) ||
3578c2ecf20Sopenharmony_ci		(msg->size > aux_cmd_i2c_max)) {
3588c2ecf20Sopenharmony_ci		DRM_ERROR("%s: invalid msg: size(%zu), request(%x)\n",
3598c2ecf20Sopenharmony_ci			__func__, msg->size, msg->request);
3608c2ecf20Sopenharmony_ci		ret = -EINVAL;
3618c2ecf20Sopenharmony_ci		goto unlock_exit;
3628c2ecf20Sopenharmony_ci	}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	dp_aux_update_offset_and_segment(aux, msg);
3658c2ecf20Sopenharmony_ci	dp_aux_transfer_helper(aux, msg, true);
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	aux->read = msg->request & (DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
3688c2ecf20Sopenharmony_ci	aux->cmd_busy = true;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	if (aux->read) {
3718c2ecf20Sopenharmony_ci		aux->no_send_addr = true;
3728c2ecf20Sopenharmony_ci		aux->no_send_stop = false;
3738c2ecf20Sopenharmony_ci	} else {
3748c2ecf20Sopenharmony_ci		aux->no_send_addr = true;
3758c2ecf20Sopenharmony_ci		aux->no_send_stop = true;
3768c2ecf20Sopenharmony_ci	}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	ret = dp_aux_cmd_fifo_tx(aux, msg);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	if (ret < 0) {
3818c2ecf20Sopenharmony_ci		if (aux->native) {
3828c2ecf20Sopenharmony_ci			aux->retry_cnt++;
3838c2ecf20Sopenharmony_ci			if (!(aux->retry_cnt % retry_count))
3848c2ecf20Sopenharmony_ci				dp_catalog_aux_update_cfg(aux->catalog);
3858c2ecf20Sopenharmony_ci			dp_catalog_aux_reset(aux->catalog);
3868c2ecf20Sopenharmony_ci		}
3878c2ecf20Sopenharmony_ci		usleep_range(400, 500); /* at least 400us to next try */
3888c2ecf20Sopenharmony_ci		goto unlock_exit;
3898c2ecf20Sopenharmony_ci	}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	if (aux->aux_error_num == DP_AUX_ERR_NONE) {
3928c2ecf20Sopenharmony_ci		if (aux->read)
3938c2ecf20Sopenharmony_ci			dp_aux_cmd_fifo_rx(aux, msg);
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci		msg->reply = aux->native ?
3968c2ecf20Sopenharmony_ci			DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
3978c2ecf20Sopenharmony_ci	} else {
3988c2ecf20Sopenharmony_ci		/* Reply defer to retry */
3998c2ecf20Sopenharmony_ci		msg->reply = aux->native ?
4008c2ecf20Sopenharmony_ci			DP_AUX_NATIVE_REPLY_DEFER : DP_AUX_I2C_REPLY_DEFER;
4018c2ecf20Sopenharmony_ci	}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	/* Return requested size for success or retry */
4048c2ecf20Sopenharmony_ci	ret = msg->size;
4058c2ecf20Sopenharmony_ci	aux->retry_cnt = 0;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ciunlock_exit:
4088c2ecf20Sopenharmony_ci	aux->cmd_busy = false;
4098c2ecf20Sopenharmony_ci	mutex_unlock(&aux->mutex);
4108c2ecf20Sopenharmony_ci	return ret;
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_civoid dp_aux_isr(struct drm_dp_aux *dp_aux)
4148c2ecf20Sopenharmony_ci{
4158c2ecf20Sopenharmony_ci	struct dp_aux_private *aux;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	if (!dp_aux) {
4188c2ecf20Sopenharmony_ci		DRM_ERROR("invalid input\n");
4198c2ecf20Sopenharmony_ci		return;
4208c2ecf20Sopenharmony_ci	}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	aux->isr = dp_catalog_aux_get_irq(aux->catalog);
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	/* no interrupts pending, return immediately */
4278c2ecf20Sopenharmony_ci	if (!aux->isr)
4288c2ecf20Sopenharmony_ci		return;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	if (!aux->cmd_busy)
4318c2ecf20Sopenharmony_ci		return;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	if (aux->native)
4348c2ecf20Sopenharmony_ci		dp_aux_native_handler(aux);
4358c2ecf20Sopenharmony_ci	else
4368c2ecf20Sopenharmony_ci		dp_aux_i2c_handler(aux);
4378c2ecf20Sopenharmony_ci}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_civoid dp_aux_reconfig(struct drm_dp_aux *dp_aux)
4408c2ecf20Sopenharmony_ci{
4418c2ecf20Sopenharmony_ci	struct dp_aux_private *aux;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	dp_catalog_aux_update_cfg(aux->catalog);
4468c2ecf20Sopenharmony_ci	dp_catalog_aux_reset(aux->catalog);
4478c2ecf20Sopenharmony_ci}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_civoid dp_aux_init(struct drm_dp_aux *dp_aux)
4508c2ecf20Sopenharmony_ci{
4518c2ecf20Sopenharmony_ci	struct dp_aux_private *aux;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	if (!dp_aux) {
4548c2ecf20Sopenharmony_ci		DRM_ERROR("invalid input\n");
4558c2ecf20Sopenharmony_ci		return;
4568c2ecf20Sopenharmony_ci	}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	dp_catalog_aux_enable(aux->catalog, true);
4618c2ecf20Sopenharmony_ci	aux->retry_cnt = 0;
4628c2ecf20Sopenharmony_ci}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_civoid dp_aux_deinit(struct drm_dp_aux *dp_aux)
4658c2ecf20Sopenharmony_ci{
4668c2ecf20Sopenharmony_ci	struct dp_aux_private *aux;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	dp_catalog_aux_enable(aux->catalog, false);
4718c2ecf20Sopenharmony_ci}
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ciint dp_aux_register(struct drm_dp_aux *dp_aux)
4748c2ecf20Sopenharmony_ci{
4758c2ecf20Sopenharmony_ci	struct dp_aux_private *aux;
4768c2ecf20Sopenharmony_ci	int ret;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	if (!dp_aux) {
4798c2ecf20Sopenharmony_ci		DRM_ERROR("invalid input\n");
4808c2ecf20Sopenharmony_ci		return -EINVAL;
4818c2ecf20Sopenharmony_ci	}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	aux->dp_aux.name = "dpu_dp_aux";
4868c2ecf20Sopenharmony_ci	aux->dp_aux.dev = aux->dev;
4878c2ecf20Sopenharmony_ci	aux->dp_aux.transfer = dp_aux_transfer;
4888c2ecf20Sopenharmony_ci	ret = drm_dp_aux_register(&aux->dp_aux);
4898c2ecf20Sopenharmony_ci	if (ret) {
4908c2ecf20Sopenharmony_ci		DRM_ERROR("%s: failed to register drm aux: %d\n", __func__,
4918c2ecf20Sopenharmony_ci				ret);
4928c2ecf20Sopenharmony_ci		return ret;
4938c2ecf20Sopenharmony_ci	}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	return 0;
4968c2ecf20Sopenharmony_ci}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_civoid dp_aux_unregister(struct drm_dp_aux *dp_aux)
4998c2ecf20Sopenharmony_ci{
5008c2ecf20Sopenharmony_ci	drm_dp_aux_unregister(dp_aux);
5018c2ecf20Sopenharmony_ci}
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_cistruct drm_dp_aux *dp_aux_get(struct device *dev, struct dp_catalog *catalog)
5048c2ecf20Sopenharmony_ci{
5058c2ecf20Sopenharmony_ci	struct dp_aux_private *aux;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	if (!catalog) {
5088c2ecf20Sopenharmony_ci		DRM_ERROR("invalid input\n");
5098c2ecf20Sopenharmony_ci		return ERR_PTR(-ENODEV);
5108c2ecf20Sopenharmony_ci	}
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	aux = devm_kzalloc(dev, sizeof(*aux), GFP_KERNEL);
5138c2ecf20Sopenharmony_ci	if (!aux)
5148c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	init_completion(&aux->comp);
5178c2ecf20Sopenharmony_ci	aux->cmd_busy = false;
5188c2ecf20Sopenharmony_ci	mutex_init(&aux->mutex);
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	aux->dev = dev;
5218c2ecf20Sopenharmony_ci	aux->catalog = catalog;
5228c2ecf20Sopenharmony_ci	aux->retry_cnt = 0;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	return &aux->dp_aux;
5258c2ecf20Sopenharmony_ci}
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_civoid dp_aux_put(struct drm_dp_aux *dp_aux)
5288c2ecf20Sopenharmony_ci{
5298c2ecf20Sopenharmony_ci	struct dp_aux_private *aux;
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	if (!dp_aux)
5328c2ecf20Sopenharmony_ci		return;
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	mutex_destroy(&aux->mutex);
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	devm_kfree(aux->dev, aux);
5398c2ecf20Sopenharmony_ci}
540