Skip to content

Commit d7164a5

Browse files
ideaktursulin
authored andcommitted
drm/i915/tgl+: Add locking around DKL PHY register accesses
Accessing the TypeC DKL PHY registers during modeset-commit, -verification, DP link-retraining and AUX power well toggling is racy due to these code paths being concurrent and the PHY register bank selection register (HIP_INDEX_REG) being shared between PHY instances (aka TC ports) and the bank selection being not atomic wrt. the actual PHY register access. Add the required locking around each PHY register bank selection-> register access sequence. Kudos to Ville for noticing the race conditions. v2: - Add the DKL PHY register accessors to intel_dkl_phy.[ch]. (Jani) - Make the DKL_REG_TC_PORT macro independent of PHY internals. - Move initing the DKL PHY lock to a more logical place. v3: - Fix parameter reuse in the DKL_REG_TC_PORT definition. - Document the usage of phy_lock. v4: - Fix adding TC_PORT_1 offset in the DKL_REG_TC_PORT definition. Cc: Ville Syrjälä <[email protected]> Cc: Jani Nikula <[email protected]> Cc: <[email protected]> # v5.5+ Acked-by: Jani Nikula <[email protected]> Signed-off-by: Imre Deak <[email protected]> Link: https://patchwork.freedesktop.org/patch/msgid/[email protected] (cherry picked from commit 89cb0ba) Signed-off-by: Tvrtko Ursulin <[email protected]>
1 parent 30a0b95 commit d7164a5

File tree

9 files changed

+204
-76
lines changed

9 files changed

+204
-76
lines changed

drivers/gpu/drm/i915/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,7 @@ i915-y += \
282282
display/intel_ddi.o \
283283
display/intel_ddi_buf_trans.o \
284284
display/intel_display_trace.o \
285+
display/intel_dkl_phy.o \
285286
display/intel_dp.o \
286287
display/intel_dp_aux.o \
287288
display/intel_dp_aux_backlight.o \

drivers/gpu/drm/i915/display/intel_ddi.c

