18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright(c) 2016, Analogix Semiconductor. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Based on anx7808 driver obtained from chromeos with copyright: 68c2ecf20Sopenharmony_ci * Copyright(c) 2013, Google Inc. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include <linux/regmap.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <drm/drm.h> 118c2ecf20Sopenharmony_ci#include <drm/drm_dp_helper.h> 128c2ecf20Sopenharmony_ci#include <drm/drm_print.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "analogix-i2c-dptx.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define AUX_WAIT_TIMEOUT_MS 15 178c2ecf20Sopenharmony_ci#define AUX_CH_BUFFER_SIZE 16 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic int anx_i2c_dp_clear_bits(struct regmap *map, u8 reg, u8 mask) 208c2ecf20Sopenharmony_ci{ 218c2ecf20Sopenharmony_ci return regmap_update_bits(map, reg, mask, 0); 228c2ecf20Sopenharmony_ci} 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic bool anx_dp_aux_op_finished(struct regmap *map_dptx) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci unsigned int value; 278c2ecf20Sopenharmony_ci int err; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci err = regmap_read(map_dptx, SP_DP_AUX_CH_CTRL2_REG, &value); 308c2ecf20Sopenharmony_ci if (err < 0) 318c2ecf20Sopenharmony_ci return false; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci return (value & SP_AUX_EN) == 0; 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic int anx_dp_aux_wait(struct regmap *map_dptx) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci unsigned long timeout; 398c2ecf20Sopenharmony_ci unsigned int status; 408c2ecf20Sopenharmony_ci int err; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(AUX_WAIT_TIMEOUT_MS) + 1; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci while (!anx_dp_aux_op_finished(map_dptx)) { 458c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) { 468c2ecf20Sopenharmony_ci if (!anx_dp_aux_op_finished(map_dptx)) { 478c2ecf20Sopenharmony_ci DRM_ERROR("Timed out waiting AUX to finish\n"); 488c2ecf20Sopenharmony_ci return -ETIMEDOUT; 498c2ecf20Sopenharmony_ci } 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci break; 528c2ecf20Sopenharmony_ci } 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci /* Read the AUX channel access status */ 588c2ecf20Sopenharmony_ci err = regmap_read(map_dptx, SP_AUX_CH_STATUS_REG, &status); 598c2ecf20Sopenharmony_ci if (err < 0) { 608c2ecf20Sopenharmony_ci DRM_ERROR("Failed to read from AUX channel: %d\n", err); 618c2ecf20Sopenharmony_ci return err; 628c2ecf20Sopenharmony_ci } 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci if (status & SP_AUX_STATUS) { 658c2ecf20Sopenharmony_ci DRM_ERROR("Failed to wait for AUX channel (status: %02x)\n", 668c2ecf20Sopenharmony_ci status); 678c2ecf20Sopenharmony_ci return -ETIMEDOUT; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci return 0; 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic int anx_dp_aux_address(struct regmap *map_dptx, unsigned int addr) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci int err; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci err = regmap_write(map_dptx, SP_AUX_ADDR_7_0_REG, addr & 0xff); 788c2ecf20Sopenharmony_ci if (err) 798c2ecf20Sopenharmony_ci return err; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci err = regmap_write(map_dptx, SP_AUX_ADDR_15_8_REG, 828c2ecf20Sopenharmony_ci (addr & 0xff00) >> 8); 838c2ecf20Sopenharmony_ci if (err) 848c2ecf20Sopenharmony_ci return err; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci /* 878c2ecf20Sopenharmony_ci * DP AUX CH Address Register #2, only update bits[3:0] 888c2ecf20Sopenharmony_ci * [7:4] RESERVED 898c2ecf20Sopenharmony_ci * [3:0] AUX_ADDR[19:16], Register control AUX CH address. 908c2ecf20Sopenharmony_ci */ 918c2ecf20Sopenharmony_ci err = regmap_update_bits(map_dptx, SP_AUX_ADDR_19_16_REG, 928c2ecf20Sopenharmony_ci SP_AUX_ADDR_19_16_MASK, 938c2ecf20Sopenharmony_ci (addr & 0xf0000) >> 16); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if (err) 968c2ecf20Sopenharmony_ci return err; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci return 0; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cissize_t anx_dp_aux_transfer(struct regmap *map_dptx, 1028c2ecf20Sopenharmony_ci struct drm_dp_aux_msg *msg) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci u8 ctrl1 = msg->request; 1058c2ecf20Sopenharmony_ci u8 ctrl2 = SP_AUX_EN; 1068c2ecf20Sopenharmony_ci u8 *buffer = msg->buffer; 1078c2ecf20Sopenharmony_ci int err; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* The DP AUX transmit and receive buffer has 16 bytes. */ 1108c2ecf20Sopenharmony_ci if (WARN_ON(msg->size > AUX_CH_BUFFER_SIZE)) 1118c2ecf20Sopenharmony_ci return -E2BIG; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci /* Zero-sized messages specify address-only transactions. */ 1148c2ecf20Sopenharmony_ci if (msg->size < 1) 1158c2ecf20Sopenharmony_ci ctrl2 |= SP_ADDR_ONLY; 1168c2ecf20Sopenharmony_ci else /* For non-zero-sized set the length field. */ 1178c2ecf20Sopenharmony_ci ctrl1 |= (msg->size - 1) << SP_AUX_LENGTH_SHIFT; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if ((msg->size > 0) && ((msg->request & DP_AUX_I2C_READ) == 0)) { 1208c2ecf20Sopenharmony_ci /* When WRITE | MOT write values to data buffer */ 1218c2ecf20Sopenharmony_ci err = regmap_bulk_write(map_dptx, 1228c2ecf20Sopenharmony_ci SP_DP_BUF_DATA0_REG, buffer, 1238c2ecf20Sopenharmony_ci msg->size); 1248c2ecf20Sopenharmony_ci if (err) 1258c2ecf20Sopenharmony_ci return err; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* Write address and request */ 1298c2ecf20Sopenharmony_ci err = anx_dp_aux_address(map_dptx, msg->address); 1308c2ecf20Sopenharmony_ci if (err) 1318c2ecf20Sopenharmony_ci return err; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci err = regmap_write(map_dptx, SP_DP_AUX_CH_CTRL1_REG, ctrl1); 1348c2ecf20Sopenharmony_ci if (err) 1358c2ecf20Sopenharmony_ci return err; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* Start transaction */ 1388c2ecf20Sopenharmony_ci err = regmap_update_bits(map_dptx, SP_DP_AUX_CH_CTRL2_REG, 1398c2ecf20Sopenharmony_ci SP_ADDR_ONLY | SP_AUX_EN, ctrl2); 1408c2ecf20Sopenharmony_ci if (err) 1418c2ecf20Sopenharmony_ci return err; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci err = anx_dp_aux_wait(map_dptx); 1448c2ecf20Sopenharmony_ci if (err) 1458c2ecf20Sopenharmony_ci return err; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci msg->reply = DP_AUX_I2C_REPLY_ACK; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if ((msg->size > 0) && (msg->request & DP_AUX_I2C_READ)) { 1508c2ecf20Sopenharmony_ci /* Read values from data buffer */ 1518c2ecf20Sopenharmony_ci err = regmap_bulk_read(map_dptx, 1528c2ecf20Sopenharmony_ci SP_DP_BUF_DATA0_REG, buffer, 1538c2ecf20Sopenharmony_ci msg->size); 1548c2ecf20Sopenharmony_ci if (err) 1558c2ecf20Sopenharmony_ci return err; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci err = anx_i2c_dp_clear_bits(map_dptx, SP_DP_AUX_CH_CTRL2_REG, 1598c2ecf20Sopenharmony_ci SP_ADDR_ONLY); 1608c2ecf20Sopenharmony_ci if (err) 1618c2ecf20Sopenharmony_ci return err; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci return msg->size; 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(anx_dp_aux_transfer); 166