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