mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 02:50:49 +09:00
FROMLIST: drm/rockchip: add transfer function for cdn-dp
We may support training outside firmware, so we need support dpcd read/write to get the message or do some setting with display. Change-Id: If89911e6205546df1a5ae8997ea214d5d2a60af6 Signed-off-by: Chris Zhong <zyw@rock-chips.com> Signed-off-by: Lin Huang <hl@rock-chips.com> Reviewed-by: Sean Paul <seanpaul@chromium.org> Reviewed-by: Enric Balletbo <enric.balletbo@collabora.com> Signed-off-by: Wyon Bi <bivvy.bi@rock-chips.com> (am from https://patchwork.kernel.org/patch/10420461/) Signed-off-by: Zhang Yubing <yubing.zhang@rock-chips.com>
This commit is contained in:
@@ -151,8 +151,8 @@ static int cdn_dp_get_sink_count(struct cdn_dp_device *dp, u8 *sink_count)
|
|||||||
u8 value;
|
u8 value;
|
||||||
|
|
||||||
*sink_count = 0;
|
*sink_count = 0;
|
||||||
ret = cdn_dp_dpcd_read(dp, DP_SINK_COUNT, &value, 1);
|
ret = drm_dp_dpcd_read(&dp->aux, DP_SINK_COUNT, &value, 1);
|
||||||
if (ret)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
*sink_count = DP_GET_SINK_COUNT(value);
|
*sink_count = DP_GET_SINK_COUNT(value);
|
||||||
@@ -351,9 +351,9 @@ static int cdn_dp_get_sink_capability(struct cdn_dp_device *dp)
|
|||||||
if (!cdn_dp_check_sink_connection(dp))
|
if (!cdn_dp_check_sink_connection(dp))
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
ret = cdn_dp_dpcd_read(dp, DP_DPCD_REV, dp->dpcd,
|
ret = drm_dp_dpcd_read(&dp->aux, DP_DPCD_REV, dp->dpcd,
|
||||||
DP_RECEIVER_CAP_SIZE);
|
sizeof(dp->dpcd));
|
||||||
if (ret) {
|
if (ret < 0) {
|
||||||
DRM_DEV_ERROR(dp->dev, "Failed to get caps %d\n", ret);
|
DRM_DEV_ERROR(dp->dev, "Failed to get caps %d\n", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -551,8 +551,8 @@ static bool cdn_dp_check_link_status(struct cdn_dp_device *dp)
|
|||||||
if (!port || !dp->max_rate || !dp->max_lanes)
|
if (!port || !dp->max_rate || !dp->max_lanes)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (cdn_dp_dpcd_read(dp, DP_LANE0_1_STATUS, link_status,
|
if (drm_dp_dpcd_read_link_status(&dp->aux, link_status) !=
|
||||||
DP_LINK_STATUS_SIZE)) {
|
DP_LINK_STATUS_SIZE) {
|
||||||
DRM_ERROR("Failed to get link status\n");
|
DRM_ERROR("Failed to get link status\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -962,6 +962,40 @@ out:
|
|||||||
drm_kms_helper_hotplug_event(dp->drm_dev);
|
drm_kms_helper_hotplug_event(dp->drm_dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ssize_t cdn_dp_aux_transfer(struct drm_dp_aux *aux,
|
||||||
|
struct drm_dp_aux_msg *msg)
|
||||||
|
{
|
||||||
|
struct cdn_dp_device *dp = container_of(aux, struct cdn_dp_device, aux);
|
||||||
|
int ret;
|
||||||
|
u8 status;
|
||||||
|
|
||||||
|
switch (msg->request & ~DP_AUX_I2C_MOT) {
|
||||||
|
case DP_AUX_NATIVE_WRITE:
|
||||||
|
case DP_AUX_I2C_WRITE:
|
||||||
|
case DP_AUX_I2C_WRITE_STATUS_UPDATE:
|
||||||
|
ret = cdn_dp_dpcd_write(dp, msg->address, msg->buffer,
|
||||||
|
msg->size);
|
||||||
|
break;
|
||||||
|
case DP_AUX_NATIVE_READ:
|
||||||
|
case DP_AUX_I2C_READ:
|
||||||
|
ret = cdn_dp_dpcd_read(dp, msg->address, msg->buffer,
|
||||||
|
msg->size);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = cdn_dp_get_aux_status(dp);
|
||||||
|
if (status == AUX_STATUS_ACK)
|
||||||
|
msg->reply = DP_AUX_NATIVE_REPLY_ACK;
|
||||||
|
else if (status == AUX_STATUS_NACK)
|
||||||
|
msg->reply = DP_AUX_NATIVE_REPLY_NACK;
|
||||||
|
else if (status == AUX_STATUS_DEFER)
|
||||||
|
msg->reply = DP_AUX_NATIVE_REPLY_DEFER;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
|
static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
|
||||||
{
|
{
|
||||||
struct cdn_dp_device *dp = dev_get_drvdata(dev);
|
struct cdn_dp_device *dp = dev_get_drvdata(dev);
|
||||||
@@ -979,6 +1013,13 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
|
|||||||
dp->active = false;
|
dp->active = false;
|
||||||
dp->active_port = -1;
|
dp->active_port = -1;
|
||||||
dp->fw_loaded = false;
|
dp->fw_loaded = false;
|
||||||
|
dp->aux.name = "DP-AUX";
|
||||||
|
dp->aux.transfer = cdn_dp_aux_transfer;
|
||||||
|
dp->aux.dev = dev;
|
||||||
|
|
||||||
|
ret = drm_dp_aux_register(&dp->aux);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
INIT_DELAYED_WORK(&dp->event_work, cdn_dp_pd_event_work);
|
INIT_DELAYED_WORK(&dp->event_work, cdn_dp_pd_event_work);
|
||||||
|
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ struct cdn_dp_device {
|
|||||||
struct platform_device *audio_pdev;
|
struct platform_device *audio_pdev;
|
||||||
struct delayed_work event_work;
|
struct delayed_work event_work;
|
||||||
struct edid *edid;
|
struct edid *edid;
|
||||||
|
struct drm_dp_aux aux;
|
||||||
struct rockchip_drm_sub_dev sub_dev;
|
struct rockchip_drm_sub_dev sub_dev;
|
||||||
|
|
||||||
struct mutex lock;
|
struct mutex lock;
|
||||||
|
|||||||
@@ -213,7 +213,12 @@ static int cdn_dp_reg_write_bit(struct cdn_dp_device *dp, u16 addr,
|
|||||||
sizeof(field), field);
|
sizeof(field), field);
|
||||||
}
|
}
|
||||||
|
|
||||||
int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
|
/*
|
||||||
|
* Returns the number of bytes transferred on success, or a negative
|
||||||
|
* error code on failure. -ETIMEDOUT is returned if mailbox message was
|
||||||
|
* not send successfully;
|
||||||
|
*/
|
||||||
|
ssize_t cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
|
||||||
{
|
{
|
||||||
u8 msg[5], reg[5];
|
u8 msg[5], reg[5];
|
||||||
int ret;
|
int ret;
|
||||||
@@ -239,24 +244,41 @@ int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
|
|||||||
goto err_dpcd_read;
|
goto err_dpcd_read;
|
||||||
|
|
||||||
ret = cdn_dp_mailbox_read_receive(dp, data, len);
|
ret = cdn_dp_mailbox_read_receive(dp, data, len);
|
||||||
|
if (!ret)
|
||||||
|
return len;
|
||||||
|
|
||||||
err_dpcd_read:
|
err_dpcd_read:
|
||||||
|
DRM_DEV_ERROR(dp->dev, "dpcd read failed: %d\n", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
|
#define CDN_AUX_HEADER_SIZE 5
|
||||||
|
#define CDN_AUX_MSG_SIZE 20
|
||||||
|
/*
|
||||||
|
* Returns the number of bytes transferred on success, or a negative error
|
||||||
|
* code on failure. -ETIMEDOUT is returned if mailbox message was not send
|
||||||
|
* success; -EINVAL is returned if get the wrong data size after message
|
||||||
|
* is sent
|
||||||
|
*/
|
||||||
|
ssize_t cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
|
||||||
{
|
{
|
||||||
u8 msg[6], reg[5];
|
u8 msg[CDN_AUX_MSG_SIZE + CDN_AUX_HEADER_SIZE];
|
||||||
|
u8 reg[CDN_AUX_HEADER_SIZE];
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
msg[0] = 0;
|
if (WARN_ON(len > CDN_AUX_MSG_SIZE) || WARN_ON(len <= 0))
|
||||||
msg[1] = 1;
|
return -EINVAL;
|
||||||
|
|
||||||
|
msg[0] = (len >> 8) & 0xff;
|
||||||
|
msg[1] = len & 0xff;
|
||||||
msg[2] = (addr >> 16) & 0xff;
|
msg[2] = (addr >> 16) & 0xff;
|
||||||
msg[3] = (addr >> 8) & 0xff;
|
msg[3] = (addr >> 8) & 0xff;
|
||||||
msg[4] = addr & 0xff;
|
msg[4] = addr & 0xff;
|
||||||
msg[5] = value;
|
|
||||||
|
memcpy(msg + CDN_AUX_HEADER_SIZE, data, len);
|
||||||
|
|
||||||
ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_DPCD,
|
ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_DPCD,
|
||||||
sizeof(msg), msg);
|
CDN_AUX_HEADER_SIZE + len, msg);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_dpcd_write;
|
goto err_dpcd_write;
|
||||||
|
|
||||||
@@ -269,8 +291,12 @@ int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
|
|||||||
if (ret)
|
if (ret)
|
||||||
goto err_dpcd_write;
|
goto err_dpcd_write;
|
||||||
|
|
||||||
if (addr != (reg[2] << 16 | reg[3] << 8 | reg[4]))
|
if ((len != (reg[0] << 8 | reg[1])) ||
|
||||||
|
(addr != (reg[2] << 16 | reg[3] << 8 | reg[4]))) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
|
} else {
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
err_dpcd_write:
|
err_dpcd_write:
|
||||||
if (ret)
|
if (ret)
|
||||||
@@ -278,6 +304,33 @@ err_dpcd_write:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int cdn_dp_get_aux_status(struct cdn_dp_device *dp)
|
||||||
|
{
|
||||||
|
u8 status;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
|
||||||
|
DPTX_GET_LAST_AUX_STAUS, 0, NULL);
|
||||||
|
if (ret)
|
||||||
|
goto err_get_hpd;
|
||||||
|
|
||||||
|
ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
|
||||||
|
DPTX_GET_LAST_AUX_STAUS,
|
||||||
|
sizeof(status));
|
||||||
|
if (ret)
|
||||||
|
goto err_get_hpd;
|
||||||
|
|
||||||
|
ret = cdn_dp_mailbox_read_receive(dp, &status, sizeof(status));
|
||||||
|
if (ret)
|
||||||
|
goto err_get_hpd;
|
||||||
|
|
||||||
|
return status;
|
||||||
|
|
||||||
|
err_get_hpd:
|
||||||
|
DRM_DEV_ERROR(dp->dev, "get aux status failed: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
|
int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
|
||||||
u32 i_size, const u32 *d_mem, u32 d_size)
|
u32 i_size, const u32 *d_mem, u32 d_size)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -320,6 +320,13 @@
|
|||||||
#define GENERAL_BUS_SETTINGS 0x03
|
#define GENERAL_BUS_SETTINGS 0x03
|
||||||
#define GENERAL_TEST_ACCESS 0x04
|
#define GENERAL_TEST_ACCESS 0x04
|
||||||
|
|
||||||
|
/* AUX status*/
|
||||||
|
#define AUX_STATUS_ACK 0
|
||||||
|
#define AUX_STATUS_NACK 1
|
||||||
|
#define AUX_STATUS_DEFER 2
|
||||||
|
#define AUX_STATUS_SINK_ERROR 3
|
||||||
|
#define AUX_STATUS_BUS_ERROR 4
|
||||||
|
|
||||||
#define DPTX_SET_POWER_MNG 0x00
|
#define DPTX_SET_POWER_MNG 0x00
|
||||||
#define DPTX_SET_HOST_CAPABILITIES 0x01
|
#define DPTX_SET_HOST_CAPABILITIES 0x01
|
||||||
#define DPTX_GET_EDID 0x02
|
#define DPTX_GET_EDID 0x02
|
||||||
@@ -461,8 +468,11 @@ int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes, bool flip);
|
|||||||
int cdn_dp_event_config(struct cdn_dp_device *dp);
|
int cdn_dp_event_config(struct cdn_dp_device *dp);
|
||||||
u32 cdn_dp_get_event(struct cdn_dp_device *dp);
|
u32 cdn_dp_get_event(struct cdn_dp_device *dp);
|
||||||
int cdn_dp_get_hpd_status(struct cdn_dp_device *dp);
|
int cdn_dp_get_hpd_status(struct cdn_dp_device *dp);
|
||||||
int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value);
|
ssize_t cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr,
|
||||||
int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len);
|
u8 *data, u16 len);
|
||||||
|
ssize_t cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr,
|
||||||
|
u8 *data, u16 len);
|
||||||
|
int cdn_dp_get_aux_status(struct cdn_dp_device *dp);
|
||||||
int cdn_dp_get_edid_block(void *dp, u8 *edid,
|
int cdn_dp_get_edid_block(void *dp, u8 *edid,
|
||||||
unsigned int block, size_t length);
|
unsigned int block, size_t length);
|
||||||
int cdn_dp_train_link(struct cdn_dp_device *dp);
|
int cdn_dp_train_link(struct cdn_dp_device *dp);
|
||||||
|
|||||||
Reference in New Issue
Block a user