Skip to content

Commit

Permalink
MLK-24491: drm: bridge: cdns: Add support of i2c-over-aux
Browse files Browse the repository at this point in the history
Port the i2c over aux feature from 4.19.35 to the 5.4.x kernel. Add the
the i2c read/write functions. The i2c features in the FW have been introduced in
version 1.0.62.

Signed-off-by: Julien Jayat <julien.jayat@nxp.com>
Signed-off-by: Oliver Brown <oliver.brown@nxp.com>
  • Loading branch information
JulienJayat authored and nxpobrown committed Mar 29, 2021
1 parent 268df94 commit b6181a1
Show file tree
Hide file tree
Showing 3 changed files with 204 additions and 11 deletions.
100 changes: 89 additions & 11 deletions drivers/gpu/drm/bridge/cadence/cdns-dp-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,16 @@ static ssize_t dp_aux_transfer(struct drm_dp_aux *aux,
bool native = msg->request & (DP_AUX_NATIVE_WRITE & DP_AUX_NATIVE_READ);
int ret;

/* Ignore address only message */
if ((msg->size == 0) || (msg->buffer == NULL)) {
msg->reply = native ?
DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
/* Ignore address only message , for native */
if ((native == true) && ((msg->size == 0) || (msg->buffer == NULL))) {
msg->reply = DP_AUX_NATIVE_REPLY_ACK;
return msg->size;
}

if (!native) {
dev_err(mhdp->dev, "%s: only native messages supported\n", __func__);
return -EINVAL;
}

/* msg sanity check */
if (msg->size > DP_AUX_MAX_PAYLOAD_BYTES) {
dev_err(mhdp->dev, "%s: invalid msg: size(%zu), request(%x)\n",
__func__, msg->size, (unsigned int)msg->request);
__func__, msg->size, (unsigned int)msg->request);
return -EINVAL;
}

Expand All @@ -72,12 +66,96 @@ static ssize_t dp_aux_transfer(struct drm_dp_aux *aux,
}

if (msg->request == DP_AUX_NATIVE_READ) {
ret = cdns_mhdp_dpcd_read(mhdp, msg->address, msg->buffer, msg->size);
ret = cdns_mhdp_dpcd_read(mhdp, msg->address, msg->buffer,
msg->size);
if (ret < 0)
return -EIO;
msg->reply = DP_AUX_NATIVE_REPLY_ACK;
return msg->size;
}

if (((msg->request & ~DP_AUX_I2C_MOT) == DP_AUX_I2C_WRITE)
|| ((msg->request & ~DP_AUX_I2C_MOT) ==
DP_AUX_I2C_WRITE_STATUS_UPDATE)) {

u8 i2c_status = 0u;
u16 respSize = 0u;

ret = cdns_mhdp_i2c_write(mhdp, msg->address,
msg->buffer,
!!(msg->request & DP_AUX_I2C_MOT),
msg->size, &respSize);

if (ret < 0) {
dev_err(aux->dev, "cdns_mhdp_i2c_write status %d\n",
ret);
return -EIO;
}

ret = cdns_mhdp_get_last_i2c_status(mhdp, &i2c_status);
if (ret < 0) {
dev_err(aux->dev,
"cdns_mhdp_get_last_i2c_status status %d\n",
ret);
return -EIO;
}

switch (i2c_status) {
case 0u:
msg->reply = DP_AUX_I2C_REPLY_ACK;
break;
case 1u:
msg->reply = DP_AUX_I2C_REPLY_NACK;
break;
case 2u:
msg->reply = DP_AUX_I2C_REPLY_DEFER;
break;
default:
msg->reply = DP_AUX_I2C_REPLY_NACK;
break;
}

return respSize;
}

if ((msg->request & ~DP_AUX_I2C_MOT) == DP_AUX_I2C_READ) {

u8 i2c_status = 0u;
u16 respSize = 0u;

ret = cdns_mhdp_i2c_read(mhdp, msg->address, msg->buffer,
msg->size,
!!(msg->request & DP_AUX_I2C_MOT),
&respSize);
if (ret < 0)
return -EIO;

ret = cdns_mhdp_get_last_i2c_status(mhdp, &i2c_status);

if (ret < 0) {
dev_err(aux->dev,
"cdns_mhdp_get_last_i2c_status ret %d\n", ret);
return -EIO;
}

switch (i2c_status) {
case 0u:
msg->reply = DP_AUX_I2C_REPLY_ACK;
break;
case 1u:
msg->reply = DP_AUX_I2C_REPLY_NACK;
break;
case 2u:
msg->reply = DP_AUX_I2C_REPLY_DEFER;
break;
default:
msg->reply = DP_AUX_I2C_REPLY_NACK;
break;
}

return respSize;
}

return 0;
}

Expand Down
104 changes: 104 additions & 0 deletions drivers/gpu/drm/bridge/cadence/cdns-mhdp-dp.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,41 @@ int cdns_mhdp_dpcd_read(struct cdns_mhdp_device *mhdp,
}
EXPORT_SYMBOL(cdns_mhdp_dpcd_read);

