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