drm/bridge: analogix_dp: Add DP Test Automation

Test on RK3588 with Unigraf DPR-100 DP reference sink.

Signed-off-by: Wyon Bi <bivvy.bi@rock-chips.com>
Change-Id: I01c099e1ad25ab92d9e8efef13c0c2304115a957
This commit is contained in:
Wyon Bi
2021-03-22 19:17:44 +08:00
parent 79f32b338b
commit 304dbb104d
4 changed files with 196 additions and 2 deletions

View File

@@ -917,6 +917,156 @@ static int analogix_dp_enable_scramble(struct analogix_dp_device *dp,
return ret < 0 ? ret : 0;
}
static u8 analogix_dp_autotest_phy_pattern(struct analogix_dp_device *dp)
{
struct drm_dp_phy_test_params *data = &dp->compliance.phytest;
if (drm_dp_get_phy_test_pattern(&dp->aux, data)) {
dev_err(dp->dev, "DP Phy Test pattern AUX read failure\n");
return DP_TEST_NAK;
}
if (data->link_rate > drm_dp_bw_code_to_link_rate(dp->video_info.max_link_rate)) {
dev_err(dp->dev, "invalid link rate = 0x%x\n", data->link_rate);
return DP_TEST_NAK;
}
/* Set test active flag here so userspace doesn't interrupt things */
dp->compliance.test_active = true;
return DP_TEST_ACK;
}
static void analogix_dp_handle_test_request(struct analogix_dp_device *dp)
{
u8 response = DP_TEST_NAK;
u8 request = 0;
int ret;
ret = drm_dp_dpcd_readb(&dp->aux, DP_TEST_REQUEST, &request);
if (ret < 0) {
dev_err(dp->dev, "Could not read test request from sink\n");
goto update_status;
}
switch (request) {
case DP_TEST_LINK_PHY_TEST_PATTERN:
dev_info(dp->dev, "PHY_PATTERN test requested\n");
response = analogix_dp_autotest_phy_pattern(dp);
break;
default:
dev_err(dp->dev, "Invalid test request '%02x'\n", request);
break;
}
if (response & DP_TEST_ACK)
dp->compliance.test_type = request;
update_status:
ret = drm_dp_dpcd_writeb(&dp->aux, DP_TEST_RESPONSE, response);
if (ret < 0)
dev_err(dp->dev, "Could not write test response to sink\n");
}
void analogix_dp_check_device_service_irq(struct analogix_dp_device *dp)
{
u8 val;
int ret;
ret = drm_dp_dpcd_readb(&dp->aux, DP_DEVICE_SERVICE_IRQ_VECTOR, &val);
if (ret < 0 || !val)
return;
ret = drm_dp_dpcd_writeb(&dp->aux, DP_DEVICE_SERVICE_IRQ_VECTOR, val);
if (ret < 0)
return;
if (val & DP_AUTOMATED_TEST_REQUEST)
analogix_dp_handle_test_request(dp);
}
EXPORT_SYMBOL_GPL(analogix_dp_check_device_service_irq);
static void analogix_dp_process_phy_request(struct analogix_dp_device *dp)
{
struct drm_dp_phy_test_params *data = &dp->compliance.phytest;
u8 spread, adjust_request[2];
int ret;
dp->link_train.link_rate = drm_dp_link_rate_to_bw_code(data->link_rate);
dp->link_train.lane_count = data->num_lanes;
ret = drm_dp_dpcd_readb(&dp->aux, DP_MAX_DOWNSPREAD, &spread);
if (ret < 0) {
dev_err(dp->dev, "Could not read ssc from sink\n");
return;
}
dp->link_train.ssc = !!(spread & DP_MAX_DOWNSPREAD_0_5);
ret = drm_dp_dpcd_read(&dp->aux, DP_ADJUST_REQUEST_LANE0_1,
adjust_request, 2);
if (ret < 0) {
dev_err(dp->dev, "Could not read swing/pre-emphasis\n");
return;
}
analogix_dp_set_link_bandwidth(dp, dp->link_train.link_rate);
analogix_dp_set_lane_count(dp, dp->link_train.lane_count);
analogix_dp_get_adjust_training_lane(dp, adjust_request);
analogix_dp_set_lane_link_training(dp);
switch (data->phy_pattern) {
case DP_PHY_TEST_PATTERN_NONE:
dev_info(dp->dev, "Disable Phy Test Pattern\n");
analogix_dp_set_training_pattern(dp, DP_NONE);
break;
case DP_PHY_TEST_PATTERN_D10_2:
dev_info(dp->dev, "Set D10.2 Phy Test Pattern\n");
analogix_dp_set_training_pattern(dp, D10_2);
break;
case DP_PHY_TEST_PATTERN_PRBS7:
dev_info(dp->dev, "Set PRBS7 Phy Test Pattern\n");
analogix_dp_set_training_pattern(dp, PRBS7);
break;
case DP_PHY_TEST_PATTERN_80BIT_CUSTOM:
dev_info(dp->dev, "Set 80Bit Custom Phy Test Pattern\n");
analogix_dp_set_training_pattern(dp, TEST_PATTREN_80BIT);
break;
case DP_PHY_TEST_PATTERN_CP2520:
dev_info(dp->dev, "Set HBR2 compliance Phy Test Pattern\n");
analogix_dp_set_training_pattern(dp, TEST_PATTREN_HBR2);
break;
default:
dev_err(dp->dev, "Invalid Phy Test Pattern: %d\n", data->phy_pattern);
return;
}
drm_dp_set_phy_test_pattern(&dp->aux, data, 0x11);
}
void analogix_dp_phy_test(struct analogix_dp_device *dp)
{
struct drm_device *dev = dp->drm_dev;
struct drm_modeset_acquire_ctx ctx;
int ret;
DRM_DEV_INFO(dp->dev, "PHY test\n");
drm_modeset_acquire_init(&ctx, 0);
for (;;) {
ret = drm_modeset_lock(&dev->mode_config.connection_mutex, &ctx);
if (ret != -EDEADLK)
break;
drm_modeset_backoff(&ctx);
}
analogix_dp_process_phy_request(dp);
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
}
EXPORT_SYMBOL_GPL(analogix_dp_phy_test);
static irqreturn_t analogix_dp_hpd_irq_handler(int irq, void *arg)
{
struct analogix_dp_device *dp = arg;

View File

@@ -71,6 +71,8 @@ enum pattern_set {
TRAINING_PTN1,
TRAINING_PTN2,
TRAINING_PTN3,
TEST_PATTREN_80BIT,
TEST_PATTREN_HBR2,
DP_NONE
};
@@ -155,6 +157,14 @@ struct link_train {
enum link_training_state lt_state;
};
struct analogix_dp_compliance {
struct drm_dp_phy_test_params phytest;
int test_link_rate;
u8 test_lane_count;
unsigned long test_type;
bool test_active;
};
struct analogix_dp_device {
struct drm_encoder *encoder;
struct device *dev;
@@ -183,6 +193,7 @@ struct analogix_dp_device {
u8 dpcd[DP_RECEIVER_CAP_SIZE];
struct analogix_dp_plat_data *plat_data;
struct extcon_dev *extcon;
struct analogix_dp_compliance compliance;
};
/* analogix_dp_reg.c */
@@ -252,5 +263,7 @@ void analogix_dp_audio_enable(struct analogix_dp_device *dp);
void analogix_dp_audio_disable(struct analogix_dp_device *dp);
void analogix_dp_init(struct analogix_dp_device *dp);
void analogix_dp_irq_handler(struct analogix_dp_device *dp);
void analogix_dp_phy_test(struct analogix_dp_device *dp);
void analogix_dp_check_device_service_irq(struct analogix_dp_device *dp);
#endif /* _ANALOGIX_DP_CORE_H */

View File

@@ -427,8 +427,17 @@ static void analogix_dp_handle_hpd_event(struct analogix_dp_device *dp)
reg = analogix_dp_read(dp, ANALOGIX_DP_INT_STA);
if (reg & INT_HPD) {
dev_info(dp->dev, "irq-hpd, it's being ignored for now\n");
analogix_dp_write(dp, ANALOGIX_DP_INT_STA, INT_HPD);
memset(&dp->compliance, 0, sizeof(dp->compliance));
analogix_dp_check_device_service_irq(dp);
if (dp->compliance.test_active &&
dp->compliance.test_type == DP_TEST_LINK_PHY_TEST_PATTERN) {
analogix_dp_phy_test(dp);
return;
}
}
reg = analogix_dp_read(dp, ANALOGIX_DP_COMMON_INT_STA_4);
@@ -726,6 +735,22 @@ void analogix_dp_set_training_pattern(struct analogix_dp_device *dp,
reg = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN3;
analogix_dp_write(dp, ANALOGIX_DP_TRAINING_PTN_SET, reg);
break;
case TEST_PATTREN_80BIT:
reg = 0x3e0f83e0;
analogix_dp_write(dp, ANALOGIX_DP_TEST_80B_PATTERN0, reg);
reg = 0x0f83e0f8;
analogix_dp_write(dp, ANALOGIX_DP_TEST_80B_PATTERN1, reg);
reg = 0x0000f83e;
analogix_dp_write(dp, ANALOGIX_DP_TEST_80B_PATTERN2, reg);
reg = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_80BIT;
analogix_dp_write(dp, ANALOGIX_DP_TRAINING_PTN_SET, reg);
break;
case TEST_PATTREN_HBR2:
reg = 0xfb;
analogix_dp_write(dp, ANALOGIX_DP_TEST_HBR2_PATTERN, reg);
reg = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_HBR2;
analogix_dp_write(dp, ANALOGIX_DP_TRAINING_PTN_SET, reg);
break;
case DP_NONE:
reg = SCRAMBLING_ENABLE |
LINK_QUAL_PATTERN_SET_DISABLE |

View File

@@ -138,6 +138,10 @@
#define ANALOGIX_DP_BUF_DATA_0 0x7C0
#define ANALOGIX_DP_SOC_GENERAL_CTL 0x800
#define ANALOGIX_DP_TEST_80B_PATTERN0 0x81C
#define ANALOGIX_DP_TEST_80B_PATTERN1 0x820
#define ANALOGIX_DP_TEST_80B_PATTERN2 0x824
#define ANALOGIX_DP_TEST_HBR2_PATTERN 0x828
#define ANALOGIX_DP_AUD_CHANNEL_CTL 0x834
#define ANALOGIX_DP_CRC_CON 0x890
#define ANALOGIX_DP_I2S_CTRL 0x9C8
@@ -400,7 +404,9 @@
#define HW_LINK_TRAINING_PATTERN (0x1 << 8)
#define SCRAMBLING_DISABLE (0x1 << 5)
#define SCRAMBLING_ENABLE (0x0 << 5)
#define LINK_QUAL_PATTERN_SET_MASK (0x3 << 2)
#define LINK_QUAL_PATTERN_SET_MASK (0x7 << 2)
#define LINK_QUAL_PATTERN_SET_HBR2 (0x5 << 2)
#define LINK_QUAL_PATTERN_SET_80BIT (0x4 << 2)
#define LINK_QUAL_PATTERN_SET_PRBS7 (0x3 << 2)
#define LINK_QUAL_PATTERN_SET_D10_2 (0x1 << 2)
#define LINK_QUAL_PATTERN_SET_DISABLE (0x0 << 2)