diff options
Diffstat (limited to 'drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c')
-rw-r--r-- | drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c | 268 |
1 files changed, 187 insertions, 81 deletions
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c index e6da8506128b..2b09310965bc 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c @@ -36,9 +36,19 @@ #include "core_types.h" #include "dc_link_ddc.h" #include "dce/dce_aux.h" +#include "dmub/inc/dmub_cmd.h" + +#define DC_LOGGER_INIT(logger) + +static const uint8_t DP_VGA_DONGLE_BRANCH_DEV_NAME[] = "DpVga"; +/* DP to Dual link DVI converter */ +static const uint8_t DP_DVI_CONVERTER_ID_4[] = "m2DVIa"; +static const uint8_t DP_DVI_CONVERTER_ID_5[] = "3393N2"; #define AUX_POWER_UP_WA_DELAY 500 #define I2C_OVER_AUX_DEFER_WA_DELAY 70 +#define DPVGA_DONGLE_AUX_DEFER_WA_DELAY 40 +#define I2C_OVER_AUX_DEFER_WA_DELAY_1MS 1 /* CV smart dongle slave address for retrieving supported HDTV modes*/ #define CV_SMART_DONGLE_ADDRESS 0x20 @@ -126,22 +136,16 @@ struct aux_payloads { struct vector payloads; }; -static struct i2c_payloads *dal_ddc_i2c_payloads_create(struct dc_context *ctx, uint32_t count) +static bool dal_ddc_i2c_payloads_create( + struct dc_context *ctx, + struct i2c_payloads *payloads, + uint32_t count) { - struct i2c_payloads *payloads; - - payloads = kzalloc(sizeof(struct i2c_payloads), GFP_KERNEL); - - if (!payloads) - return NULL; - if (dal_vector_construct( &payloads->payloads, ctx, count, sizeof(struct i2c_payload))) - return payloads; - - kfree(payloads); - return NULL; + return true; + return false; } static struct i2c_payload *dal_ddc_i2c_payloads_get(struct i2c_payloads *p) @@ -154,16 +158,6 @@ static uint32_t dal_ddc_i2c_payloads_get_count(struct i2c_payloads *p) return p->payloads.count; } -static void dal_ddc_i2c_payloads_destroy(struct i2c_payloads **p) -{ - if (!p || !*p) - return; - dal_vector_destruct(&(*p)->payloads); - kfree(*p); - *p = NULL; - -} - #define DDC_MIN(a, b) (((a) < (b)) ? (a) : (b)) void dal_ddc_i2c_payloads_add( @@ -187,7 +181,7 @@ void dal_ddc_i2c_payloads_add( } -static void construct( +static void ddc_service_construct( struct ddc_service *ddc_service, struct ddc_service_init_data *init_data) { @@ -202,11 +196,19 @@ static void construct( ddc_service->link = init_data->link; ddc_service->ctx = init_data->ctx; - if (BP_RESULT_OK != dcb->funcs->get_i2c_info(dcb, init_data->id, &i2c_info)) { + if (init_data->is_dpia_link || + dcb->funcs->get_i2c_info(dcb, init_data->id, &i2c_info) != BP_RESULT_OK) { ddc_service->ddc_pin = NULL; } else { + DC_LOGGER_INIT(ddc_service->ctx->logger); + DC_LOG_DC("BIOS object table - i2c_line: %d", i2c_info.i2c_line); + DC_LOG_DC("BIOS object table - i2c_engine_id: %d", i2c_info.i2c_engine_id); + hw_info.ddc_channel = i2c_info.i2c_line; - hw_info.hw_supported = i2c_info.i2c_hw_assist; + if (ddc_service->link != NULL) + hw_info.hw_supported = i2c_info.i2c_hw_assist; + else + hw_info.hw_supported = false; ddc_service->ddc_pin = dal_gpio_create_ddc( gpio_service, @@ -236,11 +238,11 @@ struct ddc_service *dal_ddc_service_create( if (!ddc_service) return NULL; - construct(ddc_service, init_data); + ddc_service_construct(ddc_service, init_data); return ddc_service; } -static void destruct(struct ddc_service *ddc) +static void ddc_service_destruct(struct ddc_service *ddc) { if (ddc->ddc_pin) dal_gpio_destroy_ddc(&ddc->ddc_pin); @@ -252,7 +254,7 @@ void dal_ddc_service_destroy(struct ddc_service **ddc) BREAK_TO_DEBUGGER(); return; } - destruct(*ddc); + ddc_service_destruct(*ddc); kfree(*ddc); *ddc = NULL; } @@ -294,12 +296,30 @@ static uint32_t defer_delay_converter_wa( { struct dc_link *link = ddc->link; - if (link->dpcd_caps.branch_dev_id == DP_BRANCH_DEVICE_ID_4 && + if (link->dpcd_caps.dongle_type == DISPLAY_DONGLE_DP_VGA_CONVERTER && + link->dpcd_caps.branch_dev_id == DP_BRANCH_DEVICE_ID_0080E1 && + (link->dpcd_caps.branch_fw_revision[0] < 0x01 || + (link->dpcd_caps.branch_fw_revision[0] == 0x01 && + link->dpcd_caps.branch_fw_revision[1] < 0x40)) && !memcmp(link->dpcd_caps.branch_dev_name, - DP_DVI_CONVERTER_ID_4, + DP_VGA_DONGLE_BRANCH_DEV_NAME, sizeof(link->dpcd_caps.branch_dev_name))) + + return defer_delay > DPVGA_DONGLE_AUX_DEFER_WA_DELAY ? + defer_delay : DPVGA_DONGLE_AUX_DEFER_WA_DELAY; + + if (link->dpcd_caps.branch_dev_id == DP_BRANCH_DEVICE_ID_0080E1 && + !memcmp(link->dpcd_caps.branch_dev_name, + DP_DVI_CONVERTER_ID_4, + sizeof(link->dpcd_caps.branch_dev_name))) return defer_delay > I2C_OVER_AUX_DEFER_WA_DELAY ? defer_delay : I2C_OVER_AUX_DEFER_WA_DELAY; + if (link->dpcd_caps.branch_dev_id == DP_BRANCH_DEVICE_ID_006037 && + !memcmp(link->dpcd_caps.branch_dev_name, + DP_DVI_CONVERTER_ID_5, + sizeof(link->dpcd_caps.branch_dev_name))) + return defer_delay > I2C_OVER_AUX_DEFER_WA_DELAY_1MS ? + I2C_OVER_AUX_DEFER_WA_DELAY_1MS : defer_delay; return defer_delay; } @@ -374,6 +394,7 @@ void dal_ddc_service_i2c_query_dp_dual_mode_adaptor( enum display_dongle_type *dongle = &sink_cap->dongle_type; uint8_t type2_dongle_buf[DP_ADAPTOR_TYPE2_SIZE]; bool is_type2_dongle = false; + int retry_count = 2; struct dp_hdmi_dongle_signature_data *dongle_signature; /* Assume we have no valid DP passive dongle connected */ @@ -386,13 +407,24 @@ void dal_ddc_service_i2c_query_dp_dual_mode_adaptor( DP_HDMI_DONGLE_ADDRESS, type2_dongle_buf, sizeof(type2_dongle_buf))) { - *dongle = DISPLAY_DONGLE_DP_DVI_DONGLE; - sink_cap->max_hdmi_pixel_clock = DP_ADAPTOR_DVI_MAX_TMDS_CLK; + /* Passive HDMI dongles can sometimes fail here without retrying*/ + while (retry_count > 0) { + if (i2c_read(ddc, + DP_HDMI_DONGLE_ADDRESS, + type2_dongle_buf, + sizeof(type2_dongle_buf))) + break; + retry_count--; + } + if (retry_count == 0) { + *dongle = DISPLAY_DONGLE_DP_DVI_DONGLE; + sink_cap->max_hdmi_pixel_clock = DP_ADAPTOR_DVI_MAX_TMDS_CLK; - CONN_DATA_DETECT(ddc->link, type2_dongle_buf, sizeof(type2_dongle_buf), - "DP-DVI passive dongle %dMhz: ", - DP_ADAPTOR_DVI_MAX_TMDS_CLK / 1000); - return; + CONN_DATA_DETECT(ddc->link, type2_dongle_buf, sizeof(type2_dongle_buf), + "DP-DVI passive dongle %dMhz: ", + DP_ADAPTOR_DVI_MAX_TMDS_CLK / 1000); + return; + } } /* Check if Type 2 dongle.*/ @@ -461,6 +493,7 @@ void dal_ddc_service_i2c_query_dp_dual_mode_adaptor( sink_cap->max_hdmi_pixel_clock = max_tmds_clk * 1000; } + sink_cap->is_dongle_type_one = false; } else { if (is_valid_hdmi_signature == true) { @@ -478,6 +511,7 @@ void dal_ddc_service_i2c_query_dp_dual_mode_adaptor( "Type 1 DP-HDMI passive dongle (no signature) %dMhz: ", sink_cap->max_hdmi_pixel_clock / 1000); } + sink_cap->is_dongle_type_one = true; } return; @@ -496,7 +530,7 @@ bool dal_ddc_service_query_ddc_data( uint8_t *read_buf, uint32_t read_size) { - bool ret; + bool success = true; uint32_t payload_size = dal_ddc_service_is_in_aux_transaction_mode(ddc) ? DEFAULT_AUX_MAX_DATA_SIZE : EDID_SEGMENT_SIZE; @@ -509,67 +543,108 @@ bool dal_ddc_service_query_ddc_data( uint32_t payloads_num = write_payloads + read_payloads; - if (write_size > EDID_SEGMENT_SIZE || read_size > EDID_SEGMENT_SIZE) + if (!payloads_num) return false; - /*TODO: len of payload data for i2c and aux is uint8!!!!, - * but we want to read 256 over i2c!!!!*/ if (dal_ddc_service_is_in_aux_transaction_mode(ddc)) { - struct aux_payload write_payload = { - .i2c_over_aux = true, - .write = true, - .mot = true, - .address = address, - .length = write_size, - .data = write_buf, - .reply = NULL, - .defer_delay = get_defer_delay(ddc), - }; - - struct aux_payload read_payload = { - .i2c_over_aux = true, - .write = false, - .mot = false, - .address = address, - .length = read_size, - .data = read_buf, - .reply = NULL, - .defer_delay = get_defer_delay(ddc), - }; - - ret = dc_link_aux_transfer_with_retries(ddc, &write_payload); + struct aux_payload payload; + + payload.i2c_over_aux = true; + payload.address = address; + payload.reply = NULL; + payload.defer_delay = get_defer_delay(ddc); + payload.write_status_update = false; + + if (write_size != 0) { + payload.write = true; + /* should not set mot (middle of transaction) to 0 + * if there are pending read payloads + */ + payload.mot = !(read_size == 0); + payload.length = write_size; + payload.data = write_buf; + + success = dal_ddc_submit_aux_command(ddc, &payload); + } - if (!ret) - return false; + if (read_size != 0 && success) { + payload.write = false; + /* should set mot (middle of transaction) to 0 + * since it is the last payload to send + */ + payload.mot = false; + payload.length = read_size; + payload.data = read_buf; - ret = dc_link_aux_transfer_with_retries(ddc, &read_payload); + success = dal_ddc_submit_aux_command(ddc, &payload); + } } else { - struct i2c_payloads *payloads = - dal_ddc_i2c_payloads_create(ddc->ctx, payloads_num); + struct i2c_command command = {0}; + struct i2c_payloads payloads; + + if (!dal_ddc_i2c_payloads_create(ddc->ctx, &payloads, payloads_num)) + return false; - struct i2c_command command = { - .payloads = dal_ddc_i2c_payloads_get(payloads), - .number_of_payloads = 0, - .engine = DDC_I2C_COMMAND_ENGINE, - .speed = ddc->ctx->dc->caps.i2c_speed_in_khz }; + command.payloads = dal_ddc_i2c_payloads_get(&payloads); + command.number_of_payloads = 0; + command.engine = DDC_I2C_COMMAND_ENGINE; + command.speed = ddc->ctx->dc->caps.i2c_speed_in_khz; dal_ddc_i2c_payloads_add( - payloads, address, write_size, write_buf, true); + &payloads, address, write_size, write_buf, true); dal_ddc_i2c_payloads_add( - payloads, address, read_size, read_buf, false); + &payloads, address, read_size, read_buf, false); command.number_of_payloads = - dal_ddc_i2c_payloads_get_count(payloads); + dal_ddc_i2c_payloads_get_count(&payloads); - ret = dm_helpers_submit_i2c( + success = dm_helpers_submit_i2c( ddc->ctx, ddc->link, &command); - dal_ddc_i2c_payloads_destroy(&payloads); + dal_vector_destruct(&payloads.payloads); } + return success; +} + +bool dal_ddc_submit_aux_command(struct ddc_service *ddc, + struct aux_payload *payload) +{ + uint32_t retrieved = 0; + bool ret = false; + + if (!ddc) + return false; + + if (!payload) + return false; + + do { + struct aux_payload current_payload; + bool is_end_of_payload = (retrieved + DEFAULT_AUX_MAX_DATA_SIZE) >= + payload->length; + uint32_t payload_length = is_end_of_payload ? + payload->length - retrieved : DEFAULT_AUX_MAX_DATA_SIZE; + + current_payload.address = payload->address; + current_payload.data = &payload->data[retrieved]; + current_payload.defer_delay = payload->defer_delay; + current_payload.i2c_over_aux = payload->i2c_over_aux; + current_payload.length = payload_length; + /* set mot (middle of transaction) to false if it is the last payload */ + current_payload.mot = is_end_of_payload ? payload->mot:true; + current_payload.write_status_update = false; + current_payload.reply = payload->reply; + current_payload.write = payload->write; + + ret = dc_link_aux_transfer_with_retries(ddc, ¤t_payload); + + retrieved += payload_length; + } while (retrieved < payload->length && ret == true); + return ret; } @@ -582,9 +657,14 @@ bool dal_ddc_service_query_ddc_data( */ int dc_link_aux_transfer_raw(struct ddc_service *ddc, struct aux_payload *payload, - enum aux_channel_operation_result *operation_result) + enum aux_return_code_type *operation_result) { - return dce_aux_transfer_raw(ddc, payload, operation_result); + if (ddc->ctx->dc->debug.enable_dmub_aux_for_legacy_ddc || + !ddc->ddc_pin) { + return dce_aux_transfer_dmub_raw(ddc, payload, operation_result); + } else { + return dce_aux_transfer_raw(ddc, payload, operation_result); + } } /* dc_link_aux_transfer_with_retries() - Attempt to submit an @@ -601,6 +681,24 @@ bool dc_link_aux_transfer_with_retries(struct ddc_service *ddc, return dce_aux_transfer_with_retries(ddc, payload); } + +bool dc_link_aux_try_to_configure_timeout(struct ddc_service *ddc, + uint32_t timeout) +{ + bool result = false; + struct ddc *ddc_pin = ddc->ddc_pin; + + /* Do not try to access nonexistent DDC pin. */ + if (ddc->link->ep_type != DISPLAY_ENDPOINT_PHY) + return true; + + if (ddc->ctx->dc->res_pool->engines[ddc_pin->pin_data->en]->funcs->configure_timeout) { + ddc->ctx->dc->res_pool->engines[ddc_pin->pin_data->en]->funcs->configure_timeout(ddc, timeout); + result = true; + } + return result; +} + /*test only function*/ void dal_ddc_service_set_ddc_pin( struct ddc_service *ddc_service, @@ -625,6 +723,10 @@ void dal_ddc_service_write_scdc_data(struct ddc_service *ddc_service, uint8_t write_buffer[2] = {0}; /*Lower than 340 Scramble bit from SCDC caps*/ + if (ddc_service->link->local_sink && + ddc_service->link->local_sink->edid_caps.panel_patch.skip_scdc_overwrite) + return; + dal_ddc_service_query_ddc_data(ddc_service, slave_address, &offset, sizeof(offset), &sink_version, sizeof(sink_version)); if (sink_version == 1) { @@ -654,10 +756,14 @@ void dal_ddc_service_read_scdc_data(struct ddc_service *ddc_service) uint8_t offset = HDMI_SCDC_TMDS_CONFIG; uint8_t tmds_config = 0; + if (ddc_service->link->local_sink && + ddc_service->link->local_sink->edid_caps.panel_patch.skip_scdc_overwrite) + return; + dal_ddc_service_query_ddc_data(ddc_service, slave_address, &offset, sizeof(offset), &tmds_config, sizeof(tmds_config)); if (tmds_config & 0x1) { - union hdmi_scdc_status_flags_data status_data = { {0} }; + union hdmi_scdc_status_flags_data status_data = {0}; uint8_t scramble_status = 0; offset = HDMI_SCDC_SCRAMBLER_STATUS; |