diff options
author | Alex Deucher <alexdeucher@gmail.com> | 2011-05-20 04:34:24 -0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2011-05-20 06:02:28 -0400 |
commit | 834b2904bbfde3d85b5e984688777d56e9c7bf80 (patch) | |
tree | 90f82af08bf00bf1d27bd395802939c776a1e4d1 | |
parent | f8d0edde15702f1e3114d4afc80cb9cced2c754b (diff) |
drm/radeon/kms: improve aux error handling
Signed-off-by: Alex Deucher <alexdeucher@gmail.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
-rw-r--r-- | drivers/gpu/drm/radeon/atombios_dp.c | 234 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_mode.h | 2 |
2 files changed, 151 insertions, 85 deletions
diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c index 695de9a38506..0f72f4d85c30 100644 --- a/drivers/gpu/drm/radeon/atombios_dp.c +++ b/drivers/gpu/drm/radeon/atombios_dp.c | |||
@@ -327,23 +327,23 @@ union aux_channel_transaction { | |||
327 | }; | 327 | }; |
328 | 328 | ||
329 | /* radeon aux chan functions */ | 329 | /* radeon aux chan functions */ |
330 | bool radeon_process_aux_ch(struct radeon_i2c_chan *chan, u8 *req_bytes, | 330 | static int radeon_process_aux_ch(struct radeon_i2c_chan *chan, |
331 | int num_bytes, u8 *read_byte, | 331 | u8 *send, int send_bytes, |
332 | u8 read_buf_len, u8 delay) | 332 | u8 *recv, int recv_size, |
333 | u8 delay, u8 *ack) | ||
333 | { | 334 | { |
334 | struct drm_device *dev = chan->dev; | 335 | struct drm_device *dev = chan->dev; |
335 | struct radeon_device *rdev = dev->dev_private; | 336 | struct radeon_device *rdev = dev->dev_private; |
336 | union aux_channel_transaction args; | 337 | union aux_channel_transaction args; |
337 | int index = GetIndexIntoMasterTable(COMMAND, ProcessAuxChannelTransaction); | 338 | int index = GetIndexIntoMasterTable(COMMAND, ProcessAuxChannelTransaction); |
338 | unsigned char *base; | 339 | unsigned char *base; |
339 | int retry_count = 0; | 340 | int recv_bytes; |
340 | 341 | ||
341 | memset(&args, 0, sizeof(args)); | 342 | memset(&args, 0, sizeof(args)); |
342 | 343 | ||
343 | base = (unsigned char *)rdev->mode_info.atom_context->scratch; | 344 | base = (unsigned char *)rdev->mode_info.atom_context->scratch; |
344 | 345 | ||
345 | retry: | 346 | memcpy(base, send, send_bytes); |
346 | memcpy(base, req_bytes, num_bytes); | ||
347 | 347 | ||
348 | args.v1.lpAuxRequest = 0; | 348 | args.v1.lpAuxRequest = 0; |
349 | args.v1.lpDataOut = 16; | 349 | args.v1.lpDataOut = 16; |
@@ -355,75 +355,103 @@ retry: | |||
355 | 355 | ||
356 | atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); | 356 | atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); |
357 | 357 | ||
358 | if (args.v1.ucReplyStatus && !args.v1.ucDataOutLen) { | 358 | *ack = args.v1.ucReplyStatus; |
359 | if (args.v1.ucReplyStatus == 0x20 && retry_count++ < 10) | 359 | |
360 | goto retry; | 360 | /* timeout */ |
361 | DRM_DEBUG_KMS("failed to get auxch %02x%02x %02x %02x 0x%02x %02x after %d retries\n", | 361 | if (args.v1.ucReplyStatus == 1) { |
362 | req_bytes[1], req_bytes[0], req_bytes[2], req_bytes[3], | 362 | DRM_DEBUG_KMS("dp_aux_ch timeout\n"); |
363 | chan->rec.i2c_id, args.v1.ucReplyStatus, retry_count); | 363 | return -ETIMEDOUT; |
364 | return false; | ||
365 | } | 364 | } |
366 | 365 | ||
367 | if (args.v1.ucDataOutLen && read_byte && read_buf_len) { | 366 | /* flags not zero */ |
368 | if (read_buf_len < args.v1.ucDataOutLen) { | 367 | if (args.v1.ucReplyStatus == 2) { |
369 | DRM_ERROR("Buffer to small for return answer %d %d\n", | 368 | DRM_DEBUG_KMS("dp_aux_ch flags not zero\n"); |
370 | read_buf_len, args.v1.ucDataOutLen); | 369 | return -EBUSY; |
371 | return false; | ||
372 | } | ||
373 | { | ||
374 | int len = min(read_buf_len, args.v1.ucDataOutLen); | ||
375 | memcpy(read_byte, base + 16, len); | ||
376 | } | ||
377 | } | 370 | } |
378 | return true; | 371 | |
372 | /* error */ | ||
373 | if (args.v1.ucReplyStatus == 3) { | ||
374 | DRM_DEBUG_KMS("dp_aux_ch error\n"); | ||
375 | return -EIO; | ||
376 | } | ||
377 | |||
378 | recv_bytes = args.v1.ucDataOutLen; | ||
379 | if (recv_bytes > recv_size) | ||
380 | recv_bytes = recv_size; | ||
381 | |||
382 | if (recv && recv_size) | ||
383 | memcpy(recv, base + 16, recv_bytes); | ||
384 | |||
385 | return recv_bytes; | ||
379 | } | 386 | } |
380 | 387 | ||
381 | bool radeon_dp_aux_native_write(struct radeon_connector *radeon_connector, uint16_t address, | 388 | static int radeon_dp_aux_native_write(struct radeon_connector *radeon_connector, |
382 | uint8_t send_bytes, uint8_t *send) | 389 | u16 address, u8 *send, u8 send_bytes, u8 delay) |
383 | { | 390 | { |
384 | struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; | 391 | struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; |
392 | int ret; | ||
385 | u8 msg[20]; | 393 | u8 msg[20]; |
386 | u8 msg_len, dp_msg_len; | 394 | int msg_bytes = send_bytes + 4; |
387 | bool ret; | 395 | u8 ack; |
396 | |||
397 | if (send_bytes > 16) | ||
398 | return -1; | ||
388 | 399 | ||
389 | dp_msg_len = 4; | ||
390 | msg[0] = address; | 400 | msg[0] = address; |
391 | msg[1] = address >> 8; | 401 | msg[1] = address >> 8; |
392 | msg[2] = AUX_NATIVE_WRITE << 4; | 402 | msg[2] = AUX_NATIVE_WRITE << 4; |
393 | dp_msg_len += send_bytes; | 403 | msg[3] = (msg_bytes << 4) | (send_bytes - 1); |
394 | msg[3] = (dp_msg_len << 4) | (send_bytes - 1); | 404 | memcpy(&msg[4], send, send_bytes); |
395 | 405 | ||
396 | if (send_bytes > 16) | 406 | while (1) { |
397 | return false; | 407 | ret = radeon_process_aux_ch(dig_connector->dp_i2c_bus, |
408 | msg, msg_bytes, NULL, 0, delay, &ack); | ||
409 | if (ret < 0) | ||
410 | return ret; | ||
411 | if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_ACK) | ||
412 | break; | ||
413 | else if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_DEFER) | ||
414 | udelay(400); | ||
415 | else | ||
416 | return -EIO; | ||
417 | } | ||
398 | 418 | ||
399 | memcpy(&msg[4], send, send_bytes); | 419 | return send_bytes; |
400 | msg_len = 4 + send_bytes; | ||
401 | ret = radeon_process_aux_ch(dig_connector->dp_i2c_bus, msg, msg_len, NULL, 0, 0); | ||
402 | return ret; | ||
403 | } | 420 | } |
404 | 421 | ||
405 | bool radeon_dp_aux_native_read(struct radeon_connector *radeon_connector, uint16_t address, | 422 | static int radeon_dp_aux_native_read(struct radeon_connector *radeon_connector, |
406 | uint8_t delay, uint8_t expected_bytes, | 423 | u16 address, u8 *recv, int recv_bytes, u8 delay) |
407 | uint8_t *read_p) | ||
408 | { | 424 | { |
409 | struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; | 425 | struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; |
410 | u8 msg[20]; | 426 | u8 msg[4]; |
411 | u8 msg_len, dp_msg_len; | 427 | int msg_bytes = 4; |
412 | bool ret = false; | 428 | u8 ack; |
413 | msg_len = 4; | 429 | int ret; |
414 | dp_msg_len = 4; | 430 | |
415 | msg[0] = address; | 431 | msg[0] = address; |
416 | msg[1] = address >> 8; | 432 | msg[1] = address >> 8; |
417 | msg[2] = AUX_NATIVE_READ << 4; | 433 | msg[2] = AUX_NATIVE_READ << 4; |
418 | msg[3] = (dp_msg_len) << 4; | 434 | msg[3] = (msg_bytes << 4) | (recv_bytes - 1); |
419 | msg[3] |= expected_bytes - 1; | 435 | |
420 | 436 | while (1) { | |
421 | ret = radeon_process_aux_ch(dig_connector->dp_i2c_bus, msg, msg_len, read_p, expected_bytes, delay); | 437 | ret = radeon_process_aux_ch(dig_connector->dp_i2c_bus, |
422 | return ret; | 438 | msg, msg_bytes, recv, recv_bytes, delay, &ack); |
439 | if (ret == 0) | ||
440 | return -EPROTO; | ||
441 | if (ret < 0) | ||
442 | return ret; | ||
443 | if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_ACK) | ||
444 | return ret; | ||
445 | else if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_DEFER) | ||
446 | udelay(400); | ||
447 | else | ||
448 | return -EIO; | ||
449 | } | ||
423 | } | 450 | } |
424 | 451 | ||
425 | /* radeon dp functions */ | 452 | /* radeon dp functions */ |
426 | static u8 radeon_dp_encoder_service(struct radeon_device *rdev, int action, int dp_clock, | 453 | static u8 radeon_dp_encoder_service(struct radeon_device *rdev, |
454 | int action, int dp_clock, | ||
427 | uint8_t ucconfig, uint8_t lane_num) | 455 | uint8_t ucconfig, uint8_t lane_num) |
428 | { | 456 | { |
429 | DP_ENCODER_SERVICE_PARAMETERS args; | 457 | DP_ENCODER_SERVICE_PARAMETERS args; |
@@ -456,8 +484,8 @@ bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector) | |||
456 | u8 msg[25]; | 484 | u8 msg[25]; |
457 | int ret; | 485 | int ret; |
458 | 486 | ||
459 | ret = radeon_dp_aux_native_read(radeon_connector, DP_DPCD_REV, 0, 8, msg); | 487 | ret = radeon_dp_aux_native_read(radeon_connector, DP_DPCD_REV, msg, 8, 0); |
460 | if (ret) { | 488 | if (ret > 0) { |
461 | memcpy(dig_connector->dpcd, msg, 8); | 489 | memcpy(dig_connector->dpcd, msg, 8); |
462 | { | 490 | { |
463 | int i; | 491 | int i; |
@@ -505,9 +533,9 @@ static bool atom_dp_get_link_status(struct radeon_connector *radeon_connector, | |||
505 | u8 link_status[DP_LINK_STATUS_SIZE]) | 533 | u8 link_status[DP_LINK_STATUS_SIZE]) |
506 | { | 534 | { |
507 | int ret; | 535 | int ret; |
508 | ret = radeon_dp_aux_native_read(radeon_connector, DP_LANE0_1_STATUS, 100, | 536 | ret = radeon_dp_aux_native_read(radeon_connector, DP_LANE0_1_STATUS, |
509 | DP_LINK_STATUS_SIZE, link_status); | 537 | link_status, DP_LINK_STATUS_SIZE, 100); |
510 | if (!ret) { | 538 | if (ret <= 0) { |
511 | DRM_ERROR("displayport link status failed\n"); | 539 | DRM_ERROR("displayport link status failed\n"); |
512 | return false; | 540 | return false; |
513 | } | 541 | } |
@@ -535,22 +563,22 @@ static void dp_set_power(struct radeon_connector *radeon_connector, u8 power_sta | |||
535 | struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; | 563 | struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; |
536 | 564 | ||
537 | if (dig_connector->dpcd[0] >= 0x11) { | 565 | if (dig_connector->dpcd[0] >= 0x11) { |
538 | radeon_dp_aux_native_write(radeon_connector, DP_SET_POWER, 1, | 566 | radeon_dp_aux_native_write(radeon_connector, DP_SET_POWER, |
539 | &power_state); | 567 | &power_state, 1, 0); |
540 | } | 568 | } |
541 | } | 569 | } |
542 | 570 | ||
543 | static void dp_set_downspread(struct radeon_connector *radeon_connector, u8 downspread) | 571 | static void dp_set_downspread(struct radeon_connector *radeon_connector, u8 downspread) |
544 | { | 572 | { |
545 | radeon_dp_aux_native_write(radeon_connector, DP_DOWNSPREAD_CTRL, 1, | 573 | radeon_dp_aux_native_write(radeon_connector, DP_DOWNSPREAD_CTRL, |
546 | &downspread); | 574 | &downspread, 1, 0); |
547 | } | 575 | } |
548 | 576 | ||
549 | static void dp_set_link_bw_lanes(struct radeon_connector *radeon_connector, | 577 | static void dp_set_link_bw_lanes(struct radeon_connector *radeon_connector, |
550 | u8 link_configuration[DP_LINK_CONFIGURATION_SIZE]) | 578 | u8 link_configuration[DP_LINK_CONFIGURATION_SIZE]) |
551 | { | 579 | { |
552 | radeon_dp_aux_native_write(radeon_connector, DP_LINK_BW_SET, 2, | 580 | radeon_dp_aux_native_write(radeon_connector, DP_LINK_BW_SET, |
553 | link_configuration); | 581 | link_configuration, 2, 0); |
554 | } | 582 | } |
555 | 583 | ||
556 | static void dp_update_dpvs_emph(struct radeon_connector *radeon_connector, | 584 | static void dp_update_dpvs_emph(struct radeon_connector *radeon_connector, |
@@ -566,14 +594,14 @@ static void dp_update_dpvs_emph(struct radeon_connector *radeon_connector, | |||
566 | i, train_set[i]); | 594 | i, train_set[i]); |
567 | 595 | ||
568 | radeon_dp_aux_native_write(radeon_connector, DP_TRAINING_LANE0_SET, | 596 | radeon_dp_aux_native_write(radeon_connector, DP_TRAINING_LANE0_SET, |
569 | dig_connector->dp_lane_count, train_set); | 597 | train_set, dig_connector->dp_lane_count, 0); |
570 | } | 598 | } |
571 | 599 | ||
572 | static void dp_set_training(struct radeon_connector *radeon_connector, | 600 | static void dp_set_training(struct radeon_connector *radeon_connector, |
573 | u8 training) | 601 | u8 training) |
574 | { | 602 | { |
575 | radeon_dp_aux_native_write(radeon_connector, DP_TRAINING_PATTERN_SET, | 603 | radeon_dp_aux_native_write(radeon_connector, DP_TRAINING_PATTERN_SET, |
576 | 1, &training); | 604 | &training, 1, 0); |
577 | } | 605 | } |
578 | 606 | ||
579 | void dp_link_train(struct drm_encoder *encoder, | 607 | void dp_link_train(struct drm_encoder *encoder, |
@@ -756,16 +784,18 @@ void dp_link_train(struct drm_encoder *encoder, | |||
756 | } | 784 | } |
757 | 785 | ||
758 | int radeon_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, | 786 | int radeon_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, |
759 | uint8_t write_byte, uint8_t *read_byte) | 787 | u8 write_byte, u8 *read_byte) |
760 | { | 788 | { |
761 | struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; | 789 | struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; |
762 | struct radeon_i2c_chan *auxch = (struct radeon_i2c_chan *)adapter; | 790 | struct radeon_i2c_chan *auxch = (struct radeon_i2c_chan *)adapter; |
763 | int ret = 0; | 791 | u16 address = algo_data->address; |
764 | uint16_t address = algo_data->address; | 792 | u8 msg[5]; |
765 | uint8_t msg[5]; | 793 | u8 reply[2]; |
766 | uint8_t reply[2]; | 794 | unsigned retry; |
767 | int msg_len, dp_msg_len; | 795 | int msg_bytes; |
768 | int reply_bytes; | 796 | int reply_bytes = 1; |
797 | int ret; | ||
798 | u8 ack; | ||
769 | 799 | ||
770 | /* Set up the command byte */ | 800 | /* Set up the command byte */ |
771 | if (mode & MODE_I2C_READ) | 801 | if (mode & MODE_I2C_READ) |
@@ -779,31 +809,67 @@ int radeon_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, | |||
779 | msg[0] = address; | 809 | msg[0] = address; |
780 | msg[1] = address >> 8; | 810 | msg[1] = address >> 8; |
781 | 811 | ||
782 | reply_bytes = 1; | ||
783 | |||
784 | msg_len = 4; | ||
785 | dp_msg_len = 3; | ||
786 | switch (mode) { | 812 | switch (mode) { |
787 | case MODE_I2C_WRITE: | 813 | case MODE_I2C_WRITE: |
814 | msg_bytes = 5; | ||
815 | msg[3] = msg_bytes << 4; | ||
788 | msg[4] = write_byte; | 816 | msg[4] = write_byte; |
789 | msg_len++; | ||
790 | dp_msg_len += 2; | ||
791 | break; | 817 | break; |
792 | case MODE_I2C_READ: | 818 | case MODE_I2C_READ: |
793 | dp_msg_len += 1; | 819 | msg_bytes = 4; |
820 | msg[3] = msg_bytes << 4; | ||
794 | break; | 821 | break; |
795 | default: | 822 | default: |
823 | msg_bytes = 4; | ||
824 | msg[3] = 3 << 4; | ||
796 | break; | 825 | break; |
797 | } | 826 | } |
798 | 827 | ||
799 | msg[3] = (dp_msg_len) << 4; | 828 | for (retry = 0; retry < 4; retry++) { |
800 | ret = radeon_process_aux_ch(auxch, msg, msg_len, reply, reply_bytes, 0); | 829 | ret = radeon_process_aux_ch(auxch, |
830 | msg, msg_bytes, reply, reply_bytes, 0, &ack); | ||
831 | if (ret < 0) { | ||
832 | DRM_DEBUG_KMS("aux_ch failed %d\n", ret); | ||
833 | return ret; | ||
834 | } | ||
835 | |||
836 | switch (ack & AUX_NATIVE_REPLY_MASK) { | ||
837 | case AUX_NATIVE_REPLY_ACK: | ||
838 | /* I2C-over-AUX Reply field is only valid | ||
839 | * when paired with AUX ACK. | ||
840 | */ | ||
841 | break; | ||
842 | case AUX_NATIVE_REPLY_NACK: | ||
843 | DRM_DEBUG_KMS("aux_ch native nack\n"); | ||
844 | return -EREMOTEIO; | ||
845 | case AUX_NATIVE_REPLY_DEFER: | ||
846 | DRM_DEBUG_KMS("aux_ch native defer\n"); | ||
847 | udelay(400); | ||
848 | continue; | ||
849 | default: | ||
850 | DRM_ERROR("aux_ch invalid native reply 0x%02x\n", ack); | ||
851 | return -EREMOTEIO; | ||
852 | } | ||
801 | 853 | ||
802 | if (ret) { | 854 | switch (ack & AUX_I2C_REPLY_MASK) { |
803 | if (read_byte) | 855 | case AUX_I2C_REPLY_ACK: |
804 | *read_byte = reply[0]; | 856 | if (mode == MODE_I2C_READ) |
805 | return reply_bytes; | 857 | *read_byte = reply[0]; |
858 | return ret; | ||
859 | case AUX_I2C_REPLY_NACK: | ||
860 | DRM_DEBUG_KMS("aux_i2c nack\n"); | ||
861 | return -EREMOTEIO; | ||
862 | case AUX_I2C_REPLY_DEFER: | ||
863 | DRM_DEBUG_KMS("aux_i2c defer\n"); | ||
864 | udelay(400); | ||
865 | break; | ||
866 | default: | ||
867 | DRM_ERROR("aux_i2c invalid reply 0x%02x\n", ack); | ||
868 | return -EREMOTEIO; | ||
869 | } | ||
806 | } | 870 | } |
871 | |||
872 | DRM_ERROR("aux i2c too many retries, giving up\n"); | ||
807 | return -EREMOTEIO; | 873 | return -EREMOTEIO; |
808 | } | 874 | } |
809 | 875 | ||
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index 7a3cfa09d45f..bb43573ff3cf 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h | |||
@@ -484,7 +484,7 @@ extern void atombios_dig_transmitter_setup(struct drm_encoder *encoder, | |||
484 | int action, uint8_t lane_num, | 484 | int action, uint8_t lane_num, |
485 | uint8_t lane_set); | 485 | uint8_t lane_set); |
486 | extern int radeon_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, | 486 | extern int radeon_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, |
487 | uint8_t write_byte, uint8_t *read_byte); | 487 | u8 write_byte, u8 *read_byte); |
488 | 488 | ||
489 | extern void radeon_i2c_init(struct radeon_device *rdev); | 489 | extern void radeon_i2c_init(struct radeon_device *rdev); |
490 | extern void radeon_i2c_fini(struct radeon_device *rdev); | 490 | extern void radeon_i2c_fini(struct radeon_device *rdev); |