.. | .. |
---|
20 | 20 | * OF THIS SOFTWARE. |
---|
21 | 21 | */ |
---|
22 | 22 | |
---|
| 23 | +#include <linux/delay.h> |
---|
| 24 | +#include <linux/errno.h> |
---|
| 25 | +#include <linux/i2c.h> |
---|
| 26 | +#include <linux/init.h> |
---|
23 | 27 | #include <linux/kernel.h> |
---|
24 | 28 | #include <linux/module.h> |
---|
25 | | -#include <linux/delay.h> |
---|
26 | | -#include <linux/init.h> |
---|
27 | | -#include <linux/errno.h> |
---|
28 | 29 | #include <linux/sched.h> |
---|
29 | | -#include <linux/i2c.h> |
---|
30 | 30 | #include <linux/seq_file.h> |
---|
| 31 | + |
---|
31 | 32 | #include <drm/drm_dp_helper.h> |
---|
32 | | -#include <drm/drmP.h> |
---|
| 33 | +#include <drm/drm_print.h> |
---|
| 34 | +#include <drm/drm_vblank.h> |
---|
| 35 | +#include <drm/drm_dp_mst_helper.h> |
---|
33 | 36 | |
---|
34 | 37 | #include "drm_crtc_helper_internal.h" |
---|
35 | 38 | |
---|
.. | .. |
---|
54 | 57 | int i = DP_LANE0_1_STATUS + (lane >> 1); |
---|
55 | 58 | int s = (lane & 1) * 4; |
---|
56 | 59 | u8 l = dp_link_status(link_status, i); |
---|
| 60 | + |
---|
57 | 61 | return (l >> s) & 0xf; |
---|
58 | 62 | } |
---|
59 | 63 | |
---|
.. | .. |
---|
118 | 122 | } |
---|
119 | 123 | EXPORT_SYMBOL(drm_dp_get_adjust_request_pre_emphasis); |
---|
120 | 124 | |
---|
121 | | -void drm_dp_link_train_clock_recovery_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) { |
---|
122 | | - int rd_interval = dpcd[DP_TRAINING_AUX_RD_INTERVAL] & |
---|
123 | | - DP_TRAINING_AUX_RD_MASK; |
---|
| 125 | +u8 drm_dp_get_adjust_request_post_cursor(const u8 link_status[DP_LINK_STATUS_SIZE], |
---|
| 126 | + unsigned int lane) |
---|
| 127 | +{ |
---|
| 128 | + unsigned int offset = DP_ADJUST_REQUEST_POST_CURSOR2; |
---|
| 129 | + u8 value = dp_link_status(link_status, offset); |
---|
| 130 | + |
---|
| 131 | + return (value >> (lane << 1)) & 0x3; |
---|
| 132 | +} |
---|
| 133 | +EXPORT_SYMBOL(drm_dp_get_adjust_request_post_cursor); |
---|
| 134 | + |
---|
| 135 | +void drm_dp_link_train_clock_recovery_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) |
---|
| 136 | +{ |
---|
| 137 | + unsigned long rd_interval = dpcd[DP_TRAINING_AUX_RD_INTERVAL] & |
---|
| 138 | + DP_TRAINING_AUX_RD_MASK; |
---|
124 | 139 | |
---|
125 | 140 | if (rd_interval > 4) |
---|
126 | | - DRM_DEBUG_KMS("AUX interval %d, out of range (max 4)\n", |
---|
| 141 | + DRM_DEBUG_KMS("AUX interval %lu, out of range (max 4)\n", |
---|
127 | 142 | rd_interval); |
---|
128 | 143 | |
---|
129 | 144 | if (rd_interval == 0 || dpcd[DP_DPCD_REV] >= DP_DPCD_REV_14) |
---|
130 | | - udelay(100); |
---|
| 145 | + rd_interval = 100; |
---|
131 | 146 | else |
---|
132 | | - mdelay(rd_interval * 4); |
---|
| 147 | + rd_interval *= 4 * USEC_PER_MSEC; |
---|
| 148 | + |
---|
| 149 | + usleep_range(rd_interval, rd_interval * 2); |
---|
133 | 150 | } |
---|
134 | 151 | EXPORT_SYMBOL(drm_dp_link_train_clock_recovery_delay); |
---|
135 | 152 | |
---|
136 | | -void drm_dp_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) { |
---|
137 | | - int rd_interval = dpcd[DP_TRAINING_AUX_RD_INTERVAL] & |
---|
138 | | - DP_TRAINING_AUX_RD_MASK; |
---|
| 153 | +void drm_dp_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) |
---|
| 154 | +{ |
---|
| 155 | + unsigned long rd_interval = dpcd[DP_TRAINING_AUX_RD_INTERVAL] & |
---|
| 156 | + DP_TRAINING_AUX_RD_MASK; |
---|
139 | 157 | |
---|
140 | 158 | if (rd_interval > 4) |
---|
141 | | - DRM_DEBUG_KMS("AUX interval %d, out of range (max 4)\n", |
---|
| 159 | + DRM_DEBUG_KMS("AUX interval %lu, out of range (max 4)\n", |
---|
142 | 160 | rd_interval); |
---|
143 | 161 | |
---|
144 | 162 | if (rd_interval == 0) |
---|
145 | | - udelay(400); |
---|
| 163 | + rd_interval = 400; |
---|
146 | 164 | else |
---|
147 | | - mdelay(rd_interval * 4); |
---|
| 165 | + rd_interval *= 4 * USEC_PER_MSEC; |
---|
| 166 | + |
---|
| 167 | + usleep_range(rd_interval, rd_interval * 2); |
---|
148 | 168 | } |
---|
149 | 169 | EXPORT_SYMBOL(drm_dp_link_train_channel_eq_delay); |
---|
150 | 170 | |
---|
151 | 171 | u8 drm_dp_link_rate_to_bw_code(int link_rate) |
---|
152 | 172 | { |
---|
153 | | - switch (link_rate) { |
---|
154 | | - default: |
---|
155 | | - WARN(1, "unknown DP link rate %d, using %x\n", link_rate, |
---|
156 | | - DP_LINK_BW_1_62); |
---|
157 | | - case 162000: |
---|
158 | | - return DP_LINK_BW_1_62; |
---|
159 | | - case 270000: |
---|
160 | | - return DP_LINK_BW_2_7; |
---|
161 | | - case 540000: |
---|
162 | | - return DP_LINK_BW_5_4; |
---|
163 | | - case 810000: |
---|
164 | | - return DP_LINK_BW_8_1; |
---|
165 | | - } |
---|
| 173 | + /* Spec says link_bw = link_rate / 0.27Gbps */ |
---|
| 174 | + return link_rate / 27000; |
---|
166 | 175 | } |
---|
167 | 176 | EXPORT_SYMBOL(drm_dp_link_rate_to_bw_code); |
---|
168 | 177 | |
---|
169 | 178 | int drm_dp_bw_code_to_link_rate(u8 link_bw) |
---|
170 | 179 | { |
---|
171 | | - switch (link_bw) { |
---|
172 | | - default: |
---|
173 | | - WARN(1, "unknown DP link BW code %x, using 162000\n", link_bw); |
---|
174 | | - case DP_LINK_BW_1_62: |
---|
175 | | - return 162000; |
---|
176 | | - case DP_LINK_BW_2_7: |
---|
177 | | - return 270000; |
---|
178 | | - case DP_LINK_BW_5_4: |
---|
179 | | - return 540000; |
---|
180 | | - case DP_LINK_BW_8_1: |
---|
181 | | - return 810000; |
---|
182 | | - } |
---|
| 180 | + /* Spec says link_rate = link_bw * 0.27Gbps */ |
---|
| 181 | + return link_bw * 27000; |
---|
183 | 182 | } |
---|
184 | 183 | EXPORT_SYMBOL(drm_dp_bw_code_to_link_rate); |
---|
185 | 184 | |
---|
.. | .. |
---|
192 | 191 | const char *arrow = request == DP_AUX_NATIVE_READ ? "->" : "<-"; |
---|
193 | 192 | |
---|
194 | 193 | if (ret > 0) |
---|
195 | | - drm_dbg(DRM_UT_DP, "%s: 0x%05x AUX %s (ret=%3d) %*ph\n", |
---|
196 | | - aux->name, offset, arrow, ret, min(ret, 20), buffer); |
---|
| 194 | + DRM_DEBUG_DP("%s: 0x%05x AUX %s (ret=%3d) %*ph\n", |
---|
| 195 | + aux->name, offset, arrow, ret, min(ret, 20), buffer); |
---|
197 | 196 | else |
---|
198 | | - drm_dbg(DRM_UT_DP, "%s: 0x%05x AUX %s (ret=%3d)\n", |
---|
199 | | - aux->name, offset, arrow, ret); |
---|
| 197 | + DRM_DEBUG_DP("%s: 0x%05x AUX %s (ret=%3d)\n", |
---|
| 198 | + aux->name, offset, arrow, ret); |
---|
200 | 199 | } |
---|
201 | 200 | |
---|
202 | 201 | /** |
---|
.. | .. |
---|
239 | 238 | } |
---|
240 | 239 | |
---|
241 | 240 | ret = aux->transfer(aux, &msg); |
---|
242 | | - |
---|
243 | 241 | if (ret >= 0) { |
---|
244 | 242 | native_reply = msg.reply & DP_AUX_NATIVE_REPLY_MASK; |
---|
245 | 243 | if (native_reply == DP_AUX_NATIVE_REPLY_ACK) { |
---|
.. | .. |
---|
260 | 258 | err = ret; |
---|
261 | 259 | } |
---|
262 | 260 | |
---|
263 | | - DRM_DEBUG_KMS("Too many retries, giving up. First error: %d\n", err); |
---|
| 261 | + DRM_DEBUG_KMS("%s: Too many retries, giving up. First error: %d\n", |
---|
| 262 | + aux->name, err); |
---|
264 | 263 | ret = err; |
---|
265 | 264 | |
---|
266 | 265 | unlock: |
---|
.. | .. |
---|
270 | 269 | |
---|
271 | 270 | /** |
---|
272 | 271 | * drm_dp_dpcd_read() - read a series of bytes from the DPCD |
---|
273 | | - * @aux: DisplayPort AUX channel |
---|
| 272 | + * @aux: DisplayPort AUX channel (SST or MST) |
---|
274 | 273 | * @offset: address of the (first) register to read |
---|
275 | 274 | * @buffer: buffer to store the register values |
---|
276 | 275 | * @size: number of bytes in @buffer |
---|
.. | .. |
---|
299 | 298 | * We just have to do it before any DPCD access and hope that the |
---|
300 | 299 | * monitor doesn't power down exactly after the throw away read. |
---|
301 | 300 | */ |
---|
302 | | - ret = drm_dp_dpcd_access(aux, DP_AUX_NATIVE_READ, DP_DPCD_REV, buffer, |
---|
303 | | - 1); |
---|
304 | | - if (ret != 1) |
---|
305 | | - goto out; |
---|
| 301 | + if (!aux->is_remote) { |
---|
| 302 | + ret = drm_dp_dpcd_access(aux, DP_AUX_NATIVE_READ, DP_DPCD_REV, |
---|
| 303 | + buffer, 1); |
---|
| 304 | + if (ret != 1) |
---|
| 305 | + goto out; |
---|
| 306 | + } |
---|
306 | 307 | |
---|
307 | | - ret = drm_dp_dpcd_access(aux, DP_AUX_NATIVE_READ, offset, buffer, |
---|
308 | | - size); |
---|
| 308 | + if (aux->is_remote) |
---|
| 309 | + ret = drm_dp_mst_dpcd_read(aux, offset, buffer, size); |
---|
| 310 | + else |
---|
| 311 | + ret = drm_dp_dpcd_access(aux, DP_AUX_NATIVE_READ, offset, |
---|
| 312 | + buffer, size); |
---|
309 | 313 | |
---|
310 | 314 | out: |
---|
311 | 315 | drm_dp_dump_access(aux, DP_AUX_NATIVE_READ, offset, buffer, ret); |
---|
.. | .. |
---|
315 | 319 | |
---|
316 | 320 | /** |
---|
317 | 321 | * drm_dp_dpcd_write() - write a series of bytes to the DPCD |
---|
318 | | - * @aux: DisplayPort AUX channel |
---|
| 322 | + * @aux: DisplayPort AUX channel (SST or MST) |
---|
319 | 323 | * @offset: address of the (first) register to write |
---|
320 | 324 | * @buffer: buffer containing the values to write |
---|
321 | 325 | * @size: number of bytes in @buffer |
---|
.. | .. |
---|
332 | 336 | { |
---|
333 | 337 | int ret; |
---|
334 | 338 | |
---|
335 | | - ret = drm_dp_dpcd_access(aux, DP_AUX_NATIVE_WRITE, offset, buffer, |
---|
336 | | - size); |
---|
| 339 | + if (aux->is_remote) |
---|
| 340 | + ret = drm_dp_mst_dpcd_write(aux, offset, buffer, size); |
---|
| 341 | + else |
---|
| 342 | + ret = drm_dp_dpcd_access(aux, DP_AUX_NATIVE_WRITE, offset, |
---|
| 343 | + buffer, size); |
---|
| 344 | + |
---|
337 | 345 | drm_dp_dump_access(aux, DP_AUX_NATIVE_WRITE, offset, buffer, ret); |
---|
338 | 346 | return ret; |
---|
339 | 347 | } |
---|
.. | .. |
---|
355 | 363 | } |
---|
356 | 364 | EXPORT_SYMBOL(drm_dp_dpcd_read_link_status); |
---|
357 | 365 | |
---|
358 | | -/** |
---|
359 | | - * drm_dp_link_probe() - probe a DisplayPort link for capabilities |
---|
360 | | - * @aux: DisplayPort AUX channel |
---|
361 | | - * @link: pointer to structure in which to return link capabilities |
---|
362 | | - * |
---|
363 | | - * The structure filled in by this function can usually be passed directly |
---|
364 | | - * into drm_dp_link_power_up() and drm_dp_link_configure() to power up and |
---|
365 | | - * configure the link based on the link's capabilities. |
---|
366 | | - * |
---|
367 | | - * Returns 0 on success or a negative error code on failure. |
---|
368 | | - */ |
---|
369 | | -int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link) |
---|
| 366 | +static bool is_edid_digital_input_dp(const struct edid *edid) |
---|
370 | 367 | { |
---|
371 | | - u8 values[3]; |
---|
372 | | - int err; |
---|
373 | | - |
---|
374 | | - memset(link, 0, sizeof(*link)); |
---|
375 | | - |
---|
376 | | - err = drm_dp_dpcd_read(aux, DP_DPCD_REV, values, sizeof(values)); |
---|
377 | | - if (err < 0) |
---|
378 | | - return err; |
---|
379 | | - |
---|
380 | | - link->revision = values[0]; |
---|
381 | | - link->rate = drm_dp_bw_code_to_link_rate(values[1]); |
---|
382 | | - link->num_lanes = values[2] & DP_MAX_LANE_COUNT_MASK; |
---|
383 | | - |
---|
384 | | - if (values[2] & DP_ENHANCED_FRAME_CAP) |
---|
385 | | - link->capabilities |= DP_LINK_CAP_ENHANCED_FRAMING; |
---|
386 | | - |
---|
387 | | - return 0; |
---|
| 368 | + return edid && edid->revision >= 4 && |
---|
| 369 | + edid->input & DRM_EDID_INPUT_DIGITAL && |
---|
| 370 | + (edid->input & DRM_EDID_DIGITAL_TYPE_MASK) == DRM_EDID_DIGITAL_TYPE_DP; |
---|
388 | 371 | } |
---|
389 | | -EXPORT_SYMBOL(drm_dp_link_probe); |
---|
390 | 372 | |
---|
391 | 373 | /** |
---|
392 | | - * drm_dp_link_power_up() - power up a DisplayPort link |
---|
393 | | - * @aux: DisplayPort AUX channel |
---|
394 | | - * @link: pointer to a structure containing the link configuration |
---|
| 374 | + * drm_dp_downstream_is_type() - is the downstream facing port of certain type? |
---|
| 375 | + * @dpcd: DisplayPort configuration data |
---|
| 376 | + * @port_cap: port capabilities |
---|
| 377 | + * @type: port type to be checked. Can be: |
---|
| 378 | + * %DP_DS_PORT_TYPE_DP, %DP_DS_PORT_TYPE_VGA, %DP_DS_PORT_TYPE_DVI, |
---|
| 379 | + * %DP_DS_PORT_TYPE_HDMI, %DP_DS_PORT_TYPE_NON_EDID, |
---|
| 380 | + * %DP_DS_PORT_TYPE_DP_DUALMODE or %DP_DS_PORT_TYPE_WIRELESS. |
---|
395 | 381 | * |
---|
396 | | - * Returns 0 on success or a negative error code on failure. |
---|
| 382 | + * Caveat: Only works with DPCD 1.1+ port caps. |
---|
| 383 | + * |
---|
| 384 | + * Returns: whether the downstream facing port matches the type. |
---|
397 | 385 | */ |
---|
398 | | -int drm_dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link) |
---|
| 386 | +bool drm_dp_downstream_is_type(const u8 dpcd[DP_RECEIVER_CAP_SIZE], |
---|
| 387 | + const u8 port_cap[4], u8 type) |
---|
399 | 388 | { |
---|
400 | | - u8 value; |
---|
401 | | - int err; |
---|
| 389 | + return drm_dp_is_branch(dpcd) && |
---|
| 390 | + dpcd[DP_DPCD_REV] >= 0x11 && |
---|
| 391 | + (port_cap[0] & DP_DS_PORT_TYPE_MASK) == type; |
---|
| 392 | +} |
---|
| 393 | +EXPORT_SYMBOL(drm_dp_downstream_is_type); |
---|
402 | 394 | |
---|
403 | | - /* DP_SET_POWER register is only available on DPCD v1.1 and later */ |
---|
404 | | - if (link->revision < 0x11) |
---|
405 | | - return 0; |
---|
| 395 | +/** |
---|
| 396 | + * drm_dp_downstream_is_tmds() - is the downstream facing port TMDS? |
---|
| 397 | + * @dpcd: DisplayPort configuration data |
---|
| 398 | + * @port_cap: port capabilities |
---|
| 399 | + * @edid: EDID |
---|
| 400 | + * |
---|
| 401 | + * Returns: whether the downstream facing port is TMDS (HDMI/DVI). |
---|
| 402 | + */ |
---|
| 403 | +bool drm_dp_downstream_is_tmds(const u8 dpcd[DP_RECEIVER_CAP_SIZE], |
---|
| 404 | + const u8 port_cap[4], |
---|
| 405 | + const struct edid *edid) |
---|
| 406 | +{ |
---|
| 407 | + if (dpcd[DP_DPCD_REV] < 0x11) { |
---|
| 408 | + switch (dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_TYPE_MASK) { |
---|
| 409 | + case DP_DWN_STRM_PORT_TYPE_TMDS: |
---|
| 410 | + return true; |
---|
| 411 | + default: |
---|
| 412 | + return false; |
---|
| 413 | + } |
---|
| 414 | + } |
---|
406 | 415 | |
---|
407 | | - err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value); |
---|
408 | | - if (err < 0) |
---|
409 | | - return err; |
---|
| 416 | + switch (port_cap[0] & DP_DS_PORT_TYPE_MASK) { |
---|
| 417 | + case DP_DS_PORT_TYPE_DP_DUALMODE: |
---|
| 418 | + if (is_edid_digital_input_dp(edid)) |
---|
| 419 | + return false; |
---|
| 420 | + fallthrough; |
---|
| 421 | + case DP_DS_PORT_TYPE_DVI: |
---|
| 422 | + case DP_DS_PORT_TYPE_HDMI: |
---|
| 423 | + return true; |
---|
| 424 | + default: |
---|
| 425 | + return false; |
---|
| 426 | + } |
---|
| 427 | +} |
---|
| 428 | +EXPORT_SYMBOL(drm_dp_downstream_is_tmds); |
---|
410 | 429 | |
---|
411 | | - value &= ~DP_SET_POWER_MASK; |
---|
412 | | - value |= DP_SET_POWER_D0; |
---|
| 430 | +/** |
---|
| 431 | + * drm_dp_send_real_edid_checksum() - send back real edid checksum value |
---|
| 432 | + * @aux: DisplayPort AUX channel |
---|
| 433 | + * @real_edid_checksum: real edid checksum for the last block |
---|
| 434 | + * |
---|
| 435 | + * Returns: |
---|
| 436 | + * True on success |
---|
| 437 | + */ |
---|
| 438 | +bool drm_dp_send_real_edid_checksum(struct drm_dp_aux *aux, |
---|
| 439 | + u8 real_edid_checksum) |
---|
| 440 | +{ |
---|
| 441 | + u8 link_edid_read = 0, auto_test_req = 0, test_resp = 0; |
---|
413 | 442 | |
---|
414 | | - err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value); |
---|
415 | | - if (err < 0) |
---|
416 | | - return err; |
---|
| 443 | + if (drm_dp_dpcd_read(aux, DP_DEVICE_SERVICE_IRQ_VECTOR, |
---|
| 444 | + &auto_test_req, 1) < 1) { |
---|
| 445 | + DRM_ERROR("%s: DPCD failed read at register 0x%x\n", |
---|
| 446 | + aux->name, DP_DEVICE_SERVICE_IRQ_VECTOR); |
---|
| 447 | + return false; |
---|
| 448 | + } |
---|
| 449 | + auto_test_req &= DP_AUTOMATED_TEST_REQUEST; |
---|
| 450 | + |
---|
| 451 | + if (drm_dp_dpcd_read(aux, DP_TEST_REQUEST, &link_edid_read, 1) < 1) { |
---|
| 452 | + DRM_ERROR("%s: DPCD failed read at register 0x%x\n", |
---|
| 453 | + aux->name, DP_TEST_REQUEST); |
---|
| 454 | + return false; |
---|
| 455 | + } |
---|
| 456 | + link_edid_read &= DP_TEST_LINK_EDID_READ; |
---|
| 457 | + |
---|
| 458 | + if (!auto_test_req || !link_edid_read) { |
---|
| 459 | + DRM_DEBUG_KMS("%s: Source DUT does not support TEST_EDID_READ\n", |
---|
| 460 | + aux->name); |
---|
| 461 | + return false; |
---|
| 462 | + } |
---|
| 463 | + |
---|
| 464 | + if (drm_dp_dpcd_write(aux, DP_DEVICE_SERVICE_IRQ_VECTOR, |
---|
| 465 | + &auto_test_req, 1) < 1) { |
---|
| 466 | + DRM_ERROR("%s: DPCD failed write at register 0x%x\n", |
---|
| 467 | + aux->name, DP_DEVICE_SERVICE_IRQ_VECTOR); |
---|
| 468 | + return false; |
---|
| 469 | + } |
---|
| 470 | + |
---|
| 471 | + /* send back checksum for the last edid extension block data */ |
---|
| 472 | + if (drm_dp_dpcd_write(aux, DP_TEST_EDID_CHECKSUM, |
---|
| 473 | + &real_edid_checksum, 1) < 1) { |
---|
| 474 | + DRM_ERROR("%s: DPCD failed write at register 0x%x\n", |
---|
| 475 | + aux->name, DP_TEST_EDID_CHECKSUM); |
---|
| 476 | + return false; |
---|
| 477 | + } |
---|
| 478 | + |
---|
| 479 | + test_resp |= DP_TEST_EDID_CHECKSUM_WRITE; |
---|
| 480 | + if (drm_dp_dpcd_write(aux, DP_TEST_RESPONSE, &test_resp, 1) < 1) { |
---|
| 481 | + DRM_ERROR("%s: DPCD failed write at register 0x%x\n", |
---|
| 482 | + aux->name, DP_TEST_RESPONSE); |
---|
| 483 | + return false; |
---|
| 484 | + } |
---|
| 485 | + |
---|
| 486 | + return true; |
---|
| 487 | +} |
---|
| 488 | +EXPORT_SYMBOL(drm_dp_send_real_edid_checksum); |
---|
| 489 | + |
---|
| 490 | +static u8 drm_dp_downstream_port_count(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) |
---|
| 491 | +{ |
---|
| 492 | + u8 port_count = dpcd[DP_DOWN_STREAM_PORT_COUNT] & DP_PORT_COUNT_MASK; |
---|
| 493 | + |
---|
| 494 | + if (dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE && port_count > 4) |
---|
| 495 | + port_count = 4; |
---|
| 496 | + |
---|
| 497 | + return port_count; |
---|
| 498 | +} |
---|
| 499 | + |
---|
| 500 | +static int drm_dp_read_extended_dpcd_caps(struct drm_dp_aux *aux, |
---|
| 501 | + u8 dpcd[DP_RECEIVER_CAP_SIZE]) |
---|
| 502 | +{ |
---|
| 503 | + u8 dpcd_ext[6]; |
---|
| 504 | + int ret; |
---|
417 | 505 | |
---|
418 | 506 | /* |
---|
419 | | - * According to the DP 1.1 specification, a "Sink Device must exit the |
---|
420 | | - * power saving state within 1 ms" (Section 2.5.3.1, Table 5-52, "Sink |
---|
421 | | - * Control Field" (register 0x600). |
---|
| 507 | + * Prior to DP1.3 the bit represented by |
---|
| 508 | + * DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT was reserved. |
---|
| 509 | + * If it is set DP_DPCD_REV at 0000h could be at a value less than |
---|
| 510 | + * the true capability of the panel. The only way to check is to |
---|
| 511 | + * then compare 0000h and 2200h. |
---|
422 | 512 | */ |
---|
423 | | - usleep_range(1000, 2000); |
---|
424 | | - |
---|
425 | | - return 0; |
---|
426 | | -} |
---|
427 | | -EXPORT_SYMBOL(drm_dp_link_power_up); |
---|
428 | | - |
---|
429 | | -/** |
---|
430 | | - * drm_dp_link_power_down() - power down a DisplayPort link |
---|
431 | | - * @aux: DisplayPort AUX channel |
---|
432 | | - * @link: pointer to a structure containing the link configuration |
---|
433 | | - * |
---|
434 | | - * Returns 0 on success or a negative error code on failure. |
---|
435 | | - */ |
---|
436 | | -int drm_dp_link_power_down(struct drm_dp_aux *aux, struct drm_dp_link *link) |
---|
437 | | -{ |
---|
438 | | - u8 value; |
---|
439 | | - int err; |
---|
440 | | - |
---|
441 | | - /* DP_SET_POWER register is only available on DPCD v1.1 and later */ |
---|
442 | | - if (link->revision < 0x11) |
---|
| 513 | + if (!(dpcd[DP_TRAINING_AUX_RD_INTERVAL] & |
---|
| 514 | + DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT)) |
---|
443 | 515 | return 0; |
---|
444 | 516 | |
---|
445 | | - err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value); |
---|
446 | | - if (err < 0) |
---|
447 | | - return err; |
---|
| 517 | + ret = drm_dp_dpcd_read(aux, DP_DP13_DPCD_REV, &dpcd_ext, |
---|
| 518 | + sizeof(dpcd_ext)); |
---|
| 519 | + if (ret < 0) |
---|
| 520 | + return ret; |
---|
| 521 | + if (ret != sizeof(dpcd_ext)) |
---|
| 522 | + return -EIO; |
---|
448 | 523 | |
---|
449 | | - value &= ~DP_SET_POWER_MASK; |
---|
450 | | - value |= DP_SET_POWER_D3; |
---|
| 524 | + if (dpcd[DP_DPCD_REV] > dpcd_ext[DP_DPCD_REV]) { |
---|
| 525 | + DRM_DEBUG_KMS("%s: Extended DPCD rev less than base DPCD rev (%d > %d)\n", |
---|
| 526 | + aux->name, dpcd[DP_DPCD_REV], |
---|
| 527 | + dpcd_ext[DP_DPCD_REV]); |
---|
| 528 | + return 0; |
---|
| 529 | + } |
---|
451 | 530 | |
---|
452 | | - err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value); |
---|
453 | | - if (err < 0) |
---|
454 | | - return err; |
---|
| 531 | + if (!memcmp(dpcd, dpcd_ext, sizeof(dpcd_ext))) |
---|
| 532 | + return 0; |
---|
| 533 | + |
---|
| 534 | + DRM_DEBUG_KMS("%s: Base DPCD: %*ph\n", |
---|
| 535 | + aux->name, DP_RECEIVER_CAP_SIZE, dpcd); |
---|
| 536 | + |
---|
| 537 | + memcpy(dpcd, dpcd_ext, sizeof(dpcd_ext)); |
---|
455 | 538 | |
---|
456 | 539 | return 0; |
---|
457 | 540 | } |
---|
458 | | -EXPORT_SYMBOL(drm_dp_link_power_down); |
---|
459 | 541 | |
---|
460 | 542 | /** |
---|
461 | | - * drm_dp_link_configure() - configure a DisplayPort link |
---|
| 543 | + * drm_dp_read_dpcd_caps() - read DPCD caps and extended DPCD caps if |
---|
| 544 | + * available |
---|
462 | 545 | * @aux: DisplayPort AUX channel |
---|
463 | | - * @link: pointer to a structure containing the link configuration |
---|
| 546 | + * @dpcd: Buffer to store the resulting DPCD in |
---|
464 | 547 | * |
---|
465 | | - * Returns 0 on success or a negative error code on failure. |
---|
| 548 | + * Attempts to read the base DPCD caps for @aux. Additionally, this function |
---|
| 549 | + * checks for and reads the extended DPRX caps (%DP_DP13_DPCD_REV) if |
---|
| 550 | + * present. |
---|
| 551 | + * |
---|
| 552 | + * Returns: %0 if the DPCD was read successfully, negative error code |
---|
| 553 | + * otherwise. |
---|
466 | 554 | */ |
---|
467 | | -int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link) |
---|
| 555 | +int drm_dp_read_dpcd_caps(struct drm_dp_aux *aux, |
---|
| 556 | + u8 dpcd[DP_RECEIVER_CAP_SIZE]) |
---|
468 | 557 | { |
---|
469 | | - u8 values[2]; |
---|
470 | | - int err; |
---|
| 558 | + int ret; |
---|
471 | 559 | |
---|
472 | | - values[0] = drm_dp_link_rate_to_bw_code(link->rate); |
---|
473 | | - values[1] = link->num_lanes; |
---|
| 560 | + ret = drm_dp_dpcd_read(aux, DP_DPCD_REV, dpcd, DP_RECEIVER_CAP_SIZE); |
---|
| 561 | + if (ret < 0) |
---|
| 562 | + return ret; |
---|
| 563 | + if (ret != DP_RECEIVER_CAP_SIZE || dpcd[DP_DPCD_REV] == 0) |
---|
| 564 | + return -EIO; |
---|
474 | 565 | |
---|
475 | | - if (link->capabilities & DP_LINK_CAP_ENHANCED_FRAMING) |
---|
476 | | - values[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; |
---|
| 566 | + ret = drm_dp_read_extended_dpcd_caps(aux, dpcd); |
---|
| 567 | + if (ret < 0) |
---|
| 568 | + return ret; |
---|
477 | 569 | |
---|
478 | | - err = drm_dp_dpcd_write(aux, DP_LINK_BW_SET, values, sizeof(values)); |
---|
479 | | - if (err < 0) |
---|
480 | | - return err; |
---|
| 570 | + DRM_DEBUG_KMS("%s: DPCD: %*ph\n", |
---|
| 571 | + aux->name, DP_RECEIVER_CAP_SIZE, dpcd); |
---|
| 572 | + |
---|
| 573 | + return ret; |
---|
| 574 | +} |
---|
| 575 | +EXPORT_SYMBOL(drm_dp_read_dpcd_caps); |
---|
| 576 | + |
---|
| 577 | +/** |
---|
| 578 | + * drm_dp_read_downstream_info() - read DPCD downstream port info if available |
---|
| 579 | + * @aux: DisplayPort AUX channel |
---|
| 580 | + * @dpcd: A cached copy of the port's DPCD |
---|
| 581 | + * @downstream_ports: buffer to store the downstream port info in |
---|
| 582 | + * |
---|
| 583 | + * See also: |
---|
| 584 | + * drm_dp_downstream_max_clock() |
---|
| 585 | + * drm_dp_downstream_max_bpc() |
---|
| 586 | + * |
---|
| 587 | + * Returns: 0 if either the downstream port info was read successfully or |
---|
| 588 | + * there was no downstream info to read, or a negative error code otherwise. |
---|
| 589 | + */ |
---|
| 590 | +int drm_dp_read_downstream_info(struct drm_dp_aux *aux, |
---|
| 591 | + const u8 dpcd[DP_RECEIVER_CAP_SIZE], |
---|
| 592 | + u8 downstream_ports[DP_MAX_DOWNSTREAM_PORTS]) |
---|
| 593 | +{ |
---|
| 594 | + int ret; |
---|
| 595 | + u8 len; |
---|
| 596 | + |
---|
| 597 | + memset(downstream_ports, 0, DP_MAX_DOWNSTREAM_PORTS); |
---|
| 598 | + |
---|
| 599 | + /* No downstream info to read */ |
---|
| 600 | + if (!drm_dp_is_branch(dpcd) || |
---|
| 601 | + dpcd[DP_DPCD_REV] < DP_DPCD_REV_10 || |
---|
| 602 | + !(dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_PRESENT)) |
---|
| 603 | + return 0; |
---|
| 604 | + |
---|
| 605 | + /* Some branches advertise having 0 downstream ports, despite also advertising they have a |
---|
| 606 | + * downstream port present. The DP spec isn't clear on if this is allowed or not, but since |
---|
| 607 | + * some branches do it we need to handle it regardless. |
---|
| 608 | + */ |
---|
| 609 | + len = drm_dp_downstream_port_count(dpcd); |
---|
| 610 | + if (!len) |
---|
| 611 | + return 0; |
---|
| 612 | + |
---|
| 613 | + if (dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE) |
---|
| 614 | + len *= 4; |
---|
| 615 | + |
---|
| 616 | + ret = drm_dp_dpcd_read(aux, DP_DOWNSTREAM_PORT_0, downstream_ports, len); |
---|
| 617 | + if (ret < 0) |
---|
| 618 | + return ret; |
---|
| 619 | + if (ret != len) |
---|
| 620 | + return -EIO; |
---|
| 621 | + |
---|
| 622 | + DRM_DEBUG_KMS("%s: DPCD DFP: %*ph\n", |
---|
| 623 | + aux->name, len, downstream_ports); |
---|
481 | 624 | |
---|
482 | 625 | return 0; |
---|
483 | 626 | } |
---|
484 | | -EXPORT_SYMBOL(drm_dp_link_configure); |
---|
| 627 | +EXPORT_SYMBOL(drm_dp_read_downstream_info); |
---|
485 | 628 | |
---|
486 | 629 | /** |
---|
487 | | - * drm_dp_downstream_max_clock() - extract branch device max |
---|
488 | | - * pixel rate for legacy VGA |
---|
489 | | - * converter or max TMDS clock |
---|
490 | | - * rate for others |
---|
| 630 | + * drm_dp_downstream_max_dotclock() - extract downstream facing port max dot clock |
---|
491 | 631 | * @dpcd: DisplayPort configuration data |
---|
492 | 632 | * @port_cap: port capabilities |
---|
493 | 633 | * |
---|
494 | | - * Returns max clock in kHz on success or 0 if max clock not defined |
---|
| 634 | + * Returns: Downstream facing port max dot clock in kHz on success, |
---|
| 635 | + * or 0 if max clock not defined |
---|
495 | 636 | */ |
---|
496 | | -int drm_dp_downstream_max_clock(const u8 dpcd[DP_RECEIVER_CAP_SIZE], |
---|
497 | | - const u8 port_cap[4]) |
---|
| 637 | +int drm_dp_downstream_max_dotclock(const u8 dpcd[DP_RECEIVER_CAP_SIZE], |
---|
| 638 | + const u8 port_cap[4]) |
---|
498 | 639 | { |
---|
499 | | - int type = port_cap[0] & DP_DS_PORT_TYPE_MASK; |
---|
500 | | - bool detailed_cap_info = dpcd[DP_DOWNSTREAMPORT_PRESENT] & |
---|
501 | | - DP_DETAILED_CAP_INFO_AVAILABLE; |
---|
502 | | - |
---|
503 | | - if (!detailed_cap_info) |
---|
| 640 | + if (!drm_dp_is_branch(dpcd)) |
---|
504 | 641 | return 0; |
---|
505 | 642 | |
---|
506 | | - switch (type) { |
---|
| 643 | + if (dpcd[DP_DPCD_REV] < 0x11) |
---|
| 644 | + return 0; |
---|
| 645 | + |
---|
| 646 | + switch (port_cap[0] & DP_DS_PORT_TYPE_MASK) { |
---|
507 | 647 | case DP_DS_PORT_TYPE_VGA: |
---|
508 | | - return port_cap[1] * 8 * 1000; |
---|
509 | | - case DP_DS_PORT_TYPE_DVI: |
---|
510 | | - case DP_DS_PORT_TYPE_HDMI: |
---|
| 648 | + if ((dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE) == 0) |
---|
| 649 | + return 0; |
---|
| 650 | + return port_cap[1] * 8000; |
---|
| 651 | + default: |
---|
| 652 | + return 0; |
---|
| 653 | + } |
---|
| 654 | +} |
---|
| 655 | +EXPORT_SYMBOL(drm_dp_downstream_max_dotclock); |
---|
| 656 | + |
---|
| 657 | +/** |
---|
| 658 | + * drm_dp_downstream_max_tmds_clock() - extract downstream facing port max TMDS clock |
---|
| 659 | + * @dpcd: DisplayPort configuration data |
---|
| 660 | + * @port_cap: port capabilities |
---|
| 661 | + * @edid: EDID |
---|
| 662 | + * |
---|
| 663 | + * Returns: HDMI/DVI downstream facing port max TMDS clock in kHz on success, |
---|
| 664 | + * or 0 if max TMDS clock not defined |
---|
| 665 | + */ |
---|
| 666 | +int drm_dp_downstream_max_tmds_clock(const u8 dpcd[DP_RECEIVER_CAP_SIZE], |
---|
| 667 | + const u8 port_cap[4], |
---|
| 668 | + const struct edid *edid) |
---|
| 669 | +{ |
---|
| 670 | + if (!drm_dp_is_branch(dpcd)) |
---|
| 671 | + return 0; |
---|
| 672 | + |
---|
| 673 | + if (dpcd[DP_DPCD_REV] < 0x11) { |
---|
| 674 | + switch (dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_TYPE_MASK) { |
---|
| 675 | + case DP_DWN_STRM_PORT_TYPE_TMDS: |
---|
| 676 | + return 165000; |
---|
| 677 | + default: |
---|
| 678 | + return 0; |
---|
| 679 | + } |
---|
| 680 | + } |
---|
| 681 | + |
---|
| 682 | + switch (port_cap[0] & DP_DS_PORT_TYPE_MASK) { |
---|
511 | 683 | case DP_DS_PORT_TYPE_DP_DUALMODE: |
---|
| 684 | + if (is_edid_digital_input_dp(edid)) |
---|
| 685 | + return 0; |
---|
| 686 | + /* |
---|
| 687 | + * It's left up to the driver to check the |
---|
| 688 | + * DP dual mode adapter's max TMDS clock. |
---|
| 689 | + * |
---|
| 690 | + * Unfortunatley it looks like branch devices |
---|
| 691 | + * may not fordward that the DP dual mode i2c |
---|
| 692 | + * access so we just usually get i2c nak :( |
---|
| 693 | + */ |
---|
| 694 | + fallthrough; |
---|
| 695 | + case DP_DS_PORT_TYPE_HDMI: |
---|
| 696 | + /* |
---|
| 697 | + * We should perhaps assume 165 MHz when detailed cap |
---|
| 698 | + * info is not available. But looks like many typical |
---|
| 699 | + * branch devices fall into that category and so we'd |
---|
| 700 | + * probably end up with users complaining that they can't |
---|
| 701 | + * get high resolution modes with their favorite dongle. |
---|
| 702 | + * |
---|
| 703 | + * So let's limit to 300 MHz instead since DPCD 1.4 |
---|
| 704 | + * HDMI 2.0 DFPs are required to have the detailed cap |
---|
| 705 | + * info. So it's more likely we're dealing with a HDMI 1.4 |
---|
| 706 | + * compatible* device here. |
---|
| 707 | + */ |
---|
| 708 | + if ((dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE) == 0) |
---|
| 709 | + return 300000; |
---|
| 710 | + return port_cap[1] * 2500; |
---|
| 711 | + case DP_DS_PORT_TYPE_DVI: |
---|
| 712 | + if ((dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE) == 0) |
---|
| 713 | + return 165000; |
---|
| 714 | + /* FIXME what to do about DVI dual link? */ |
---|
512 | 715 | return port_cap[1] * 2500; |
---|
513 | 716 | default: |
---|
514 | 717 | return 0; |
---|
515 | 718 | } |
---|
516 | 719 | } |
---|
517 | | -EXPORT_SYMBOL(drm_dp_downstream_max_clock); |
---|
| 720 | +EXPORT_SYMBOL(drm_dp_downstream_max_tmds_clock); |
---|
518 | 721 | |
---|
519 | 722 | /** |
---|
520 | | - * drm_dp_downstream_max_bpc() - extract branch device max |
---|
521 | | - * bits per component |
---|
| 723 | + * drm_dp_downstream_min_tmds_clock() - extract downstream facing port min TMDS clock |
---|
522 | 724 | * @dpcd: DisplayPort configuration data |
---|
523 | 725 | * @port_cap: port capabilities |
---|
| 726 | + * @edid: EDID |
---|
524 | 727 | * |
---|
525 | | - * Returns max bpc on success or 0 if max bpc not defined |
---|
| 728 | + * Returns: HDMI/DVI downstream facing port min TMDS clock in kHz on success, |
---|
| 729 | + * or 0 if max TMDS clock not defined |
---|
526 | 730 | */ |
---|
527 | | -int drm_dp_downstream_max_bpc(const u8 dpcd[DP_RECEIVER_CAP_SIZE], |
---|
528 | | - const u8 port_cap[4]) |
---|
| 731 | +int drm_dp_downstream_min_tmds_clock(const u8 dpcd[DP_RECEIVER_CAP_SIZE], |
---|
| 732 | + const u8 port_cap[4], |
---|
| 733 | + const struct edid *edid) |
---|
529 | 734 | { |
---|
530 | | - int type = port_cap[0] & DP_DS_PORT_TYPE_MASK; |
---|
531 | | - bool detailed_cap_info = dpcd[DP_DOWNSTREAMPORT_PRESENT] & |
---|
532 | | - DP_DETAILED_CAP_INFO_AVAILABLE; |
---|
533 | | - int bpc; |
---|
534 | | - |
---|
535 | | - if (!detailed_cap_info) |
---|
| 735 | + if (!drm_dp_is_branch(dpcd)) |
---|
536 | 736 | return 0; |
---|
537 | 737 | |
---|
538 | | - switch (type) { |
---|
539 | | - case DP_DS_PORT_TYPE_VGA: |
---|
| 738 | + if (dpcd[DP_DPCD_REV] < 0x11) { |
---|
| 739 | + switch (dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_TYPE_MASK) { |
---|
| 740 | + case DP_DWN_STRM_PORT_TYPE_TMDS: |
---|
| 741 | + return 25000; |
---|
| 742 | + default: |
---|
| 743 | + return 0; |
---|
| 744 | + } |
---|
| 745 | + } |
---|
| 746 | + |
---|
| 747 | + switch (port_cap[0] & DP_DS_PORT_TYPE_MASK) { |
---|
| 748 | + case DP_DS_PORT_TYPE_DP_DUALMODE: |
---|
| 749 | + if (is_edid_digital_input_dp(edid)) |
---|
| 750 | + return 0; |
---|
| 751 | + fallthrough; |
---|
540 | 752 | case DP_DS_PORT_TYPE_DVI: |
---|
541 | 753 | case DP_DS_PORT_TYPE_HDMI: |
---|
542 | | - case DP_DS_PORT_TYPE_DP_DUALMODE: |
---|
543 | | - bpc = port_cap[2] & DP_DS_MAX_BPC_MASK; |
---|
| 754 | + /* |
---|
| 755 | + * Unclear whether the protocol converter could |
---|
| 756 | + * utilize pixel replication. Assume it won't. |
---|
| 757 | + */ |
---|
| 758 | + return 25000; |
---|
| 759 | + default: |
---|
| 760 | + return 0; |
---|
| 761 | + } |
---|
| 762 | +} |
---|
| 763 | +EXPORT_SYMBOL(drm_dp_downstream_min_tmds_clock); |
---|
544 | 764 | |
---|
545 | | - switch (bpc) { |
---|
| 765 | +/** |
---|
| 766 | + * drm_dp_downstream_max_bpc() - extract downstream facing port max |
---|
| 767 | + * bits per component |
---|
| 768 | + * @dpcd: DisplayPort configuration data |
---|
| 769 | + * @port_cap: downstream facing port capabilities |
---|
| 770 | + * @edid: EDID |
---|
| 771 | + * |
---|
| 772 | + * Returns: Max bpc on success or 0 if max bpc not defined |
---|
| 773 | + */ |
---|
| 774 | +int drm_dp_downstream_max_bpc(const u8 dpcd[DP_RECEIVER_CAP_SIZE], |
---|
| 775 | + const u8 port_cap[4], |
---|
| 776 | + const struct edid *edid) |
---|
| 777 | +{ |
---|
| 778 | + if (!drm_dp_is_branch(dpcd)) |
---|
| 779 | + return 0; |
---|
| 780 | + |
---|
| 781 | + if (dpcd[DP_DPCD_REV] < 0x11) { |
---|
| 782 | + switch (dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_TYPE_MASK) { |
---|
| 783 | + case DP_DWN_STRM_PORT_TYPE_DP: |
---|
| 784 | + return 0; |
---|
| 785 | + default: |
---|
| 786 | + return 8; |
---|
| 787 | + } |
---|
| 788 | + } |
---|
| 789 | + |
---|
| 790 | + switch (port_cap[0] & DP_DS_PORT_TYPE_MASK) { |
---|
| 791 | + case DP_DS_PORT_TYPE_DP: |
---|
| 792 | + return 0; |
---|
| 793 | + case DP_DS_PORT_TYPE_DP_DUALMODE: |
---|
| 794 | + if (is_edid_digital_input_dp(edid)) |
---|
| 795 | + return 0; |
---|
| 796 | + fallthrough; |
---|
| 797 | + case DP_DS_PORT_TYPE_HDMI: |
---|
| 798 | + case DP_DS_PORT_TYPE_DVI: |
---|
| 799 | + case DP_DS_PORT_TYPE_VGA: |
---|
| 800 | + if ((dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE) == 0) |
---|
| 801 | + return 8; |
---|
| 802 | + |
---|
| 803 | + switch (port_cap[2] & DP_DS_MAX_BPC_MASK) { |
---|
546 | 804 | case DP_DS_8BPC: |
---|
547 | 805 | return 8; |
---|
548 | 806 | case DP_DS_10BPC: |
---|
.. | .. |
---|
551 | 809 | return 12; |
---|
552 | 810 | case DP_DS_16BPC: |
---|
553 | 811 | return 16; |
---|
| 812 | + default: |
---|
| 813 | + return 8; |
---|
554 | 814 | } |
---|
| 815 | + break; |
---|
555 | 816 | default: |
---|
556 | | - return 0; |
---|
| 817 | + return 8; |
---|
557 | 818 | } |
---|
558 | 819 | } |
---|
559 | 820 | EXPORT_SYMBOL(drm_dp_downstream_max_bpc); |
---|
| 821 | + |
---|
| 822 | +/** |
---|
| 823 | + * drm_dp_downstream_420_passthrough() - determine downstream facing port |
---|
| 824 | + * YCbCr 4:2:0 pass-through capability |
---|
| 825 | + * @dpcd: DisplayPort configuration data |
---|
| 826 | + * @port_cap: downstream facing port capabilities |
---|
| 827 | + * |
---|
| 828 | + * Returns: whether the downstream facing port can pass through YCbCr 4:2:0 |
---|
| 829 | + */ |
---|
| 830 | +bool drm_dp_downstream_420_passthrough(const u8 dpcd[DP_RECEIVER_CAP_SIZE], |
---|
| 831 | + const u8 port_cap[4]) |
---|
| 832 | +{ |
---|
| 833 | + if (!drm_dp_is_branch(dpcd)) |
---|
| 834 | + return false; |
---|
| 835 | + |
---|
| 836 | + if (dpcd[DP_DPCD_REV] < 0x13) |
---|
| 837 | + return false; |
---|
| 838 | + |
---|
| 839 | + switch (port_cap[0] & DP_DS_PORT_TYPE_MASK) { |
---|
| 840 | + case DP_DS_PORT_TYPE_DP: |
---|
| 841 | + return true; |
---|
| 842 | + case DP_DS_PORT_TYPE_HDMI: |
---|
| 843 | + if ((dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE) == 0) |
---|
| 844 | + return false; |
---|
| 845 | + |
---|
| 846 | + return port_cap[3] & DP_DS_HDMI_YCBCR420_PASS_THROUGH; |
---|
| 847 | + default: |
---|
| 848 | + return false; |
---|
| 849 | + } |
---|
| 850 | +} |
---|
| 851 | +EXPORT_SYMBOL(drm_dp_downstream_420_passthrough); |
---|
| 852 | + |
---|
| 853 | +/** |
---|
| 854 | + * drm_dp_downstream_444_to_420_conversion() - determine downstream facing port |
---|
| 855 | + * YCbCr 4:4:4->4:2:0 conversion capability |
---|
| 856 | + * @dpcd: DisplayPort configuration data |
---|
| 857 | + * @port_cap: downstream facing port capabilities |
---|
| 858 | + * |
---|
| 859 | + * Returns: whether the downstream facing port can convert YCbCr 4:4:4 to 4:2:0 |
---|
| 860 | + */ |
---|
| 861 | +bool drm_dp_downstream_444_to_420_conversion(const u8 dpcd[DP_RECEIVER_CAP_SIZE], |
---|
| 862 | + const u8 port_cap[4]) |
---|
| 863 | +{ |
---|
| 864 | + if (!drm_dp_is_branch(dpcd)) |
---|
| 865 | + return false; |
---|
| 866 | + |
---|
| 867 | + if (dpcd[DP_DPCD_REV] < 0x13) |
---|
| 868 | + return false; |
---|
| 869 | + |
---|
| 870 | + switch (port_cap[0] & DP_DS_PORT_TYPE_MASK) { |
---|
| 871 | + case DP_DS_PORT_TYPE_HDMI: |
---|
| 872 | + if ((dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE) == 0) |
---|
| 873 | + return false; |
---|
| 874 | + |
---|
| 875 | + return port_cap[3] & DP_DS_HDMI_YCBCR444_TO_420_CONV; |
---|
| 876 | + default: |
---|
| 877 | + return false; |
---|
| 878 | + } |
---|
| 879 | +} |
---|
| 880 | +EXPORT_SYMBOL(drm_dp_downstream_444_to_420_conversion); |
---|
| 881 | + |
---|
| 882 | +/** |
---|
| 883 | + * drm_dp_downstream_mode() - return a mode for downstream facing port |
---|
| 884 | + * @dev: DRM device |
---|
| 885 | + * @dpcd: DisplayPort configuration data |
---|
| 886 | + * @port_cap: port capabilities |
---|
| 887 | + * |
---|
| 888 | + * Provides a suitable mode for downstream facing ports without EDID. |
---|
| 889 | + * |
---|
| 890 | + * Returns: A new drm_display_mode on success or NULL on failure |
---|
| 891 | + */ |
---|
| 892 | +struct drm_display_mode * |
---|
| 893 | +drm_dp_downstream_mode(struct drm_device *dev, |
---|
| 894 | + const u8 dpcd[DP_RECEIVER_CAP_SIZE], |
---|
| 895 | + const u8 port_cap[4]) |
---|
| 896 | + |
---|
| 897 | +{ |
---|
| 898 | + u8 vic; |
---|
| 899 | + |
---|
| 900 | + if (!drm_dp_is_branch(dpcd)) |
---|
| 901 | + return NULL; |
---|
| 902 | + |
---|
| 903 | + if (dpcd[DP_DPCD_REV] < 0x11) |
---|
| 904 | + return NULL; |
---|
| 905 | + |
---|
| 906 | + switch (port_cap[0] & DP_DS_PORT_TYPE_MASK) { |
---|
| 907 | + case DP_DS_PORT_TYPE_NON_EDID: |
---|
| 908 | + switch (port_cap[0] & DP_DS_NON_EDID_MASK) { |
---|
| 909 | + case DP_DS_NON_EDID_720x480i_60: |
---|
| 910 | + vic = 6; |
---|
| 911 | + break; |
---|
| 912 | + case DP_DS_NON_EDID_720x480i_50: |
---|
| 913 | + vic = 21; |
---|
| 914 | + break; |
---|
| 915 | + case DP_DS_NON_EDID_1920x1080i_60: |
---|
| 916 | + vic = 5; |
---|
| 917 | + break; |
---|
| 918 | + case DP_DS_NON_EDID_1920x1080i_50: |
---|
| 919 | + vic = 20; |
---|
| 920 | + break; |
---|
| 921 | + case DP_DS_NON_EDID_1280x720_60: |
---|
| 922 | + vic = 4; |
---|
| 923 | + break; |
---|
| 924 | + case DP_DS_NON_EDID_1280x720_50: |
---|
| 925 | + vic = 19; |
---|
| 926 | + break; |
---|
| 927 | + default: |
---|
| 928 | + return NULL; |
---|
| 929 | + } |
---|
| 930 | + return drm_display_mode_from_cea_vic(dev, vic); |
---|
| 931 | + default: |
---|
| 932 | + return NULL; |
---|
| 933 | + } |
---|
| 934 | +} |
---|
| 935 | +EXPORT_SYMBOL(drm_dp_downstream_mode); |
---|
560 | 936 | |
---|
561 | 937 | /** |
---|
562 | 938 | * drm_dp_downstream_id() - identify branch device |
---|
.. | .. |
---|
576 | 952 | * @m: pointer for debugfs file |
---|
577 | 953 | * @dpcd: DisplayPort configuration data |
---|
578 | 954 | * @port_cap: port capabilities |
---|
| 955 | + * @edid: EDID |
---|
579 | 956 | * @aux: DisplayPort AUX channel |
---|
580 | 957 | * |
---|
581 | 958 | */ |
---|
582 | 959 | void drm_dp_downstream_debug(struct seq_file *m, |
---|
583 | 960 | const u8 dpcd[DP_RECEIVER_CAP_SIZE], |
---|
584 | | - const u8 port_cap[4], struct drm_dp_aux *aux) |
---|
| 961 | + const u8 port_cap[4], |
---|
| 962 | + const struct edid *edid, |
---|
| 963 | + struct drm_dp_aux *aux) |
---|
585 | 964 | { |
---|
586 | 965 | bool detailed_cap_info = dpcd[DP_DOWNSTREAMPORT_PRESENT] & |
---|
587 | 966 | DP_DETAILED_CAP_INFO_AVAILABLE; |
---|
.. | .. |
---|
591 | 970 | int len; |
---|
592 | 971 | uint8_t rev[2]; |
---|
593 | 972 | int type = port_cap[0] & DP_DS_PORT_TYPE_MASK; |
---|
594 | | - bool branch_device = dpcd[DP_DOWNSTREAMPORT_PRESENT] & |
---|
595 | | - DP_DWN_STRM_PORT_PRESENT; |
---|
| 973 | + bool branch_device = drm_dp_is_branch(dpcd); |
---|
596 | 974 | |
---|
597 | 975 | seq_printf(m, "\tDP branch device present: %s\n", |
---|
598 | 976 | branch_device ? "yes" : "no"); |
---|
.. | .. |
---|
640 | 1018 | seq_printf(m, "\t\tSW: %d.%d\n", rev[0], rev[1]); |
---|
641 | 1019 | |
---|
642 | 1020 | if (detailed_cap_info) { |
---|
643 | | - clk = drm_dp_downstream_max_clock(dpcd, port_cap); |
---|
| 1021 | + clk = drm_dp_downstream_max_dotclock(dpcd, port_cap); |
---|
| 1022 | + if (clk > 0) |
---|
| 1023 | + seq_printf(m, "\t\tMax dot clock: %d kHz\n", clk); |
---|
644 | 1024 | |
---|
645 | | - if (clk > 0) { |
---|
646 | | - if (type == DP_DS_PORT_TYPE_VGA) |
---|
647 | | - seq_printf(m, "\t\tMax dot clock: %d kHz\n", clk); |
---|
648 | | - else |
---|
649 | | - seq_printf(m, "\t\tMax TMDS clock: %d kHz\n", clk); |
---|
650 | | - } |
---|
| 1025 | + clk = drm_dp_downstream_max_tmds_clock(dpcd, port_cap, edid); |
---|
| 1026 | + if (clk > 0) |
---|
| 1027 | + seq_printf(m, "\t\tMax TMDS clock: %d kHz\n", clk); |
---|
651 | 1028 | |
---|
652 | | - bpc = drm_dp_downstream_max_bpc(dpcd, port_cap); |
---|
| 1029 | + clk = drm_dp_downstream_min_tmds_clock(dpcd, port_cap, edid); |
---|
| 1030 | + if (clk > 0) |
---|
| 1031 | + seq_printf(m, "\t\tMin TMDS clock: %d kHz\n", clk); |
---|
| 1032 | + |
---|
| 1033 | + bpc = drm_dp_downstream_max_bpc(dpcd, port_cap, edid); |
---|
653 | 1034 | |
---|
654 | 1035 | if (bpc > 0) |
---|
655 | 1036 | seq_printf(m, "\t\tMax bpc: %d\n", bpc); |
---|
656 | 1037 | } |
---|
657 | 1038 | } |
---|
658 | 1039 | EXPORT_SYMBOL(drm_dp_downstream_debug); |
---|
| 1040 | + |
---|
| 1041 | +/** |
---|
| 1042 | + * drm_dp_subconnector_type() - get DP branch device type |
---|
| 1043 | + * @dpcd: DisplayPort configuration data |
---|
| 1044 | + * @port_cap: port capabilities |
---|
| 1045 | + */ |
---|
| 1046 | +enum drm_mode_subconnector |
---|
| 1047 | +drm_dp_subconnector_type(const u8 dpcd[DP_RECEIVER_CAP_SIZE], |
---|
| 1048 | + const u8 port_cap[4]) |
---|
| 1049 | +{ |
---|
| 1050 | + int type; |
---|
| 1051 | + if (!drm_dp_is_branch(dpcd)) |
---|
| 1052 | + return DRM_MODE_SUBCONNECTOR_Native; |
---|
| 1053 | + /* DP 1.0 approach */ |
---|
| 1054 | + if (dpcd[DP_DPCD_REV] == DP_DPCD_REV_10) { |
---|
| 1055 | + type = dpcd[DP_DOWNSTREAMPORT_PRESENT] & |
---|
| 1056 | + DP_DWN_STRM_PORT_TYPE_MASK; |
---|
| 1057 | + |
---|
| 1058 | + switch (type) { |
---|
| 1059 | + case DP_DWN_STRM_PORT_TYPE_TMDS: |
---|
| 1060 | + /* Can be HDMI or DVI-D, DVI-D is a safer option */ |
---|
| 1061 | + return DRM_MODE_SUBCONNECTOR_DVID; |
---|
| 1062 | + case DP_DWN_STRM_PORT_TYPE_ANALOG: |
---|
| 1063 | + /* Can be VGA or DVI-A, VGA is more popular */ |
---|
| 1064 | + return DRM_MODE_SUBCONNECTOR_VGA; |
---|
| 1065 | + case DP_DWN_STRM_PORT_TYPE_DP: |
---|
| 1066 | + return DRM_MODE_SUBCONNECTOR_DisplayPort; |
---|
| 1067 | + case DP_DWN_STRM_PORT_TYPE_OTHER: |
---|
| 1068 | + default: |
---|
| 1069 | + return DRM_MODE_SUBCONNECTOR_Unknown; |
---|
| 1070 | + } |
---|
| 1071 | + } |
---|
| 1072 | + type = port_cap[0] & DP_DS_PORT_TYPE_MASK; |
---|
| 1073 | + |
---|
| 1074 | + switch (type) { |
---|
| 1075 | + case DP_DS_PORT_TYPE_DP: |
---|
| 1076 | + case DP_DS_PORT_TYPE_DP_DUALMODE: |
---|
| 1077 | + return DRM_MODE_SUBCONNECTOR_DisplayPort; |
---|
| 1078 | + case DP_DS_PORT_TYPE_VGA: |
---|
| 1079 | + return DRM_MODE_SUBCONNECTOR_VGA; |
---|
| 1080 | + case DP_DS_PORT_TYPE_DVI: |
---|
| 1081 | + return DRM_MODE_SUBCONNECTOR_DVID; |
---|
| 1082 | + case DP_DS_PORT_TYPE_HDMI: |
---|
| 1083 | + return DRM_MODE_SUBCONNECTOR_HDMIA; |
---|
| 1084 | + case DP_DS_PORT_TYPE_WIRELESS: |
---|
| 1085 | + return DRM_MODE_SUBCONNECTOR_Wireless; |
---|
| 1086 | + case DP_DS_PORT_TYPE_NON_EDID: |
---|
| 1087 | + default: |
---|
| 1088 | + return DRM_MODE_SUBCONNECTOR_Unknown; |
---|
| 1089 | + } |
---|
| 1090 | +} |
---|
| 1091 | +EXPORT_SYMBOL(drm_dp_subconnector_type); |
---|
| 1092 | + |
---|
| 1093 | +/** |
---|
| 1094 | + * drm_mode_set_dp_subconnector_property - set subconnector for DP connector |
---|
| 1095 | + * @connector: connector to set property on |
---|
| 1096 | + * @status: connector status |
---|
| 1097 | + * @dpcd: DisplayPort configuration data |
---|
| 1098 | + * @port_cap: port capabilities |
---|
| 1099 | + * |
---|
| 1100 | + * Called by a driver on every detect event. |
---|
| 1101 | + */ |
---|
| 1102 | +void drm_dp_set_subconnector_property(struct drm_connector *connector, |
---|
| 1103 | + enum drm_connector_status status, |
---|
| 1104 | + const u8 *dpcd, |
---|
| 1105 | + const u8 port_cap[4]) |
---|
| 1106 | +{ |
---|
| 1107 | + enum drm_mode_subconnector subconnector = DRM_MODE_SUBCONNECTOR_Unknown; |
---|
| 1108 | + |
---|
| 1109 | + if (status == connector_status_connected) |
---|
| 1110 | + subconnector = drm_dp_subconnector_type(dpcd, port_cap); |
---|
| 1111 | + drm_object_property_set_value(&connector->base, |
---|
| 1112 | + connector->dev->mode_config.dp_subconnector_property, |
---|
| 1113 | + subconnector); |
---|
| 1114 | +} |
---|
| 1115 | +EXPORT_SYMBOL(drm_dp_set_subconnector_property); |
---|
| 1116 | + |
---|
| 1117 | +/** |
---|
| 1118 | + * drm_dp_read_sink_count_cap() - Check whether a given connector has a valid sink |
---|
| 1119 | + * count |
---|
| 1120 | + * @connector: The DRM connector to check |
---|
| 1121 | + * @dpcd: A cached copy of the connector's DPCD RX capabilities |
---|
| 1122 | + * @desc: A cached copy of the connector's DP descriptor |
---|
| 1123 | + * |
---|
| 1124 | + * See also: drm_dp_read_sink_count() |
---|
| 1125 | + * |
---|
| 1126 | + * Returns: %True if the (e)DP connector has a valid sink count that should |
---|
| 1127 | + * be probed, %false otherwise. |
---|
| 1128 | + */ |
---|
| 1129 | +bool drm_dp_read_sink_count_cap(struct drm_connector *connector, |
---|
| 1130 | + const u8 dpcd[DP_RECEIVER_CAP_SIZE], |
---|
| 1131 | + const struct drm_dp_desc *desc) |
---|
| 1132 | +{ |
---|
| 1133 | + /* Some eDP panels don't set a valid value for the sink count */ |
---|
| 1134 | + return connector->connector_type != DRM_MODE_CONNECTOR_eDP && |
---|
| 1135 | + dpcd[DP_DPCD_REV] >= DP_DPCD_REV_11 && |
---|
| 1136 | + dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_PRESENT && |
---|
| 1137 | + !drm_dp_has_quirk(desc, 0, DP_DPCD_QUIRK_NO_SINK_COUNT); |
---|
| 1138 | +} |
---|
| 1139 | +EXPORT_SYMBOL(drm_dp_read_sink_count_cap); |
---|
| 1140 | + |
---|
| 1141 | +/** |
---|
| 1142 | + * drm_dp_read_sink_count() - Retrieve the sink count for a given sink |
---|
| 1143 | + * @aux: The DP AUX channel to use |
---|
| 1144 | + * |
---|
| 1145 | + * See also: drm_dp_read_sink_count_cap() |
---|
| 1146 | + * |
---|
| 1147 | + * Returns: The current sink count reported by @aux, or a negative error code |
---|
| 1148 | + * otherwise. |
---|
| 1149 | + */ |
---|
| 1150 | +int drm_dp_read_sink_count(struct drm_dp_aux *aux) |
---|
| 1151 | +{ |
---|
| 1152 | + u8 count; |
---|
| 1153 | + int ret; |
---|
| 1154 | + |
---|
| 1155 | + ret = drm_dp_dpcd_readb(aux, DP_SINK_COUNT, &count); |
---|
| 1156 | + if (ret < 0) |
---|
| 1157 | + return ret; |
---|
| 1158 | + if (ret != 1) |
---|
| 1159 | + return -EIO; |
---|
| 1160 | + |
---|
| 1161 | + return DP_GET_SINK_COUNT(count); |
---|
| 1162 | +} |
---|
| 1163 | +EXPORT_SYMBOL(drm_dp_read_sink_count); |
---|
659 | 1164 | |
---|
660 | 1165 | /* |
---|
661 | 1166 | * I2C-over-AUX implementation |
---|
.. | .. |
---|
800 | 1305 | * Avoid spamming the kernel log with timeout errors. |
---|
801 | 1306 | */ |
---|
802 | 1307 | if (ret == -ETIMEDOUT) |
---|
803 | | - DRM_DEBUG_KMS_RATELIMITED("transaction timed out\n"); |
---|
| 1308 | + DRM_DEBUG_KMS_RATELIMITED("%s: transaction timed out\n", |
---|
| 1309 | + aux->name); |
---|
804 | 1310 | else |
---|
805 | | - DRM_DEBUG_KMS("transaction failed: %d\n", ret); |
---|
806 | | - |
---|
| 1311 | + DRM_DEBUG_KMS("%s: transaction failed: %d\n", |
---|
| 1312 | + aux->name, ret); |
---|
807 | 1313 | return ret; |
---|
808 | 1314 | } |
---|
809 | 1315 | |
---|
.. | .. |
---|
817 | 1323 | break; |
---|
818 | 1324 | |
---|
819 | 1325 | case DP_AUX_NATIVE_REPLY_NACK: |
---|
820 | | - DRM_DEBUG_KMS("native nack (result=%d, size=%zu)\n", ret, msg->size); |
---|
| 1326 | + DRM_DEBUG_KMS("%s: native nack (result=%d, size=%zu)\n", |
---|
| 1327 | + aux->name, ret, msg->size); |
---|
821 | 1328 | return -EREMOTEIO; |
---|
822 | 1329 | |
---|
823 | 1330 | case DP_AUX_NATIVE_REPLY_DEFER: |
---|
824 | | - DRM_DEBUG_KMS("native defer\n"); |
---|
| 1331 | + DRM_DEBUG_KMS("%s: native defer\n", aux->name); |
---|
825 | 1332 | /* |
---|
826 | 1333 | * We could check for I2C bit rate capabilities and if |
---|
827 | 1334 | * available adjust this interval. We could also be |
---|
.. | .. |
---|
835 | 1342 | continue; |
---|
836 | 1343 | |
---|
837 | 1344 | default: |
---|
838 | | - DRM_ERROR("invalid native reply %#04x\n", msg->reply); |
---|
| 1345 | + DRM_ERROR("%s: invalid native reply %#04x\n", |
---|
| 1346 | + aux->name, msg->reply); |
---|
839 | 1347 | return -EREMOTEIO; |
---|
840 | 1348 | } |
---|
841 | 1349 | |
---|
.. | .. |
---|
850 | 1358 | return ret; |
---|
851 | 1359 | |
---|
852 | 1360 | case DP_AUX_I2C_REPLY_NACK: |
---|
853 | | - DRM_DEBUG_KMS("I2C nack (result=%d, size=%zu\n", ret, msg->size); |
---|
| 1361 | + DRM_DEBUG_KMS("%s: I2C nack (result=%d, size=%zu)\n", |
---|
| 1362 | + aux->name, ret, msg->size); |
---|
854 | 1363 | aux->i2c_nack_count++; |
---|
855 | 1364 | return -EREMOTEIO; |
---|
856 | 1365 | |
---|
857 | 1366 | case DP_AUX_I2C_REPLY_DEFER: |
---|
858 | | - DRM_DEBUG_KMS("I2C defer\n"); |
---|
| 1367 | + DRM_DEBUG_KMS("%s: I2C defer\n", aux->name); |
---|
859 | 1368 | /* DP Compliance Test 4.2.2.5 Requirement: |
---|
860 | 1369 | * Must have at least 7 retries for I2C defers on the |
---|
861 | 1370 | * transaction to pass this test |
---|
.. | .. |
---|
869 | 1378 | continue; |
---|
870 | 1379 | |
---|
871 | 1380 | default: |
---|
872 | | - DRM_ERROR("invalid I2C reply %#04x\n", msg->reply); |
---|
| 1381 | + DRM_ERROR("%s: invalid I2C reply %#04x\n", |
---|
| 1382 | + aux->name, msg->reply); |
---|
873 | 1383 | return -EREMOTEIO; |
---|
874 | 1384 | } |
---|
875 | 1385 | } |
---|
876 | 1386 | |
---|
877 | | - DRM_DEBUG_KMS("too many retries, giving up\n"); |
---|
| 1387 | + DRM_DEBUG_KMS("%s: Too many retries, giving up\n", aux->name); |
---|
878 | 1388 | return -EREMOTEIO; |
---|
879 | 1389 | } |
---|
880 | 1390 | |
---|
.. | .. |
---|
883 | 1393 | { |
---|
884 | 1394 | msg->request = (i2c_msg->flags & I2C_M_RD) ? |
---|
885 | 1395 | DP_AUX_I2C_READ : DP_AUX_I2C_WRITE; |
---|
886 | | - msg->request |= DP_AUX_I2C_MOT; |
---|
| 1396 | + if (!(i2c_msg->flags & I2C_M_STOP)) |
---|
| 1397 | + msg->request |= DP_AUX_I2C_MOT; |
---|
887 | 1398 | } |
---|
888 | 1399 | |
---|
889 | 1400 | /* |
---|
.. | .. |
---|
902 | 1413 | return err == 0 ? -EPROTO : err; |
---|
903 | 1414 | |
---|
904 | 1415 | if (err < msg.size && err < ret) { |
---|
905 | | - DRM_DEBUG_KMS("Partial I2C reply: requested %zu bytes got %d bytes\n", |
---|
906 | | - msg.size, err); |
---|
| 1416 | + DRM_DEBUG_KMS("%s: Partial I2C reply: requested %zu bytes got %d bytes\n", |
---|
| 1417 | + aux->name, msg.size, err); |
---|
907 | 1418 | ret = err; |
---|
908 | 1419 | } |
---|
909 | 1420 | |
---|
.. | .. |
---|
1082 | 1593 | } |
---|
1083 | 1594 | |
---|
1084 | 1595 | if (ret == -EAGAIN) { |
---|
1085 | | - DRM_DEBUG_KMS("Get CRC failed after retrying: %d\n", |
---|
1086 | | - ret); |
---|
| 1596 | + DRM_DEBUG_KMS("%s: Get CRC failed after retrying: %d\n", |
---|
| 1597 | + aux->name, ret); |
---|
1087 | 1598 | continue; |
---|
1088 | 1599 | } else if (ret) { |
---|
1089 | | - DRM_DEBUG_KMS("Failed to get a CRC: %d\n", ret); |
---|
| 1600 | + DRM_DEBUG_KMS("%s: Failed to get a CRC: %d\n", |
---|
| 1601 | + aux->name, ret); |
---|
1090 | 1602 | continue; |
---|
1091 | 1603 | } |
---|
1092 | 1604 | |
---|
.. | .. |
---|
1096 | 1608 | drm_crtc_add_crc_entry(crtc, false, 0, crcs); |
---|
1097 | 1609 | } |
---|
1098 | 1610 | } |
---|
| 1611 | + |
---|
| 1612 | +/** |
---|
| 1613 | + * drm_dp_remote_aux_init() - minimally initialise a remote aux channel |
---|
| 1614 | + * @aux: DisplayPort AUX channel |
---|
| 1615 | + * |
---|
| 1616 | + * Used for remote aux channel in general. Merely initialize the crc work |
---|
| 1617 | + * struct. |
---|
| 1618 | + */ |
---|
| 1619 | +void drm_dp_remote_aux_init(struct drm_dp_aux *aux) |
---|
| 1620 | +{ |
---|
| 1621 | + INIT_WORK(&aux->crc_work, drm_dp_aux_crc_work); |
---|
| 1622 | +} |
---|
| 1623 | +EXPORT_SYMBOL(drm_dp_remote_aux_init); |
---|
1099 | 1624 | |
---|
1100 | 1625 | /** |
---|
1101 | 1626 | * drm_dp_aux_init() - minimally initialise an aux channel |
---|
.. | .. |
---|
1125 | 1650 | * @aux: DisplayPort AUX channel |
---|
1126 | 1651 | * |
---|
1127 | 1652 | * Automatically calls drm_dp_aux_init() if this hasn't been done yet. |
---|
| 1653 | + * This should only be called when the underlying &struct drm_connector is |
---|
| 1654 | + * initialiazed already. Therefore the best place to call this is from |
---|
| 1655 | + * &drm_connector_funcs.late_register. Not that drivers which don't follow this |
---|
| 1656 | + * will Oops when CONFIG_DRM_DP_AUX_CHARDEV is enabled. |
---|
| 1657 | + * |
---|
| 1658 | + * Drivers which need to use the aux channel before that point (e.g. at driver |
---|
| 1659 | + * load time, before drm_dev_register() has been called) need to call |
---|
| 1660 | + * drm_dp_aux_init(). |
---|
1128 | 1661 | * |
---|
1129 | 1662 | * Returns 0 on success or a negative error code on failure. |
---|
1130 | 1663 | */ |
---|
.. | .. |
---|
1256 | 1789 | |
---|
1257 | 1790 | struct dpcd_quirk { |
---|
1258 | 1791 | u8 oui[3]; |
---|
| 1792 | + u8 device_id[6]; |
---|
1259 | 1793 | bool is_branch; |
---|
1260 | 1794 | u32 quirks; |
---|
1261 | 1795 | }; |
---|
1262 | 1796 | |
---|
1263 | 1797 | #define OUI(first, second, third) { (first), (second), (third) } |
---|
| 1798 | +#define DEVICE_ID(first, second, third, fourth, fifth, sixth) \ |
---|
| 1799 | + { (first), (second), (third), (fourth), (fifth), (sixth) } |
---|
| 1800 | + |
---|
| 1801 | +#define DEVICE_ID_ANY DEVICE_ID(0, 0, 0, 0, 0, 0) |
---|
1264 | 1802 | |
---|
1265 | 1803 | static const struct dpcd_quirk dpcd_quirk_list[] = { |
---|
1266 | 1804 | /* Analogix 7737 needs reduced M and N at HBR2 link rates */ |
---|
1267 | | - { OUI(0x00, 0x22, 0xb9), true, BIT(DP_DPCD_QUIRK_LIMITED_M_N) }, |
---|
| 1805 | + { OUI(0x00, 0x22, 0xb9), DEVICE_ID_ANY, true, BIT(DP_DPCD_QUIRK_CONSTANT_N) }, |
---|
| 1806 | + /* LG LP140WF6-SPM1 eDP panel */ |
---|
| 1807 | + { OUI(0x00, 0x22, 0xb9), DEVICE_ID('s', 'i', 'v', 'a', 'r', 'T'), false, BIT(DP_DPCD_QUIRK_CONSTANT_N) }, |
---|
| 1808 | + /* Apple panels need some additional handling to support PSR */ |
---|
| 1809 | + { OUI(0x00, 0x10, 0xfa), DEVICE_ID_ANY, false, BIT(DP_DPCD_QUIRK_NO_PSR) }, |
---|
| 1810 | + /* CH7511 seems to leave SINK_COUNT zeroed */ |
---|
| 1811 | + { OUI(0x00, 0x00, 0x00), DEVICE_ID('C', 'H', '7', '5', '1', '1'), false, BIT(DP_DPCD_QUIRK_NO_SINK_COUNT) }, |
---|
| 1812 | + /* Synaptics DP1.4 MST hubs can support DSC without virtual DPCD */ |
---|
| 1813 | + { OUI(0x90, 0xCC, 0x24), DEVICE_ID_ANY, true, BIT(DP_DPCD_QUIRK_DSC_WITHOUT_VIRTUAL_DPCD) }, |
---|
| 1814 | + /* Apple MacBookPro 2017 15 inch eDP Retina panel reports too low DP_MAX_LINK_RATE */ |
---|
| 1815 | + { OUI(0x00, 0x10, 0xfa), DEVICE_ID(101, 68, 21, 101, 98, 97), false, BIT(DP_DPCD_QUIRK_CAN_DO_MAX_LINK_RATE_3_24_GBPS) }, |
---|
1268 | 1816 | }; |
---|
1269 | 1817 | |
---|
1270 | 1818 | #undef OUI |
---|
.. | .. |
---|
1283 | 1831 | const struct dpcd_quirk *quirk; |
---|
1284 | 1832 | u32 quirks = 0; |
---|
1285 | 1833 | int i; |
---|
| 1834 | + u8 any_device[] = DEVICE_ID_ANY; |
---|
1286 | 1835 | |
---|
1287 | 1836 | for (i = 0; i < ARRAY_SIZE(dpcd_quirk_list); i++) { |
---|
1288 | 1837 | quirk = &dpcd_quirk_list[i]; |
---|
.. | .. |
---|
1293 | 1842 | if (memcmp(quirk->oui, ident->oui, sizeof(ident->oui)) != 0) |
---|
1294 | 1843 | continue; |
---|
1295 | 1844 | |
---|
| 1845 | + if (memcmp(quirk->device_id, any_device, sizeof(any_device)) != 0 && |
---|
| 1846 | + memcmp(quirk->device_id, ident->device_id, sizeof(ident->device_id)) != 0) |
---|
| 1847 | + continue; |
---|
| 1848 | + |
---|
1296 | 1849 | quirks |= quirk->quirks; |
---|
1297 | 1850 | } |
---|
1298 | 1851 | |
---|
1299 | 1852 | return quirks; |
---|
1300 | 1853 | } |
---|
1301 | 1854 | |
---|
| 1855 | +#undef DEVICE_ID_ANY |
---|
| 1856 | +#undef DEVICE_ID |
---|
| 1857 | + |
---|
| 1858 | +struct edid_quirk { |
---|
| 1859 | + u8 mfg_id[2]; |
---|
| 1860 | + u8 prod_id[2]; |
---|
| 1861 | + u32 quirks; |
---|
| 1862 | +}; |
---|
| 1863 | + |
---|
| 1864 | +#define MFG(first, second) { (first), (second) } |
---|
| 1865 | +#define PROD_ID(first, second) { (first), (second) } |
---|
| 1866 | + |
---|
| 1867 | +/* |
---|
| 1868 | + * Some devices have unreliable OUIDs where they don't set the device ID |
---|
| 1869 | + * correctly, and as a result we need to use the EDID for finding additional |
---|
| 1870 | + * DP quirks in such cases. |
---|
| 1871 | + */ |
---|
| 1872 | +static const struct edid_quirk edid_quirk_list[] = { |
---|
| 1873 | + /* Optional 4K AMOLED panel in the ThinkPad X1 Extreme 2nd Generation |
---|
| 1874 | + * only supports DPCD backlight controls |
---|
| 1875 | + */ |
---|
| 1876 | + { MFG(0x4c, 0x83), PROD_ID(0x41, 0x41), BIT(DP_QUIRK_FORCE_DPCD_BACKLIGHT) }, |
---|
| 1877 | + /* |
---|
| 1878 | + * Some Dell CML 2020 systems have panels support both AUX and PWM |
---|
| 1879 | + * backlight control, and some only support AUX backlight control. All |
---|
| 1880 | + * said panels start up in AUX mode by default, and we don't have any |
---|
| 1881 | + * support for disabling HDR mode on these panels which would be |
---|
| 1882 | + * required to switch to PWM backlight control mode (plus, I'm not |
---|
| 1883 | + * even sure we want PWM backlight controls over DPCD backlight |
---|
| 1884 | + * controls anyway...). Until we have a better way of detecting these, |
---|
| 1885 | + * force DPCD backlight mode on all of them. |
---|
| 1886 | + */ |
---|
| 1887 | + { MFG(0x06, 0xaf), PROD_ID(0x9b, 0x32), BIT(DP_QUIRK_FORCE_DPCD_BACKLIGHT) }, |
---|
| 1888 | + { MFG(0x06, 0xaf), PROD_ID(0xeb, 0x41), BIT(DP_QUIRK_FORCE_DPCD_BACKLIGHT) }, |
---|
| 1889 | + { MFG(0x4d, 0x10), PROD_ID(0xc7, 0x14), BIT(DP_QUIRK_FORCE_DPCD_BACKLIGHT) }, |
---|
| 1890 | + { MFG(0x4d, 0x10), PROD_ID(0xe6, 0x14), BIT(DP_QUIRK_FORCE_DPCD_BACKLIGHT) }, |
---|
| 1891 | + { MFG(0x4c, 0x83), PROD_ID(0x47, 0x41), BIT(DP_QUIRK_FORCE_DPCD_BACKLIGHT) }, |
---|
| 1892 | +}; |
---|
| 1893 | + |
---|
| 1894 | +#undef MFG |
---|
| 1895 | +#undef PROD_ID |
---|
| 1896 | + |
---|
| 1897 | +/** |
---|
| 1898 | + * drm_dp_get_edid_quirks() - Check the EDID of a DP device to find additional |
---|
| 1899 | + * DP-specific quirks |
---|
| 1900 | + * @edid: The EDID to check |
---|
| 1901 | + * |
---|
| 1902 | + * While OUIDs are meant to be used to recognize a DisplayPort device, a lot |
---|
| 1903 | + * of manufacturers don't seem to like following standards and neglect to fill |
---|
| 1904 | + * the dev-ID in, making it impossible to only use OUIDs for determining |
---|
| 1905 | + * quirks in some cases. This function can be used to check the EDID and look |
---|
| 1906 | + * up any additional DP quirks. The bits returned by this function correspond |
---|
| 1907 | + * to the quirk bits in &drm_dp_quirk. |
---|
| 1908 | + * |
---|
| 1909 | + * Returns: a bitmask of quirks, if any. The driver can check this using |
---|
| 1910 | + * drm_dp_has_quirk(). |
---|
| 1911 | + */ |
---|
| 1912 | +u32 drm_dp_get_edid_quirks(const struct edid *edid) |
---|
| 1913 | +{ |
---|
| 1914 | + const struct edid_quirk *quirk; |
---|
| 1915 | + u32 quirks = 0; |
---|
| 1916 | + int i; |
---|
| 1917 | + |
---|
| 1918 | + if (!edid) |
---|
| 1919 | + return 0; |
---|
| 1920 | + |
---|
| 1921 | + for (i = 0; i < ARRAY_SIZE(edid_quirk_list); i++) { |
---|
| 1922 | + quirk = &edid_quirk_list[i]; |
---|
| 1923 | + if (memcmp(quirk->mfg_id, edid->mfg_id, |
---|
| 1924 | + sizeof(edid->mfg_id)) == 0 && |
---|
| 1925 | + memcmp(quirk->prod_id, edid->prod_code, |
---|
| 1926 | + sizeof(edid->prod_code)) == 0) |
---|
| 1927 | + quirks |= quirk->quirks; |
---|
| 1928 | + } |
---|
| 1929 | + |
---|
| 1930 | + DRM_DEBUG_KMS("DP sink: EDID mfg %*phD prod-ID %*phD quirks: 0x%04x\n", |
---|
| 1931 | + (int)sizeof(edid->mfg_id), edid->mfg_id, |
---|
| 1932 | + (int)sizeof(edid->prod_code), edid->prod_code, quirks); |
---|
| 1933 | + |
---|
| 1934 | + return quirks; |
---|
| 1935 | +} |
---|
| 1936 | +EXPORT_SYMBOL(drm_dp_get_edid_quirks); |
---|
| 1937 | + |
---|
1302 | 1938 | /** |
---|
1303 | 1939 | * drm_dp_read_desc - read sink/branch descriptor from DPCD |
---|
1304 | 1940 | * @aux: DisplayPort AUX channel |
---|
1305 | | - * @desc: Device decriptor to fill from DPCD |
---|
| 1941 | + * @desc: Device descriptor to fill from DPCD |
---|
1306 | 1942 | * @is_branch: true for branch devices, false for sink devices |
---|
1307 | 1943 | * |
---|
1308 | 1944 | * Read DPCD 0x400 (sink) or 0x500 (branch) into @desc. Also debug log the |
---|
.. | .. |
---|
1325 | 1961 | |
---|
1326 | 1962 | dev_id_len = strnlen(ident->device_id, sizeof(ident->device_id)); |
---|
1327 | 1963 | |
---|
1328 | | - DRM_DEBUG_KMS("DP %s: OUI %*phD dev-ID %*pE HW-rev %d.%d SW-rev %d.%d quirks 0x%04x\n", |
---|
1329 | | - is_branch ? "branch" : "sink", |
---|
| 1964 | + DRM_DEBUG_KMS("%s: DP %s: OUI %*phD dev-ID %*pE HW-rev %d.%d SW-rev %d.%d quirks 0x%04x\n", |
---|
| 1965 | + aux->name, is_branch ? "branch" : "sink", |
---|
1330 | 1966 | (int)sizeof(ident->oui), ident->oui, |
---|
1331 | 1967 | dev_id_len, ident->device_id, |
---|
1332 | 1968 | ident->hw_rev >> 4, ident->hw_rev & 0xf, |
---|
.. | .. |
---|
1473 | 2109 | return num_bpc; |
---|
1474 | 2110 | } |
---|
1475 | 2111 | EXPORT_SYMBOL(drm_dp_dsc_sink_supported_input_bpcs); |
---|
| 2112 | + |
---|
| 2113 | +/** |
---|
| 2114 | + * drm_dp_get_phy_test_pattern() - get the requested pattern from the sink. |
---|
| 2115 | + * @aux: DisplayPort AUX channel |
---|
| 2116 | + * @data: DP phy compliance test parameters. |
---|
| 2117 | + * |
---|
| 2118 | + * Returns 0 on success or a negative error code on failure. |
---|
| 2119 | + */ |
---|
| 2120 | +int drm_dp_get_phy_test_pattern(struct drm_dp_aux *aux, |
---|
| 2121 | + struct drm_dp_phy_test_params *data) |
---|
| 2122 | +{ |
---|
| 2123 | + int err; |
---|
| 2124 | + u8 rate, lanes; |
---|
| 2125 | + |
---|
| 2126 | + err = drm_dp_dpcd_readb(aux, DP_TEST_LINK_RATE, &rate); |
---|
| 2127 | + if (err < 0) |
---|
| 2128 | + return err; |
---|
| 2129 | + data->link_rate = drm_dp_bw_code_to_link_rate(rate); |
---|
| 2130 | + |
---|
| 2131 | + err = drm_dp_dpcd_readb(aux, DP_TEST_LANE_COUNT, &lanes); |
---|
| 2132 | + if (err < 0) |
---|
| 2133 | + return err; |
---|
| 2134 | + data->num_lanes = lanes & DP_MAX_LANE_COUNT_MASK; |
---|
| 2135 | + |
---|
| 2136 | + if (lanes & DP_ENHANCED_FRAME_CAP) |
---|
| 2137 | + data->enhanced_frame_cap = true; |
---|
| 2138 | + |
---|
| 2139 | + err = drm_dp_dpcd_readb(aux, DP_PHY_TEST_PATTERN, &data->phy_pattern); |
---|
| 2140 | + if (err < 0) |
---|
| 2141 | + return err; |
---|
| 2142 | + |
---|
| 2143 | + switch (data->phy_pattern) { |
---|
| 2144 | + case DP_PHY_TEST_PATTERN_80BIT_CUSTOM: |
---|
| 2145 | + err = drm_dp_dpcd_read(aux, DP_TEST_80BIT_CUSTOM_PATTERN_7_0, |
---|
| 2146 | + &data->custom80, sizeof(data->custom80)); |
---|
| 2147 | + if (err < 0) |
---|
| 2148 | + return err; |
---|
| 2149 | + |
---|
| 2150 | + break; |
---|
| 2151 | + case DP_PHY_TEST_PATTERN_CP2520: |
---|
| 2152 | + err = drm_dp_dpcd_read(aux, DP_TEST_HBR2_SCRAMBLER_RESET, |
---|
| 2153 | + &data->hbr2_reset, |
---|
| 2154 | + sizeof(data->hbr2_reset)); |
---|
| 2155 | + if (err < 0) |
---|
| 2156 | + return err; |
---|
| 2157 | + } |
---|
| 2158 | + |
---|
| 2159 | + return 0; |
---|
| 2160 | +} |
---|
| 2161 | +EXPORT_SYMBOL(drm_dp_get_phy_test_pattern); |
---|
| 2162 | + |
---|
| 2163 | +/** |
---|
| 2164 | + * drm_dp_set_phy_test_pattern() - set the pattern to the sink. |
---|
| 2165 | + * @aux: DisplayPort AUX channel |
---|
| 2166 | + * @data: DP phy compliance test parameters. |
---|
| 2167 | + * @dp_rev: DP revision to use for compliance testing |
---|
| 2168 | + * |
---|
| 2169 | + * Returns 0 on success or a negative error code on failure. |
---|
| 2170 | + */ |
---|
| 2171 | +int drm_dp_set_phy_test_pattern(struct drm_dp_aux *aux, |
---|
| 2172 | + struct drm_dp_phy_test_params *data, u8 dp_rev) |
---|
| 2173 | +{ |
---|
| 2174 | + int err, i; |
---|
| 2175 | + u8 test_pattern; |
---|
| 2176 | + |
---|
| 2177 | + test_pattern = data->phy_pattern; |
---|
| 2178 | + if (dp_rev < 0x12) { |
---|
| 2179 | + test_pattern = (test_pattern << 2) & |
---|
| 2180 | + DP_LINK_QUAL_PATTERN_11_MASK; |
---|
| 2181 | + err = drm_dp_dpcd_writeb(aux, DP_TRAINING_PATTERN_SET, |
---|
| 2182 | + test_pattern); |
---|
| 2183 | + if (err < 0) |
---|
| 2184 | + return err; |
---|
| 2185 | + } else { |
---|
| 2186 | + for (i = 0; i < data->num_lanes; i++) { |
---|
| 2187 | + err = drm_dp_dpcd_writeb(aux, |
---|
| 2188 | + DP_LINK_QUAL_LANE0_SET + i, |
---|
| 2189 | + test_pattern); |
---|
| 2190 | + if (err < 0) |
---|
| 2191 | + return err; |
---|
| 2192 | + } |
---|
| 2193 | + } |
---|
| 2194 | + |
---|
| 2195 | + return 0; |
---|
| 2196 | +} |
---|
| 2197 | +EXPORT_SYMBOL(drm_dp_set_phy_test_pattern); |
---|
| 2198 | + |
---|
| 2199 | +static const char *dp_pixelformat_get_name(enum dp_pixelformat pixelformat) |
---|
| 2200 | +{ |
---|
| 2201 | + if (pixelformat < 0 || pixelformat > DP_PIXELFORMAT_RESERVED) |
---|
| 2202 | + return "Invalid"; |
---|
| 2203 | + |
---|
| 2204 | + switch (pixelformat) { |
---|
| 2205 | + case DP_PIXELFORMAT_RGB: |
---|
| 2206 | + return "RGB"; |
---|
| 2207 | + case DP_PIXELFORMAT_YUV444: |
---|
| 2208 | + return "YUV444"; |
---|
| 2209 | + case DP_PIXELFORMAT_YUV422: |
---|
| 2210 | + return "YUV422"; |
---|
| 2211 | + case DP_PIXELFORMAT_YUV420: |
---|
| 2212 | + return "YUV420"; |
---|
| 2213 | + case DP_PIXELFORMAT_Y_ONLY: |
---|
| 2214 | + return "Y_ONLY"; |
---|
| 2215 | + case DP_PIXELFORMAT_RAW: |
---|
| 2216 | + return "RAW"; |
---|
| 2217 | + default: |
---|
| 2218 | + return "Reserved"; |
---|
| 2219 | + } |
---|
| 2220 | +} |
---|
| 2221 | + |
---|
| 2222 | +static const char *dp_colorimetry_get_name(enum dp_pixelformat pixelformat, |
---|
| 2223 | + enum dp_colorimetry colorimetry) |
---|
| 2224 | +{ |
---|
| 2225 | + if (pixelformat < 0 || pixelformat > DP_PIXELFORMAT_RESERVED) |
---|
| 2226 | + return "Invalid"; |
---|
| 2227 | + |
---|
| 2228 | + switch (colorimetry) { |
---|
| 2229 | + case DP_COLORIMETRY_DEFAULT: |
---|
| 2230 | + switch (pixelformat) { |
---|
| 2231 | + case DP_PIXELFORMAT_RGB: |
---|
| 2232 | + return "sRGB"; |
---|
| 2233 | + case DP_PIXELFORMAT_YUV444: |
---|
| 2234 | + case DP_PIXELFORMAT_YUV422: |
---|
| 2235 | + case DP_PIXELFORMAT_YUV420: |
---|
| 2236 | + return "BT.601"; |
---|
| 2237 | + case DP_PIXELFORMAT_Y_ONLY: |
---|
| 2238 | + return "DICOM PS3.14"; |
---|
| 2239 | + case DP_PIXELFORMAT_RAW: |
---|
| 2240 | + return "Custom Color Profile"; |
---|
| 2241 | + default: |
---|
| 2242 | + return "Reserved"; |
---|
| 2243 | + } |
---|
| 2244 | + case DP_COLORIMETRY_RGB_WIDE_FIXED: /* and DP_COLORIMETRY_BT709_YCC */ |
---|
| 2245 | + switch (pixelformat) { |
---|
| 2246 | + case DP_PIXELFORMAT_RGB: |
---|
| 2247 | + return "Wide Fixed"; |
---|
| 2248 | + case DP_PIXELFORMAT_YUV444: |
---|
| 2249 | + case DP_PIXELFORMAT_YUV422: |
---|
| 2250 | + case DP_PIXELFORMAT_YUV420: |
---|
| 2251 | + return "BT.709"; |
---|
| 2252 | + default: |
---|
| 2253 | + return "Reserved"; |
---|
| 2254 | + } |
---|
| 2255 | + case DP_COLORIMETRY_RGB_WIDE_FLOAT: /* and DP_COLORIMETRY_XVYCC_601 */ |
---|
| 2256 | + switch (pixelformat) { |
---|
| 2257 | + case DP_PIXELFORMAT_RGB: |
---|
| 2258 | + return "Wide Float"; |
---|
| 2259 | + case DP_PIXELFORMAT_YUV444: |
---|
| 2260 | + case DP_PIXELFORMAT_YUV422: |
---|
| 2261 | + case DP_PIXELFORMAT_YUV420: |
---|
| 2262 | + return "xvYCC 601"; |
---|
| 2263 | + default: |
---|
| 2264 | + return "Reserved"; |
---|
| 2265 | + } |
---|
| 2266 | + case DP_COLORIMETRY_OPRGB: /* and DP_COLORIMETRY_XVYCC_709 */ |
---|
| 2267 | + switch (pixelformat) { |
---|
| 2268 | + case DP_PIXELFORMAT_RGB: |
---|
| 2269 | + return "OpRGB"; |
---|
| 2270 | + case DP_PIXELFORMAT_YUV444: |
---|
| 2271 | + case DP_PIXELFORMAT_YUV422: |
---|
| 2272 | + case DP_PIXELFORMAT_YUV420: |
---|
| 2273 | + return "xvYCC 709"; |
---|
| 2274 | + default: |
---|
| 2275 | + return "Reserved"; |
---|
| 2276 | + } |
---|
| 2277 | + case DP_COLORIMETRY_DCI_P3_RGB: /* and DP_COLORIMETRY_SYCC_601 */ |
---|
| 2278 | + switch (pixelformat) { |
---|
| 2279 | + case DP_PIXELFORMAT_RGB: |
---|
| 2280 | + return "DCI-P3"; |
---|
| 2281 | + case DP_PIXELFORMAT_YUV444: |
---|
| 2282 | + case DP_PIXELFORMAT_YUV422: |
---|
| 2283 | + case DP_PIXELFORMAT_YUV420: |
---|
| 2284 | + return "sYCC 601"; |
---|
| 2285 | + default: |
---|
| 2286 | + return "Reserved"; |
---|
| 2287 | + } |
---|
| 2288 | + case DP_COLORIMETRY_RGB_CUSTOM: /* and DP_COLORIMETRY_OPYCC_601 */ |
---|
| 2289 | + switch (pixelformat) { |
---|
| 2290 | + case DP_PIXELFORMAT_RGB: |
---|
| 2291 | + return "Custom Profile"; |
---|
| 2292 | + case DP_PIXELFORMAT_YUV444: |
---|
| 2293 | + case DP_PIXELFORMAT_YUV422: |
---|
| 2294 | + case DP_PIXELFORMAT_YUV420: |
---|
| 2295 | + return "OpYCC 601"; |
---|
| 2296 | + default: |
---|
| 2297 | + return "Reserved"; |
---|
| 2298 | + } |
---|
| 2299 | + case DP_COLORIMETRY_BT2020_RGB: /* and DP_COLORIMETRY_BT2020_CYCC */ |
---|
| 2300 | + switch (pixelformat) { |
---|
| 2301 | + case DP_PIXELFORMAT_RGB: |
---|
| 2302 | + return "BT.2020 RGB"; |
---|
| 2303 | + case DP_PIXELFORMAT_YUV444: |
---|
| 2304 | + case DP_PIXELFORMAT_YUV422: |
---|
| 2305 | + case DP_PIXELFORMAT_YUV420: |
---|
| 2306 | + return "BT.2020 CYCC"; |
---|
| 2307 | + default: |
---|
| 2308 | + return "Reserved"; |
---|
| 2309 | + } |
---|
| 2310 | + case DP_COLORIMETRY_BT2020_YCC: |
---|
| 2311 | + switch (pixelformat) { |
---|
| 2312 | + case DP_PIXELFORMAT_YUV444: |
---|
| 2313 | + case DP_PIXELFORMAT_YUV422: |
---|
| 2314 | + case DP_PIXELFORMAT_YUV420: |
---|
| 2315 | + return "BT.2020 YCC"; |
---|
| 2316 | + default: |
---|
| 2317 | + return "Reserved"; |
---|
| 2318 | + } |
---|
| 2319 | + default: |
---|
| 2320 | + return "Invalid"; |
---|
| 2321 | + } |
---|
| 2322 | +} |
---|
| 2323 | + |
---|
| 2324 | +static const char *dp_dynamic_range_get_name(enum dp_dynamic_range dynamic_range) |
---|
| 2325 | +{ |
---|
| 2326 | + switch (dynamic_range) { |
---|
| 2327 | + case DP_DYNAMIC_RANGE_VESA: |
---|
| 2328 | + return "VESA range"; |
---|
| 2329 | + case DP_DYNAMIC_RANGE_CTA: |
---|
| 2330 | + return "CTA range"; |
---|
| 2331 | + default: |
---|
| 2332 | + return "Invalid"; |
---|
| 2333 | + } |
---|
| 2334 | +} |
---|
| 2335 | + |
---|
| 2336 | +static const char *dp_content_type_get_name(enum dp_content_type content_type) |
---|
| 2337 | +{ |
---|
| 2338 | + switch (content_type) { |
---|
| 2339 | + case DP_CONTENT_TYPE_NOT_DEFINED: |
---|
| 2340 | + return "Not defined"; |
---|
| 2341 | + case DP_CONTENT_TYPE_GRAPHICS: |
---|
| 2342 | + return "Graphics"; |
---|
| 2343 | + case DP_CONTENT_TYPE_PHOTO: |
---|
| 2344 | + return "Photo"; |
---|
| 2345 | + case DP_CONTENT_TYPE_VIDEO: |
---|
| 2346 | + return "Video"; |
---|
| 2347 | + case DP_CONTENT_TYPE_GAME: |
---|
| 2348 | + return "Game"; |
---|
| 2349 | + default: |
---|
| 2350 | + return "Reserved"; |
---|
| 2351 | + } |
---|
| 2352 | +} |
---|
| 2353 | + |
---|
| 2354 | +void drm_dp_vsc_sdp_log(const char *level, struct device *dev, |
---|
| 2355 | + const struct drm_dp_vsc_sdp *vsc) |
---|
| 2356 | +{ |
---|
| 2357 | +#define DP_SDP_LOG(fmt, ...) dev_printk(level, dev, fmt, ##__VA_ARGS__) |
---|
| 2358 | + DP_SDP_LOG("DP SDP: %s, revision %u, length %u\n", "VSC", |
---|
| 2359 | + vsc->revision, vsc->length); |
---|
| 2360 | + DP_SDP_LOG(" pixelformat: %s\n", |
---|
| 2361 | + dp_pixelformat_get_name(vsc->pixelformat)); |
---|
| 2362 | + DP_SDP_LOG(" colorimetry: %s\n", |
---|
| 2363 | + dp_colorimetry_get_name(vsc->pixelformat, vsc->colorimetry)); |
---|
| 2364 | + DP_SDP_LOG(" bpc: %u\n", vsc->bpc); |
---|
| 2365 | + DP_SDP_LOG(" dynamic range: %s\n", |
---|
| 2366 | + dp_dynamic_range_get_name(vsc->dynamic_range)); |
---|
| 2367 | + DP_SDP_LOG(" content type: %s\n", |
---|
| 2368 | + dp_content_type_get_name(vsc->content_type)); |
---|
| 2369 | +#undef DP_SDP_LOG |
---|
| 2370 | +} |
---|
| 2371 | +EXPORT_SYMBOL(drm_dp_vsc_sdp_log); |
---|