Path: blob/master/drivers/gpu/drm/bridge/analogix/analogix-i2c-dptx.c
26516 views
/* SPDX-License-Identifier: GPL-2.0-only */1/*2* Copyright(c) 2016, Analogix Semiconductor.3*4* Based on anx7808 driver obtained from chromeos with copyright:5* Copyright(c) 2013, Google Inc.6*/78#include <linux/export.h>9#include <linux/regmap.h>1011#include <drm/display/drm_dp_helper.h>12#include <drm/drm.h>13#include <drm/drm_print.h>1415#include "analogix-i2c-dptx.h"1617#define AUX_WAIT_TIMEOUT_MS 1518#define AUX_CH_BUFFER_SIZE 161920static int anx_i2c_dp_clear_bits(struct regmap *map, u8 reg, u8 mask)21{22return regmap_update_bits(map, reg, mask, 0);23}2425static bool anx_dp_aux_op_finished(struct regmap *map_dptx)26{27unsigned int value;28int err;2930err = regmap_read(map_dptx, SP_DP_AUX_CH_CTRL2_REG, &value);31if (err < 0)32return false;3334return (value & SP_AUX_EN) == 0;35}3637static int anx_dp_aux_wait(struct regmap *map_dptx)38{39unsigned long timeout;40unsigned int status;41int err;4243timeout = jiffies + msecs_to_jiffies(AUX_WAIT_TIMEOUT_MS) + 1;4445while (!anx_dp_aux_op_finished(map_dptx)) {46if (time_after(jiffies, timeout)) {47if (!anx_dp_aux_op_finished(map_dptx)) {48DRM_ERROR("Timed out waiting AUX to finish\n");49return -ETIMEDOUT;50}5152break;53}5455usleep_range(1000, 2000);56}5758/* Read the AUX channel access status */59err = regmap_read(map_dptx, SP_AUX_CH_STATUS_REG, &status);60if (err < 0) {61DRM_ERROR("Failed to read from AUX channel: %d\n", err);62return err;63}6465if (status & SP_AUX_STATUS) {66DRM_ERROR("Failed to wait for AUX channel (status: %02x)\n",67status);68return -ETIMEDOUT;69}7071return 0;72}7374static int anx_dp_aux_address(struct regmap *map_dptx, unsigned int addr)75{76int err;7778err = regmap_write(map_dptx, SP_AUX_ADDR_7_0_REG, addr & 0xff);79if (err)80return err;8182err = regmap_write(map_dptx, SP_AUX_ADDR_15_8_REG,83(addr & 0xff00) >> 8);84if (err)85return err;8687/*88* DP AUX CH Address Register #2, only update bits[3:0]89* [7:4] RESERVED90* [3:0] AUX_ADDR[19:16], Register control AUX CH address.91*/92err = regmap_update_bits(map_dptx, SP_AUX_ADDR_19_16_REG,93SP_AUX_ADDR_19_16_MASK,94(addr & 0xf0000) >> 16);9596if (err)97return err;9899return 0;100}101102ssize_t anx_dp_aux_transfer(struct regmap *map_dptx,103struct drm_dp_aux_msg *msg)104{105u8 ctrl1 = msg->request;106u8 ctrl2 = SP_AUX_EN;107u8 *buffer = msg->buffer;108int err;109110/* The DP AUX transmit and receive buffer has 16 bytes. */111if (WARN_ON(msg->size > AUX_CH_BUFFER_SIZE))112return -E2BIG;113114/* Zero-sized messages specify address-only transactions. */115if (msg->size < 1)116ctrl2 |= SP_ADDR_ONLY;117else /* For non-zero-sized set the length field. */118ctrl1 |= (msg->size - 1) << SP_AUX_LENGTH_SHIFT;119120if ((msg->size > 0) && ((msg->request & DP_AUX_I2C_READ) == 0)) {121/* When WRITE | MOT write values to data buffer */122err = regmap_bulk_write(map_dptx,123SP_DP_BUF_DATA0_REG, buffer,124msg->size);125if (err)126return err;127}128129/* Write address and request */130err = anx_dp_aux_address(map_dptx, msg->address);131if (err)132return err;133134err = regmap_write(map_dptx, SP_DP_AUX_CH_CTRL1_REG, ctrl1);135if (err)136return err;137138/* Start transaction */139err = regmap_update_bits(map_dptx, SP_DP_AUX_CH_CTRL2_REG,140SP_ADDR_ONLY | SP_AUX_EN, ctrl2);141if (err)142return err;143144err = anx_dp_aux_wait(map_dptx);145if (err)146return err;147148msg->reply = DP_AUX_I2C_REPLY_ACK;149150if ((msg->size > 0) && (msg->request & DP_AUX_I2C_READ)) {151/* Read values from data buffer */152err = regmap_bulk_read(map_dptx,153SP_DP_BUF_DATA0_REG, buffer,154msg->size);155if (err)156return err;157}158159err = anx_i2c_dp_clear_bits(map_dptx, SP_DP_AUX_CH_CTRL2_REG,160SP_ADDR_ONLY);161if (err)162return err;163164return msg->size;165}166EXPORT_SYMBOL_GPL(anx_dp_aux_transfer);167168169