MLK-24491: drm: bridge: cdns: Add support of i2c-over-aux

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>
(cherry picked from commit b6181a1aea9ade244efb2ca001e14adb5cbe23eb)
Signed-off-by: Andrey Zhizhikin <andrey.zhizhikin@leica-geosystems.com>
This commit is contained in:
Julien Jayat 2020-08-11 14:09:32 +02:00 committed by Andrey Zhizhikin
parent fbff4c5cf8
commit 422e20fe90
3 changed files with 204 additions and 11 deletions

View File

@ -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;
}
@ -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;
}

View File

@ -55,6 +55,41 @@ err_dpcd_read:
}
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];
@ -88,6 +123,75 @@ err_dpcd_write:
}
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;

View File

@ -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
@ -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);