]> asedeno.scripts.mit.edu Git - linux.git/blob - drivers/gpu/drm/tegra/dp.c
drm/tegra: dp: Set channel coding on link configuration
[linux.git] / drivers / gpu / drm / tegra / dp.c
1 // SPDX-License-Identifier: MIT
2 /*
3  * Copyright (C) 2013-2019 NVIDIA Corporation
4  * Copyright (C) 2015 Rob Clark
5  */
6
7 #include <drm/drm_dp_helper.h>
8 #include <drm/drm_print.h>
9
10 #include "dp.h"
11
12 static const u8 drm_dp_edp_revisions[] = { 0x11, 0x12, 0x13, 0x14 };
13
14 static void drm_dp_link_caps_reset(struct drm_dp_link_caps *caps)
15 {
16         caps->enhanced_framing = false;
17         caps->tps3_supported = false;
18         caps->fast_training = false;
19         caps->channel_coding = false;
20         caps->alternate_scrambler_reset = false;
21 }
22
23 void drm_dp_link_caps_copy(struct drm_dp_link_caps *dest,
24                            const struct drm_dp_link_caps *src)
25 {
26         dest->enhanced_framing = src->enhanced_framing;
27         dest->tps3_supported = src->tps3_supported;
28         dest->fast_training = src->fast_training;
29         dest->channel_coding = src->channel_coding;
30         dest->alternate_scrambler_reset = src->alternate_scrambler_reset;
31 }
32
33 static void drm_dp_link_reset(struct drm_dp_link *link)
34 {
35         if (!link)
36                 return;
37
38         link->revision = 0;
39         link->max_rate = 0;
40         link->max_lanes = 0;
41
42         drm_dp_link_caps_reset(&link->caps);
43         link->aux_rd_interval.cr = 0;
44         link->aux_rd_interval.ce = 0;
45         link->edp = 0;
46
47         link->rate = 0;
48         link->lanes = 0;
49 }
50
51 /**
52  * drm_dp_link_probe() - probe a DisplayPort link for capabilities
53  * @aux: DisplayPort AUX channel
54  * @link: pointer to structure in which to return link capabilities
55  *
56  * The structure filled in by this function can usually be passed directly
57  * into drm_dp_link_power_up() and drm_dp_link_configure() to power up and
58  * configure the link based on the link's capabilities.
59  *
60  * Returns 0 on success or a negative error code on failure.
61  */
62 int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link)
63 {
64         u8 dpcd[DP_RECEIVER_CAP_SIZE], value;
65         unsigned int rd_interval;
66         int err;
67
68         drm_dp_link_reset(link);
69
70         err = drm_dp_dpcd_read(aux, DP_DPCD_REV, dpcd, sizeof(dpcd));
71         if (err < 0)
72                 return err;
73
74         link->revision = dpcd[DP_DPCD_REV];
75         link->max_rate = drm_dp_max_link_rate(dpcd);
76         link->max_lanes = drm_dp_max_lane_count(dpcd);
77
78         link->caps.enhanced_framing = drm_dp_enhanced_frame_cap(dpcd);
79         link->caps.tps3_supported = drm_dp_tps3_supported(dpcd);
80         link->caps.fast_training = drm_dp_fast_training_cap(dpcd);
81         link->caps.channel_coding = drm_dp_channel_coding_supported(dpcd);
82
83         if (drm_dp_alternate_scrambler_reset_cap(dpcd)) {
84                 link->caps.alternate_scrambler_reset = true;
85
86                 err = drm_dp_dpcd_readb(aux, DP_EDP_DPCD_REV, &value);
87                 if (err < 0)
88                         return err;
89
90                 if (value >= ARRAY_SIZE(drm_dp_edp_revisions))
91                         DRM_ERROR("unsupported eDP version: %02x\n", value);
92                 else
93                         link->edp = drm_dp_edp_revisions[value];
94         }
95
96         /*
97          * The DPCD stores the AUX read interval in units of 4 ms. There are
98          * two special cases:
99          *
100          *   1) if the TRAINING_AUX_RD_INTERVAL field is 0, the clock recovery
101          *      and channel equalization should use 100 us or 400 us AUX read
102          *      intervals, respectively
103          *
104          *   2) for DP v1.4 and above, clock recovery should always use 100 us
105          *      AUX read intervals
106          */
107         rd_interval = dpcd[DP_TRAINING_AUX_RD_INTERVAL] &
108                            DP_TRAINING_AUX_RD_MASK;
109
110         if (rd_interval > 4) {
111                 DRM_DEBUG_KMS("AUX interval %u out of range (max. 4)\n",
112                               rd_interval);
113                 rd_interval = 4;
114         }
115
116         rd_interval *= 4 * USEC_PER_MSEC;
117
118         if (rd_interval == 0 || link->revision >= DP_DPCD_REV_14)
119                 link->aux_rd_interval.cr = 100;
120
121         if (rd_interval == 0)
122                 link->aux_rd_interval.ce = 400;
123
124         link->rate = link->max_rate;
125         link->lanes = link->max_lanes;
126
127         return 0;
128 }
129
130 /**
131  * drm_dp_link_power_up() - power up a DisplayPort link
132  * @aux: DisplayPort AUX channel
133  * @link: pointer to a structure containing the link configuration
134  *
135  * Returns 0 on success or a negative error code on failure.
136  */
137 int drm_dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link)
138 {
139         u8 value;
140         int err;
141
142         /* DP_SET_POWER register is only available on DPCD v1.1 and later */
143         if (link->revision < 0x11)
144                 return 0;
145
146         err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value);
147         if (err < 0)
148                 return err;
149
150         value &= ~DP_SET_POWER_MASK;
151         value |= DP_SET_POWER_D0;
152
153         err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value);
154         if (err < 0)
155                 return err;
156
157         /*
158          * According to the DP 1.1 specification, a "Sink Device must exit the
159          * power saving state within 1 ms" (Section 2.5.3.1, Table 5-52, "Sink
160          * Control Field" (register 0x600).
161          */
162         usleep_range(1000, 2000);
163
164         return 0;
165 }
166
167 /**
168  * drm_dp_link_power_down() - power down a DisplayPort link
169  * @aux: DisplayPort AUX channel
170  * @link: pointer to a structure containing the link configuration
171  *
172  * Returns 0 on success or a negative error code on failure.
173  */
174 int drm_dp_link_power_down(struct drm_dp_aux *aux, struct drm_dp_link *link)
175 {
176         u8 value;
177         int err;
178
179         /* DP_SET_POWER register is only available on DPCD v1.1 and later */
180         if (link->revision < 0x11)
181                 return 0;
182
183         err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value);
184         if (err < 0)
185                 return err;
186
187         value &= ~DP_SET_POWER_MASK;
188         value |= DP_SET_POWER_D3;
189
190         err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value);
191         if (err < 0)
192                 return err;
193
194         return 0;
195 }
196
197 /**
198  * drm_dp_link_configure() - configure a DisplayPort link
199  * @aux: DisplayPort AUX channel
200  * @link: pointer to a structure containing the link configuration
201  *
202  * Returns 0 on success or a negative error code on failure.
203  */
204 int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link)
205 {
206         u8 values[2], value;
207         int err;
208
209         values[0] = drm_dp_link_rate_to_bw_code(link->rate);
210         values[1] = link->lanes;
211
212         if (link->caps.enhanced_framing)
213                 values[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
214
215         err = drm_dp_dpcd_write(aux, DP_LINK_BW_SET, values, sizeof(values));
216         if (err < 0)
217                 return err;
218
219         if (link->caps.channel_coding)
220                 value = DP_SET_ANSI_8B10B;
221         else
222                 value = 0;
223
224         err = drm_dp_dpcd_writeb(aux, DP_MAIN_LINK_CHANNEL_CODING_SET, value);
225         if (err < 0)
226                 return err;
227
228         return 0;
229 }