mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 02:50:49 +09:00
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:
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user