Merge tag 'topic/drm-dp-training-delay-helpers-2021-10-19' of git://anongit.freedesktop.org/drm/drm-intel into drm-intel-next

Core Changes:
- drm dp helpers for figuring out link training delays

Merge to drm-intel-next as well after c93ce6a6df ("Merge tag
'topic/drm-dp-training-delay-helpers-2021-10-19' of
git://anongit.freedesktop.org/drm/drm-intel into drm-misc-next").

Signed-off-by: Jani Nikula <jani.nikula@intel.com>
From: Jani Nikula <jani.nikula@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/878ryps5b6.fsf@intel.com
This commit is contained in:
Jani Nikula 2021-10-19 18:15:27 +03:00
commit babc8db301
2 changed files with 156 additions and 22 deletions

View File

@ -154,38 +154,155 @@ u8 drm_dp_get_adjust_request_post_cursor(const u8 link_status[DP_LINK_STATUS_SIZ
}
EXPORT_SYMBOL(drm_dp_get_adjust_request_post_cursor);
static int __8b10b_clock_recovery_delay_us(const struct drm_dp_aux *aux, u8 rd_interval)
{
if (rd_interval > 4)
drm_dbg_kms(aux->drm_dev, "%s: invalid AUX interval 0x%02x (max 4)\n",
aux->name, rd_interval);
if (rd_interval == 0)
return 100;
return rd_interval * 4 * USEC_PER_MSEC;
}
static int __8b10b_channel_eq_delay_us(const struct drm_dp_aux *aux, u8 rd_interval)
{
if (rd_interval > 4)
drm_dbg_kms(aux->drm_dev, "%s: invalid AUX interval 0x%02x (max 4)\n",
aux->name, rd_interval);
if (rd_interval == 0)
return 400;
return rd_interval * 4 * USEC_PER_MSEC;
}
static int __128b132b_channel_eq_delay_us(const struct drm_dp_aux *aux, u8 rd_interval)
{
switch (rd_interval) {
default:
drm_dbg_kms(aux->drm_dev, "%s: invalid AUX interval 0x%02x\n",
aux->name, rd_interval);
fallthrough;
case DP_128B132B_TRAINING_AUX_RD_INTERVAL_400_US:
return 400;
case DP_128B132B_TRAINING_AUX_RD_INTERVAL_4_MS:
return 4000;
case DP_128B132B_TRAINING_AUX_RD_INTERVAL_8_MS:
return 8000;
case DP_128B132B_TRAINING_AUX_RD_INTERVAL_12_MS:
return 12000;
case DP_128B132B_TRAINING_AUX_RD_INTERVAL_16_MS:
return 16000;
case DP_128B132B_TRAINING_AUX_RD_INTERVAL_32_MS:
return 32000;
case DP_128B132B_TRAINING_AUX_RD_INTERVAL_64_MS:
return 64000;
}
}
/*
* The link training delays are different for:
*
* - Clock recovery vs. channel equalization
* - DPRX vs. LTTPR
* - 128b/132b vs. 8b/10b
* - DPCD rev 1.3 vs. later
*
* Get the correct delay in us, reading DPCD if necessary.
*/
static int __read_delay(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE],
enum drm_dp_phy dp_phy, bool uhbr, bool cr)
{
int (*parse)(const struct drm_dp_aux *aux, u8 rd_interval);
unsigned int offset;
u8 rd_interval, mask;
if (dp_phy == DP_PHY_DPRX) {
if (uhbr) {
if (cr)
return 100;
offset = DP_128B132B_TRAINING_AUX_RD_INTERVAL;
mask = DP_128B132B_TRAINING_AUX_RD_INTERVAL_MASK;
parse = __128b132b_channel_eq_delay_us;
} else {
if (cr && dpcd[DP_DPCD_REV] >= DP_DPCD_REV_14)
return 100;
offset = DP_TRAINING_AUX_RD_INTERVAL;
mask = DP_TRAINING_AUX_RD_MASK;
if (cr)
parse = __8b10b_clock_recovery_delay_us;
else
parse = __8b10b_channel_eq_delay_us;
}
} else {
if (uhbr) {
offset = DP_128B132B_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER(dp_phy);
mask = DP_128B132B_TRAINING_AUX_RD_INTERVAL_MASK;
parse = __128b132b_channel_eq_delay_us;
} else {
if (cr)
return 100;
offset = DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER(dp_phy);
mask = DP_TRAINING_AUX_RD_MASK;
parse = __8b10b_channel_eq_delay_us;
}
}
if (offset < DP_RECEIVER_CAP_SIZE) {
rd_interval = dpcd[offset];
} else {
if (drm_dp_dpcd_readb(aux, offset, &rd_interval) != 1) {
drm_dbg_kms(aux->drm_dev, "%s: failed rd interval read\n",
aux->name);
/* arbitrary default delay */
return 400;
}
}
return parse(aux, rd_interval & mask);
}
int drm_dp_read_clock_recovery_delay(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE],
enum drm_dp_phy dp_phy, bool uhbr)
{
return __read_delay(aux, dpcd, dp_phy, uhbr, true);
}
EXPORT_SYMBOL(drm_dp_read_clock_recovery_delay);
int drm_dp_read_channel_eq_delay(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE],
enum drm_dp_phy dp_phy, bool uhbr)
{
return __read_delay(aux, dpcd, dp_phy, uhbr, false);
}
EXPORT_SYMBOL(drm_dp_read_channel_eq_delay);
void drm_dp_link_train_clock_recovery_delay(const struct drm_dp_aux *aux,
const u8 dpcd[DP_RECEIVER_CAP_SIZE])
{
unsigned long rd_interval = dpcd[DP_TRAINING_AUX_RD_INTERVAL] &
DP_TRAINING_AUX_RD_MASK;
u8 rd_interval = dpcd[DP_TRAINING_AUX_RD_INTERVAL] &
DP_TRAINING_AUX_RD_MASK;
int delay_us;
if (rd_interval > 4)
drm_dbg_kms(aux->drm_dev, "%s: AUX interval %lu, out of range (max 4)\n",
aux->name, rd_interval);
if (rd_interval == 0 || dpcd[DP_DPCD_REV] >= DP_DPCD_REV_14)
rd_interval = 100;
if (dpcd[DP_DPCD_REV] >= DP_DPCD_REV_14)
delay_us = 100;
else
rd_interval *= 4 * USEC_PER_MSEC;
delay_us = __8b10b_clock_recovery_delay_us(aux, rd_interval);
usleep_range(rd_interval, rd_interval * 2);
usleep_range(delay_us, delay_us * 2);
}
EXPORT_SYMBOL(drm_dp_link_train_clock_recovery_delay);
static void __drm_dp_link_train_channel_eq_delay(const struct drm_dp_aux *aux,
unsigned long rd_interval)
u8 rd_interval)
{
if (rd_interval > 4)
drm_dbg_kms(aux->drm_dev, "%s: AUX interval %lu, out of range (max 4)\n",
aux->name, rd_interval);
int delay_us = __8b10b_channel_eq_delay_us(aux, rd_interval);
if (rd_interval == 0)
rd_interval = 400;
else
rd_interval *= 4 * USEC_PER_MSEC;
usleep_range(rd_interval, rd_interval * 2);
usleep_range(delay_us, delay_us * 2);
}
void drm_dp_link_train_channel_eq_delay(const struct drm_dp_aux *aux,

View File

@ -1114,8 +1114,15 @@ struct drm_panel;
# define DP_UHBR20 (1 << 1)
# define DP_UHBR13_5 (1 << 2)
#define DP_128B132B_TRAINING_AUX_RD_INTERVAL 0x2216 /* 2.0 */
# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_MASK 0x7f
#define DP_128B132B_TRAINING_AUX_RD_INTERVAL 0x2216 /* 2.0 */
# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_MASK 0x7f
# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_400_US 0x00
# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_4_MS 0x01
# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_8_MS 0x02
# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_12_MS 0x03
# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_16_MS 0x04
# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_32_MS 0x05
# define DP_128B132B_TRAINING_AUX_RD_INTERVAL_64_MS 0x06
#define DP_TEST_264BIT_CUSTOM_PATTERN_7_0 0x2230
#define DP_TEST_264BIT_CUSTOM_PATTERN_263_256 0x2250
@ -1389,6 +1396,11 @@ enum drm_dp_phy {
# define DP_VOLTAGE_SWING_LEVEL_3_SUPPORTED BIT(0)
# define DP_PRE_EMPHASIS_LEVEL_3_SUPPORTED BIT(1)
#define DP_128B132B_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1 0xf0022 /* 2.0 */
#define DP_128B132B_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER(dp_phy) \
DP_LTTPR_REG(dp_phy, DP_128B132B_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER1)
/* see DP_128B132B_TRAINING_AUX_RD_INTERVAL for values */
#define DP_LANE0_1_STATUS_PHY_REPEATER1 0xf0030 /* 1.3 */
#define DP_LANE0_1_STATUS_PHY_REPEATER(dp_phy) \
DP_LTTPR_REG(dp_phy, DP_LANE0_1_STATUS_PHY_REPEATER1)
@ -1527,6 +1539,11 @@ u8 drm_dp_get_adjust_request_post_cursor(const u8 link_status[DP_LINK_STATUS_SIZ
#define DP_LTTPR_COMMON_CAP_SIZE 8
#define DP_LTTPR_PHY_CAP_SIZE 3
int drm_dp_read_clock_recovery_delay(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE],
enum drm_dp_phy dp_phy, bool uhbr);
int drm_dp_read_channel_eq_delay(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE],
enum drm_dp_phy dp_phy, bool uhbr);
void drm_dp_link_train_clock_recovery_delay(const struct drm_dp_aux *aux,
const u8 dpcd[DP_RECEIVER_CAP_SIZE]);
void drm_dp_lttpr_link_train_clock_recovery_delay(void);