diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index 7f2215aefe7d..ad93891b52cb 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -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; diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h index ebc9b51e39ad..bd06f5f3cc4c 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h @@ -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 */ diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c index aa2ba8d08d91..672b8bb0b79b 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c @@ -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 | diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h index 7658ff853dcc..0a368b1722ec 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h @@ -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)