int cdns_mhdp_i2c_read(struct cdns_mhdp_device *mhdp, u8 addr, u8 *data,
u16 len, u8 mot, u16 *respLength)
{
u8 msg[5], reg[3];
int ret;

put_unaligned_be16(len, msg);
msg[2] = addr;
msg[3] = mot;

ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX,
DPTX_I2C_READ, sizeof(msg), msg);
if (ret)
goto err_i2c_read;

ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_DP_TX,
DPTX_I2C_READ,
sizeof(reg) + len);
if (ret)
goto err_i2c_read;

ret = cdns_mhdp_mailbox_read_receive(mhdp, reg, sizeof(reg));
if (ret)
goto err_i2c_read;

ret = cdns_mhdp_mailbox_read_receive(mhdp, data, len);
*respLength = (reg[0] << 8u) + reg[1];

err_i2c_read:
if (ret)
DRM_DEV_ERROR(mhdp->dev, "i2c read failed: %d\n", ret);
return ret;
}
EXPORT_SYMBOL(cdns_mhdp_i2c_read);

int cdns_mhdp_dpcd_write(struct cdns_mhdp_device *mhdp, u32 addr, u8 value)
{
u8 msg[6], reg[5];
Expand Down Expand Up @@ -88,6 +123,75 @@ int cdns_mhdp_dpcd_write(struct cdns_mhdp_device *mhdp, u32 addr, u8 value)
}
EXPORT_SYMBOL(cdns_mhdp_dpcd_write);

int cdns_mhdp_i2c_write(struct cdns_mhdp_device *mhdp, u8 addr, u8 *value,
u8 mot, u16 len, u16 *respLength)
{
u8 msg[4+DP_AUX_MAX_PAYLOAD_BYTES], reg[3];
int ret;

put_unaligned_be16(len, msg);
msg[2] = addr;
msg[3] = mot;
memcpy(&msg[4], value, len);


ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX,
DPTX_I2C_WRITE, sizeof(msg), msg);
if (ret)
goto err_i2c_write;

ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_DP_TX,
DPTX_I2C_WRITE, sizeof(reg));
if (ret)
goto err_i2c_write;

ret = cdns_mhdp_mailbox_read_receive(mhdp, reg, sizeof(reg));
if (ret)
goto err_i2c_write;

if (addr != reg[2])
ret = -EINVAL;

*respLength = (reg[0]<<8u) + reg[1];

err_i2c_write:
if (ret)
DRM_DEV_ERROR(mhdp->dev, "i2c write failed: %d\n", ret);
return ret;
}
EXPORT_SYMBOL(cdns_mhdp_i2c_write);


int cdns_mhdp_get_last_i2c_status(struct cdns_mhdp_device *mhdp, u8 *resp)
{
u8 status[1];
int ret;

ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX,
DPTX_GET_LAST_I2C_STATUS, 0, NULL);
if (ret)
goto err_get_i2c_status;

ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_DP_TX,
DPTX_GET_LAST_I2C_STATUS,
sizeof(status));
if (ret)
goto err_get_i2c_status;

ret = cdns_mhdp_mailbox_read_receive(mhdp, status, sizeof(status));
if (ret)
goto err_get_i2c_status;

*resp = status[0];

err_get_i2c_status:
if (ret)
DRM_DEV_ERROR(mhdp->dev, "get i2c status failed: %d\n",
ret);
return ret;
}
EXPORT_SYMBOL(cdns_mhdp_get_last_i2c_status);

static int cdns_mhdp_training_start(struct cdns_mhdp_device *mhdp)
{
unsigned long timeout;
Expand Down
11 changes: 11 additions & 0 deletions include/drm/bridge/cdns-mhdp.h
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,11 @@
#define DPTX_FORCE_LANES 0x10
#define DPTX_HPD_STATE 0x11
#define DPTX_ADJUST_LT 0x12
#define DPTX_I2C_READ 0x15
#define DPTX_I2C_WRITE 0x16
#define DPTX_GET_LAST_I2C_STATUS 0x17



/* HDMI TX opcode */
#define HDMI_TX_READ 0x00
Expand Down Expand Up @@ -734,6 +739,12 @@ u32 cdns_mhdp_get_event(struct cdns_mhdp_device *mhdp);
int cdns_mhdp_dpcd_write(struct cdns_mhdp_device *mhdp, u32 addr, u8 value);
int cdns_mhdp_dpcd_read(struct cdns_mhdp_device *mhdp,
u32 addr, u8 *data, u16 len);

int cdns_mhdp_get_last_i2c_status(struct cdns_mhdp_device *mhdp, u8 *resp);
int cdns_mhdp_i2c_write(struct cdns_mhdp_device *mhdp, u8 addr,
u8 *value, u8 mot, u16 len, u16 *respLength);
int cdns_mhdp_i2c_read(struct cdns_mhdp_device *mhdp, u8 addr, u8 *data,
u16 len, u8 mot, u16 *respLength);
int cdns_mhdp_get_edid_block(void *mhdp, u8 *edid,
unsigned int block, size_t length);
int cdns_mhdp_train_link(struct cdns_mhdp_device *mhdp);
Expand Down

0 comments on commit b6181a1

Please sign in to comment.