Lines changed: 28 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include "intel_de.h"
4444
#include "intel_display_power.h"
4545
#include "intel_display_types.h"
46+
#include "intel_dkl_phy.h"
4647
#include "intel_dp.h"
4748
#include "intel_dp_link_training.h"
4849
#include "intel_dp_mst.h"
@@ -1262,33 +1263,30 @@ static void tgl_dkl_phy_set_signal_levels(struct intel_encoder *encoder,
12621263
for (ln = 0; ln < 2; ln++) {
12631264
int level;
12641265

1265-
intel_de_write(dev_priv, HIP_INDEX_REG(tc_port),
1266-
HIP_INDEX_VAL(tc_port, ln));
1267-
1268-
intel_de_write(dev_priv, DKL_TX_PMD_LANE_SUS(tc_port), 0);
1266+
intel_dkl_phy_write(dev_priv, DKL_TX_PMD_LANE_SUS(tc_port), ln, 0);
12691267

12701268
level = intel_ddi_level(encoder, crtc_state, 2*ln+0);
12711269

1272-
intel_de_rmw(dev_priv, DKL_TX_DPCNTL0(tc_port),
1273-
DKL_TX_PRESHOOT_COEFF_MASK |
1274-
DKL_TX_DE_EMPAHSIS_COEFF_MASK |
1275-
DKL_TX_VSWING_CONTROL_MASK,
1276-
DKL_TX_PRESHOOT_COEFF(trans->entries[level].dkl.preshoot) |
1277-
DKL_TX_DE_EMPHASIS_COEFF(trans->entries[level].dkl.de_emphasis) |
1278-
DKL_TX_VSWING_CONTROL(trans->entries[level].dkl.vswing));
1270+
intel_dkl_phy_rmw(dev_priv, DKL_TX_DPCNTL0(tc_port), ln,
1271+
DKL_TX_PRESHOOT_COEFF_MASK |
1272+
DKL_TX_DE_EMPAHSIS_COEFF_MASK |
1273+
DKL_TX_VSWING_CONTROL_MASK,
1274+
DKL_TX_PRESHOOT_COEFF(trans->entries[level].dkl.preshoot) |
1275+
DKL_TX_DE_EMPHASIS_COEFF(trans->entries[level].dkl.de_emphasis) |
1276+
DKL_TX_VSWING_CONTROL(trans->entries[level].dkl.vswing));
12791277

12801278
level = intel_ddi_level(encoder, crtc_state, 2*ln+1);
12811279

1282-
intel_de_rmw(dev_priv, DKL_TX_DPCNTL1(tc_port),
1283-
DKL_TX_PRESHOOT_COEFF_MASK |
1284-
DKL_TX_DE_EMPAHSIS_COEFF_MASK |
1285-
DKL_TX_VSWING_CONTROL_MASK,
1286-
DKL_TX_PRESHOOT_COEFF(trans->entries[level].dkl.preshoot) |
1287-
DKL_TX_DE_EMPHASIS_COEFF(trans->entries[level].dkl.de_emphasis) |
1288-
DKL_TX_VSWING_CONTROL(trans->entries[level].dkl.vswing));
1280+
intel_dkl_phy_rmw(dev_priv, DKL_TX_DPCNTL1(tc_port), ln,
1281+
DKL_TX_PRESHOOT_COEFF_MASK |
1282+
DKL_TX_DE_EMPAHSIS_COEFF_MASK |
1283+
DKL_TX_VSWING_CONTROL_MASK,
1284+
DKL_TX_PRESHOOT_COEFF(trans->entries[level].dkl.preshoot) |
1285+
DKL_TX_DE_EMPHASIS_COEFF(trans->entries[level].dkl.de_emphasis) |
1286+
DKL_TX_VSWING_CONTROL(trans->entries[level].dkl.vswing));
12891287

1290-
intel_de_rmw(dev_priv, DKL_TX_DPCNTL2(tc_port),
1291-
DKL_TX_DP20BITMODE, 0);
1288+
intel_dkl_phy_rmw(dev_priv, DKL_TX_DPCNTL2(tc_port), ln,
1289+
DKL_TX_DP20BITMODE, 0);
12921290

12931291
if (IS_ALDERLAKE_P(dev_priv)) {
12941292
u32 val;
@@ -1306,10 +1304,10 @@ static void tgl_dkl_phy_set_signal_levels(struct intel_encoder *encoder,
13061304
val |= DKL_TX_DPCNTL2_CFG_LOADGENSELECT_TX2(0);
13071305
}
13081306

1309-
intel_de_rmw(dev_priv, DKL_TX_DPCNTL2(tc_port),
1310-
DKL_TX_DPCNTL2_CFG_LOADGENSELECT_TX1_MASK |
1311-
DKL_TX_DPCNTL2_CFG_LOADGENSELECT_TX2_MASK,
1312-
val);
1307+
intel_dkl_phy_rmw(dev_priv, DKL_TX_DPCNTL2(tc_port), ln,
1308+
DKL_TX_DPCNTL2_CFG_LOADGENSELECT_TX1_MASK |
1309+
DKL_TX_DPCNTL2_CFG_LOADGENSELECT_TX2_MASK,
1310+
val);
13131311
}
13141312
}
13151313
}
@@ -2019,12 +2017,8 @@ icl_program_mg_dp_mode(struct intel_digital_port *dig_port,
20192017
return;
20202018

20212019
if (DISPLAY_VER(dev_priv) >= 12) {
2022-
intel_de_write(dev_priv, HIP_INDEX_REG(tc_port),
2023-
HIP_INDEX_VAL(tc_port, 0x0));
2024-
ln0 = intel_de_read(dev_priv, DKL_DP_MODE(tc_port));
2025-
intel_de_write(dev_priv, HIP_INDEX_REG(tc_port),
2026-
HIP_INDEX_VAL(tc_port, 0x1));
2027-
ln1 = intel_de_read(dev_priv, DKL_DP_MODE(tc_port));
2020+
ln0 = intel_dkl_phy_read(dev_priv, DKL_DP_MODE(tc_port), 0);
2021+
ln1 = intel_dkl_phy_read(dev_priv, DKL_DP_MODE(tc_port), 1);
20282022
} else {
20292023
ln0 = intel_de_read(dev_priv, MG_DP_MODE(0, tc_port));
20302024
ln1 = intel_de_read(dev_priv, MG_DP_MODE(1, tc_port));
@@ -2085,12 +2079,8 @@ icl_program_mg_dp_mode(struct intel_digital_port *dig_port,
20852079
}
20862080

20872081
if (DISPLAY_VER(dev_priv) >= 12) {
2088-
intel_de_write(dev_priv, HIP_INDEX_REG(tc_port),
2089-
HIP_INDEX_VAL(tc_port, 0x0));
2090-
intel_de_write(dev_priv, DKL_DP_MODE(tc_port), ln0);
2091-
intel_de_write(dev_priv, HIP_INDEX_REG(tc_port),
2092-
HIP_INDEX_VAL(tc_port, 0x1));
2093-
intel_de_write(dev_priv, DKL_DP_MODE(tc_port), ln1);
2082+
intel_dkl_phy_write(dev_priv, DKL_DP_MODE(tc_port), 0, ln0);
2083+
intel_dkl_phy_write(dev_priv, DKL_DP_MODE(tc_port), 1, ln1);
20942084
} else {
20952085
intel_de_write(dev_priv, MG_DP_MODE(0, tc_port), ln0);
20962086
intel_de_write(dev_priv, MG_DP_MODE(1, tc_port), ln1);
@@ -3094,10 +3084,8 @@ static void adlp_tbt_to_dp_alt_switch_wa(struct intel_encoder *encoder)
30943084
enum tc_port tc_port = intel_port_to_tc(i915, encoder->port);
30953085
int ln;
30963086

3097-
for (ln = 0; ln < 2; ln++) {
3098-
intel_de_write(i915, HIP_INDEX_REG(tc_port), HIP_INDEX_VAL(tc_port, ln));
3099-
intel_de_rmw(i915, DKL_PCS_DW5(tc_port), DKL_PCS_DW5_CORE_SOFTRESET, 0);
3100-
}
3087+
for (ln = 0; ln < 2; ln++)
3088+
intel_dkl_phy_rmw(i915, DKL_PCS_DW5(tc_port), ln, DKL_PCS_DW5_CORE_SOFTRESET, 0);
31013089
}
31023090

31033091
static void intel_ddi_prepare_link_retrain(struct intel_dp *intel_dp,

drivers/gpu/drm/i915/display/intel_display_core.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,14 @@ struct intel_display {
315315
struct intel_global_obj obj;
316316
} dbuf;
317317

318+
struct {
319+
/*
320+
* dkl.phy_lock protects against concurrent access of the
321+
* Dekel TypeC PHYs.
322+
*/
323+
spinlock_t phy_lock;
324+
} dkl;
325+
318326
struct {
319327
/* VLV/CHV/BXT/GLK DSI MMIO register base address */
320328
u32 mmio_base;

drivers/gpu/drm/i915/display/intel_display_power_well.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "intel_de.h"
1313
#include "intel_display_power_well.h"
1414
#include "intel_display_types.h"
15+
#include "intel_dkl_phy.h"
1516
#include "intel_dmc.h"
1617
#include "intel_dpio_phy.h"
1718
#include "intel_dpll.h"
@@ -529,11 +530,9 @@ icl_tc_phy_aux_power_well_enable(struct drm_i915_private *dev_priv,
529530
enum tc_port tc_port;
530531

531532
tc_port = TGL_AUX_PW_TO_TC_PORT(i915_power_well_instance(power_well)->hsw.idx);
532-
intel_de_write(dev_priv, HIP_INDEX_REG(tc_port),
533-
HIP_INDEX_VAL(tc_port, 0x2));
534533

535-
if (intel_de_wait_for_set(dev_priv, DKL_CMN_UC_DW_27(tc_port),
536-
DKL_CMN_UC_DW27_UC_HEALTH, 1))
534+
if (wait_for(intel_dkl_phy_read(dev_priv, DKL_CMN_UC_DW_27(tc_port), 2) &
535+
DKL_CMN_UC_DW27_UC_HEALTH, 1))
537536
drm_warn(&dev_priv->drm,
538537
"Timeout waiting TC uC health\n");
539538
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// SPDX-License-Identifier: MIT
2+
/*
3+
* Copyright © 2022 Intel Corporation
4+
*/
5+
6+
#include "i915_drv.h"
7+
#include "i915_reg.h"
8+
9+
#include "intel_de.h"
10+
#include "intel_display.h"
11+
#include "intel_dkl_phy.h"
12+
13+
static void
14+
dkl_phy_set_hip_idx(struct drm_i915_private *i915, i915_reg_t reg, int idx)
15+
{
16+
enum tc_port tc_port = DKL_REG_TC_PORT(reg);
17+
18+
drm_WARN_ON(&i915->drm, tc_port < TC_PORT_1 || tc_port >= I915_MAX_TC_PORTS);
19+
20+
intel_de_write(i915,
21+
HIP_INDEX_REG(tc_port),
22+
HIP_INDEX_VAL(tc_port, idx));
23+
}
24+
25+
/**
26+
* intel_dkl_phy_read - read a Dekel PHY register
27+
* @i915: i915 device instance
28+
* @reg: Dekel PHY register
29+
* @ln: lane instance of @reg
30+
*
31+
* Read the @reg Dekel PHY register.
32+
*
33+
* Returns the read value.
34+
*/
35+
u32
36+
intel_dkl_phy_read(struct drm_i915_private *i915, i915_reg_t reg, int ln)
37+
{
38+
u32 val;
39+
40+
spin_lock(&i915->display.dkl.phy_lock);
41+
42+
dkl_phy_set_hip_idx(i915, reg, ln);
43+
val = intel_de_read(i915, reg);
44+
45+
spin_unlock(&i915->display.dkl.phy_lock);
46+
47+
return val;
48+
}
49+
50+
/**
51+
* intel_dkl_phy_write - write a Dekel PHY register
52+
* @i915: i915 device instance
53+
* @reg: Dekel PHY register
54+
* @ln: lane instance of @reg
55+
* @val: value to write
56+
*
57+
* Write @val to the @reg Dekel PHY register.
58+
*/
59+
void
60+
intel_dkl_phy_write(struct drm_i915_private *i915, i915_reg_t reg, int ln, u32 val)
61+
{
62+
spin_lock(&i915->display.dkl.phy_lock);
63+
64+
dkl_phy_set_hip_idx(i915, reg, ln);
65+
intel_de_write(i915, reg, val);
66+
67+
spin_unlock(&i915->display.dkl.phy_lock);
68+
}
69+
70+
/**
71+
* intel_dkl_phy_rmw - read-modify-write a Dekel PHY register
72+
* @i915: i915 device instance
73+
* @reg: Dekel PHY register
74+
* @ln: lane instance of @reg
75+
* @clear: mask to clear
76+
* @set: mask to set
77+
*
78+
* Read the @reg Dekel PHY register, clearing then setting the @clear/@set bits in it, and writing
79+
* this value back to the register if the value differs from the read one.
80+
*/
81+
void
82+
intel_dkl_phy_rmw(struct drm_i915_private *i915, i915_reg_t reg, int ln, u32 clear, u32 set)
83+
{
84+
spin_lock(&i915->display.dkl.phy_lock);
85+
86+
dkl_phy_set_hip_idx(i915, reg, ln);
87+
intel_de_rmw(i915, reg, clear, set);
88+
89+
spin_unlock(&i915->display.dkl.phy_lock);
90+
}
91+
92+
/**
93+
* intel_dkl_phy_posting_read - do a posting read from a Dekel PHY register
94+
* @i915: i915 device instance
95+
* @reg: Dekel PHY register
96+
* @ln: lane instance of @reg
97+
*
98+
* Read the @reg Dekel PHY register without returning the read value.
99+
*/
100+
void
101+
intel_dkl_phy_posting_read(struct drm_i915_private *i915, i915_reg_t reg, int ln)
102+
{
103+
spin_lock(&i915->display.dkl.phy_lock);
104+
105+
dkl_phy_set_hip_idx(i915, reg, ln);
106+
intel_de_posting_read(i915, reg);
107+
108+
spin_unlock(&i915->display.dkl.phy_lock);
109+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/* SPDX-License-Identifier: MIT */
2+
/*
3+
* Copyright © 2022 Intel Corporation
4+
*/
5+
6+
#ifndef __INTEL_DKL_PHY_H__
7+
#define __INTEL_DKL_PHY_H__
8+
9+
#include <linux/types.h>
10+
11+
#include "i915_reg_defs.h"
12+
13+
struct drm_i915_private;
14+
15+
u32
16+
intel_dkl_phy_read(struct drm_i915_private *i915, i915_reg_t reg, int ln);
17+
void
18+
intel_dkl_phy_write(struct drm_i915_private *i915, i915_reg_t reg, int ln, u32 val);
19+
void
20+
intel_dkl_phy_rmw(struct drm_i915_private *i915, i915_reg_t reg, int ln, u32 clear, u32 set);
21+
void
22+
intel_dkl_phy_posting_read(struct drm_i915_private *i915, i915_reg_t reg, int ln);
23+
24+
#endif /* __INTEL_DKL_PHY_H__ */

0 commit comments

Comments
 (0)