18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include "edp.h"
78c2ecf20Sopenharmony_ci#include "edp.xml.h"
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#define AUX_CMD_FIFO_LEN	144
108c2ecf20Sopenharmony_ci#define AUX_CMD_NATIVE_MAX	16
118c2ecf20Sopenharmony_ci#define AUX_CMD_I2C_MAX		128
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#define EDP_INTR_AUX_I2C_ERR	\
148c2ecf20Sopenharmony_ci	(EDP_INTERRUPT_REG_1_WRONG_ADDR | EDP_INTERRUPT_REG_1_TIMEOUT | \
158c2ecf20Sopenharmony_ci	EDP_INTERRUPT_REG_1_NACK_DEFER | EDP_INTERRUPT_REG_1_WRONG_DATA_CNT | \
168c2ecf20Sopenharmony_ci	EDP_INTERRUPT_REG_1_I2C_NACK | EDP_INTERRUPT_REG_1_I2C_DEFER)
178c2ecf20Sopenharmony_ci#define EDP_INTR_TRANS_STATUS	\
188c2ecf20Sopenharmony_ci	(EDP_INTERRUPT_REG_1_AUX_I2C_DONE | EDP_INTR_AUX_I2C_ERR)
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistruct edp_aux {
218c2ecf20Sopenharmony_ci	void __iomem *base;
228c2ecf20Sopenharmony_ci	bool msg_err;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	struct completion msg_comp;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	/* To prevent the message transaction routine from reentry. */
278c2ecf20Sopenharmony_ci	struct mutex msg_mutex;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	struct drm_dp_aux drm_aux;
308c2ecf20Sopenharmony_ci};
318c2ecf20Sopenharmony_ci#define to_edp_aux(x) container_of(x, struct edp_aux, drm_aux)
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic int edp_msg_fifo_tx(struct edp_aux *aux, struct drm_dp_aux_msg *msg)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	u32 data[4];
368c2ecf20Sopenharmony_ci	u32 reg, len;
378c2ecf20Sopenharmony_ci	bool native = msg->request & (DP_AUX_NATIVE_WRITE & DP_AUX_NATIVE_READ);
388c2ecf20Sopenharmony_ci	bool read = msg->request & (DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
398c2ecf20Sopenharmony_ci	u8 *msgdata = msg->buffer;
408c2ecf20Sopenharmony_ci	int i;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	if (read)
438c2ecf20Sopenharmony_ci		len = 4;
448c2ecf20Sopenharmony_ci	else
458c2ecf20Sopenharmony_ci		len = msg->size + 4;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	/*
488c2ecf20Sopenharmony_ci	 * cmd fifo only has depth of 144 bytes
498c2ecf20Sopenharmony_ci	 */
508c2ecf20Sopenharmony_ci	if (len > AUX_CMD_FIFO_LEN)
518c2ecf20Sopenharmony_ci		return -EINVAL;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	/* Pack cmd and write to HW */
548c2ecf20Sopenharmony_ci	data[0] = (msg->address >> 16) & 0xf;	/* addr[19:16] */
558c2ecf20Sopenharmony_ci	if (read)
568c2ecf20Sopenharmony_ci		data[0] |=  BIT(4);		/* R/W */
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	data[1] = (msg->address >> 8) & 0xff;	/* addr[15:8] */
598c2ecf20Sopenharmony_ci	data[2] = msg->address & 0xff;		/* addr[7:0] */
608c2ecf20Sopenharmony_ci	data[3] = (msg->size - 1) & 0xff;	/* len[7:0] */
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	for (i = 0; i < len; i++) {
638c2ecf20Sopenharmony_ci		reg = (i < 4) ? data[i] : msgdata[i - 4];
648c2ecf20Sopenharmony_ci		reg = EDP_AUX_DATA_DATA(reg); /* index = 0, write */
658c2ecf20Sopenharmony_ci		if (i == 0)
668c2ecf20Sopenharmony_ci			reg |= EDP_AUX_DATA_INDEX_WRITE;
678c2ecf20Sopenharmony_ci		edp_write(aux->base + REG_EDP_AUX_DATA, reg);
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	reg = 0; /* Transaction number is always 1 */
718c2ecf20Sopenharmony_ci	if (!native) /* i2c */
728c2ecf20Sopenharmony_ci		reg |= EDP_AUX_TRANS_CTRL_I2C;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	reg |= EDP_AUX_TRANS_CTRL_GO;
758c2ecf20Sopenharmony_ci	edp_write(aux->base + REG_EDP_AUX_TRANS_CTRL, reg);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	return 0;
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic int edp_msg_fifo_rx(struct edp_aux *aux, struct drm_dp_aux_msg *msg)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	u32 data;
838c2ecf20Sopenharmony_ci	u8 *dp;
848c2ecf20Sopenharmony_ci	int i;
858c2ecf20Sopenharmony_ci	u32 len = msg->size;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	edp_write(aux->base + REG_EDP_AUX_DATA,
888c2ecf20Sopenharmony_ci		EDP_AUX_DATA_INDEX_WRITE | EDP_AUX_DATA_READ); /* index = 0 */
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	dp = msg->buffer;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	/* discard first byte */
938c2ecf20Sopenharmony_ci	data = edp_read(aux->base + REG_EDP_AUX_DATA);
948c2ecf20Sopenharmony_ci	for (i = 0; i < len; i++) {
958c2ecf20Sopenharmony_ci		data = edp_read(aux->base + REG_EDP_AUX_DATA);
968c2ecf20Sopenharmony_ci		dp[i] = (u8)((data >> 8) & 0xff);
978c2ecf20Sopenharmony_ci	}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	return 0;
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci/*
1038c2ecf20Sopenharmony_ci * This function does the real job to process an AUX transaction.
1048c2ecf20Sopenharmony_ci * It will call msm_edp_aux_ctrl() function to reset the AUX channel,
1058c2ecf20Sopenharmony_ci * if the waiting is timeout.
1068c2ecf20Sopenharmony_ci * The caller who triggers the transaction should avoid the
1078c2ecf20Sopenharmony_ci * msm_edp_aux_ctrl() running concurrently in other threads, i.e.
1088c2ecf20Sopenharmony_ci * start transaction only when AUX channel is fully enabled.
1098c2ecf20Sopenharmony_ci */
1108c2ecf20Sopenharmony_cistatic ssize_t edp_aux_transfer(struct drm_dp_aux *drm_aux,
1118c2ecf20Sopenharmony_ci		struct drm_dp_aux_msg *msg)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	struct edp_aux *aux = to_edp_aux(drm_aux);
1148c2ecf20Sopenharmony_ci	ssize_t ret;
1158c2ecf20Sopenharmony_ci	unsigned long time_left;
1168c2ecf20Sopenharmony_ci	bool native = msg->request & (DP_AUX_NATIVE_WRITE & DP_AUX_NATIVE_READ);
1178c2ecf20Sopenharmony_ci	bool read = msg->request & (DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	/* Ignore address only message */
1208c2ecf20Sopenharmony_ci	if ((msg->size == 0) || (msg->buffer == NULL)) {
1218c2ecf20Sopenharmony_ci		msg->reply = native ?
1228c2ecf20Sopenharmony_ci			DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
1238c2ecf20Sopenharmony_ci		return msg->size;
1248c2ecf20Sopenharmony_ci	}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	/* msg sanity check */
1278c2ecf20Sopenharmony_ci	if ((native && (msg->size > AUX_CMD_NATIVE_MAX)) ||
1288c2ecf20Sopenharmony_ci		(msg->size > AUX_CMD_I2C_MAX)) {
1298c2ecf20Sopenharmony_ci		pr_err("%s: invalid msg: size(%zu), request(%x)\n",
1308c2ecf20Sopenharmony_ci			__func__, msg->size, msg->request);
1318c2ecf20Sopenharmony_ci		return -EINVAL;
1328c2ecf20Sopenharmony_ci	}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	mutex_lock(&aux->msg_mutex);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	aux->msg_err = false;
1378c2ecf20Sopenharmony_ci	reinit_completion(&aux->msg_comp);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	ret = edp_msg_fifo_tx(aux, msg);
1408c2ecf20Sopenharmony_ci	if (ret < 0)
1418c2ecf20Sopenharmony_ci		goto unlock_exit;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	DBG("wait_for_completion");
1448c2ecf20Sopenharmony_ci	time_left = wait_for_completion_timeout(&aux->msg_comp,
1458c2ecf20Sopenharmony_ci						msecs_to_jiffies(300));
1468c2ecf20Sopenharmony_ci	if (!time_left) {
1478c2ecf20Sopenharmony_ci		/*
1488c2ecf20Sopenharmony_ci		 * Clear GO and reset AUX channel
1498c2ecf20Sopenharmony_ci		 * to cancel the current transaction.
1508c2ecf20Sopenharmony_ci		 */
1518c2ecf20Sopenharmony_ci		edp_write(aux->base + REG_EDP_AUX_TRANS_CTRL, 0);
1528c2ecf20Sopenharmony_ci		msm_edp_aux_ctrl(aux, 1);
1538c2ecf20Sopenharmony_ci		pr_err("%s: aux timeout,\n", __func__);
1548c2ecf20Sopenharmony_ci		ret = -ETIMEDOUT;
1558c2ecf20Sopenharmony_ci		goto unlock_exit;
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci	DBG("completion");
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	if (!aux->msg_err) {
1608c2ecf20Sopenharmony_ci		if (read) {
1618c2ecf20Sopenharmony_ci			ret = edp_msg_fifo_rx(aux, msg);
1628c2ecf20Sopenharmony_ci			if (ret < 0)
1638c2ecf20Sopenharmony_ci				goto unlock_exit;
1648c2ecf20Sopenharmony_ci		}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci		msg->reply = native ?
1678c2ecf20Sopenharmony_ci			DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
1688c2ecf20Sopenharmony_ci	} else {
1698c2ecf20Sopenharmony_ci		/* Reply defer to retry */
1708c2ecf20Sopenharmony_ci		msg->reply = native ?
1718c2ecf20Sopenharmony_ci			DP_AUX_NATIVE_REPLY_DEFER : DP_AUX_I2C_REPLY_DEFER;
1728c2ecf20Sopenharmony_ci		/*
1738c2ecf20Sopenharmony_ci		 * The sleep time in caller is not long enough to make sure
1748c2ecf20Sopenharmony_ci		 * our H/W completes transactions. Add more defer time here.
1758c2ecf20Sopenharmony_ci		 */
1768c2ecf20Sopenharmony_ci		msleep(100);
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	/* Return requested size for success or retry */
1808c2ecf20Sopenharmony_ci	ret = msg->size;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ciunlock_exit:
1838c2ecf20Sopenharmony_ci	mutex_unlock(&aux->msg_mutex);
1848c2ecf20Sopenharmony_ci	return ret;
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_civoid *msm_edp_aux_init(struct device *dev, void __iomem *regbase,
1888c2ecf20Sopenharmony_ci	struct drm_dp_aux **drm_aux)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	struct edp_aux *aux = NULL;
1918c2ecf20Sopenharmony_ci	int ret;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	DBG("");
1948c2ecf20Sopenharmony_ci	aux = devm_kzalloc(dev, sizeof(*aux), GFP_KERNEL);
1958c2ecf20Sopenharmony_ci	if (!aux)
1968c2ecf20Sopenharmony_ci		return NULL;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	aux->base = regbase;
1998c2ecf20Sopenharmony_ci	mutex_init(&aux->msg_mutex);
2008c2ecf20Sopenharmony_ci	init_completion(&aux->msg_comp);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	aux->drm_aux.name = "msm_edp_aux";
2038c2ecf20Sopenharmony_ci	aux->drm_aux.dev = dev;
2048c2ecf20Sopenharmony_ci	aux->drm_aux.transfer = edp_aux_transfer;
2058c2ecf20Sopenharmony_ci	ret = drm_dp_aux_register(&aux->drm_aux);
2068c2ecf20Sopenharmony_ci	if (ret) {
2078c2ecf20Sopenharmony_ci		pr_err("%s: failed to register drm aux: %d\n", __func__, ret);
2088c2ecf20Sopenharmony_ci		mutex_destroy(&aux->msg_mutex);
2098c2ecf20Sopenharmony_ci	}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	if (drm_aux && aux)
2128c2ecf20Sopenharmony_ci		*drm_aux = &aux->drm_aux;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	return aux;
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_civoid msm_edp_aux_destroy(struct device *dev, struct edp_aux *aux)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	if (aux) {
2208c2ecf20Sopenharmony_ci		drm_dp_aux_unregister(&aux->drm_aux);
2218c2ecf20Sopenharmony_ci		mutex_destroy(&aux->msg_mutex);
2228c2ecf20Sopenharmony_ci	}
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ciirqreturn_t msm_edp_aux_irq(struct edp_aux *aux, u32 isr)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	if (isr & EDP_INTR_TRANS_STATUS) {
2288c2ecf20Sopenharmony_ci		DBG("isr=%x", isr);
2298c2ecf20Sopenharmony_ci		edp_write(aux->base + REG_EDP_AUX_TRANS_CTRL, 0);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci		if (isr & EDP_INTR_AUX_I2C_ERR)
2328c2ecf20Sopenharmony_ci			aux->msg_err = true;
2338c2ecf20Sopenharmony_ci		else
2348c2ecf20Sopenharmony_ci			aux->msg_err = false;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci		complete(&aux->msg_comp);
2378c2ecf20Sopenharmony_ci	}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2408c2ecf20Sopenharmony_ci}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_civoid msm_edp_aux_ctrl(struct edp_aux *aux, int enable)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	u32 data;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	DBG("enable=%d", enable);
2478c2ecf20Sopenharmony_ci	data = edp_read(aux->base + REG_EDP_AUX_CTRL);
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	if (enable) {
2508c2ecf20Sopenharmony_ci		data |= EDP_AUX_CTRL_RESET;
2518c2ecf20Sopenharmony_ci		edp_write(aux->base + REG_EDP_AUX_CTRL, data);
2528c2ecf20Sopenharmony_ci		/* Make sure full reset */
2538c2ecf20Sopenharmony_ci		wmb();
2548c2ecf20Sopenharmony_ci		usleep_range(500, 1000);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci		data &= ~EDP_AUX_CTRL_RESET;
2578c2ecf20Sopenharmony_ci		data |= EDP_AUX_CTRL_ENABLE;
2588c2ecf20Sopenharmony_ci		edp_write(aux->base + REG_EDP_AUX_CTRL, data);
2598c2ecf20Sopenharmony_ci	} else {
2608c2ecf20Sopenharmony_ci		data &= ~EDP_AUX_CTRL_ENABLE;
2618c2ecf20Sopenharmony_ci		edp_write(aux->base + REG_EDP_AUX_CTRL, data);
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
265