| /* |
| * Copyright 2023 Advanced Micro Devices, Inc. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in |
| * all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR |
| * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
| * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
| * OTHER DEALINGS IN THE SOFTWARE. |
| * |
| * Authors: AMD |
| * |
| */ |
| |
| /* FILE POLICY AND INTENDED USAGE: |
| * This file owns the creation/destruction of link structure. |
| */ |
| #include "link_factory.h" |
| #include "link_detection.h" |
| #include "link_resource.h" |
| #include "link_validation.h" |
| #include "link_dpms.h" |
| #include "accessories/link_dp_cts.h" |
| #include "accessories/link_dp_trace.h" |
| #include "protocols/link_ddc.h" |
| #include "protocols/link_dp_capability.h" |
| #include "protocols/link_dp_dpia_bw.h" |
| #include "protocols/link_dp_dpia.h" |
| #include "protocols/link_dp_irq_handler.h" |
| #include "protocols/link_dp_phy.h" |
| #include "protocols/link_dp_training.h" |
| #include "protocols/link_edp_panel_control.h" |
| #include "protocols/link_hpd.h" |
| #include "gpio_service_interface.h" |
| #include "atomfirmware.h" |
| |
| #define DC_LOGGER \ |
| dc_ctx->logger |
| #define DC_LOGGER_INIT(logger) |
| |
| #define LINK_INFO(...) \ |
| DC_LOG_HW_HOTPLUG( \ |
| __VA_ARGS__) |
| |
| /* link factory owns the creation/destruction of link structures. */ |
| static void construct_link_service_factory(struct link_service *link_srv) |
| { |
| |
| link_srv->create_link = link_create; |
| link_srv->destroy_link = link_destroy; |
| } |
| |
| /* link_detection manages link detection states and receiver states by using |
| * various link protocols. It also provides helper functions to interpret |
| * certain capabilities or status based on the states it manages or retrieve |
| * them directly from connected receivers. |
| */ |
| static void construct_link_service_detection(struct link_service *link_srv) |
| { |
| link_srv->detect_link = link_detect; |
| link_srv->detect_connection_type = link_detect_connection_type; |
| link_srv->add_remote_sink = link_add_remote_sink; |
| link_srv->remove_remote_sink = link_remove_remote_sink; |
| link_srv->get_hpd_state = link_get_hpd_state; |
| link_srv->get_hpd_gpio = link_get_hpd_gpio; |
| link_srv->enable_hpd = link_enable_hpd; |
| link_srv->disable_hpd = link_disable_hpd; |
| link_srv->enable_hpd_filter = link_enable_hpd_filter; |
| link_srv->reset_cur_dp_mst_topology = link_reset_cur_dp_mst_topology; |
| link_srv->get_status = link_get_status; |
| link_srv->is_hdcp1x_supported = link_is_hdcp14; |
| link_srv->is_hdcp2x_supported = link_is_hdcp22; |
| link_srv->clear_dprx_states = link_clear_dprx_states; |
| } |
| |
| /* link resource implements accessors to link resource. */ |
| static void construct_link_service_resource(struct link_service *link_srv) |
| { |
| link_srv->get_cur_res_map = link_get_cur_res_map; |
| link_srv->restore_res_map = link_restore_res_map; |
| link_srv->get_cur_link_res = link_get_cur_link_res; |
| } |
| |
| /* link validation owns timing validation against various link limitations. (ex. |
| * link bandwidth, receiver capability or our hardware capability) It also |
| * provides helper functions exposing bandwidth formulas used in validation. |
| */ |
| static void construct_link_service_validation(struct link_service *link_srv) |
| { |
| link_srv->validate_mode_timing = link_validate_mode_timing; |
| link_srv->dp_link_bandwidth_kbps = dp_link_bandwidth_kbps; |
| link_srv->validate_dpia_bandwidth = link_validate_dpia_bandwidth; |
| } |
| |
| /* link dpms owns the programming sequence of stream's dpms state associated |
| * with the link and link's enable/disable sequences as result of the stream's |
| * dpms state change. |
| */ |
| static void construct_link_service_dpms(struct link_service *link_srv) |
| { |
| link_srv->set_dpms_on = link_set_dpms_on; |
| link_srv->set_dpms_off = link_set_dpms_off; |
| link_srv->resume = link_resume; |
| link_srv->blank_all_dp_displays = link_blank_all_dp_displays; |
| link_srv->blank_all_edp_displays = link_blank_all_edp_displays; |
| link_srv->blank_dp_stream = link_blank_dp_stream; |
| link_srv->increase_mst_payload = link_increase_mst_payload; |
| link_srv->reduce_mst_payload = link_reduce_mst_payload; |
| link_srv->set_dsc_on_stream = link_set_dsc_on_stream; |
| link_srv->set_dsc_enable = link_set_dsc_enable; |
| link_srv->update_dsc_config = link_update_dsc_config; |
| } |
| |
| /* link ddc implements generic display communication protocols such as i2c, aux |
| * and scdc. It should not contain any specific applications of these |
| * protocols such as display capability query, detection, or handshaking such as |
| * link training. |
| */ |
| static void construct_link_service_ddc(struct link_service *link_srv) |
| { |
| link_srv->create_ddc_service = link_create_ddc_service; |
| link_srv->destroy_ddc_service = link_destroy_ddc_service; |
| link_srv->query_ddc_data = link_query_ddc_data; |
| link_srv->aux_transfer_raw = link_aux_transfer_raw; |
| link_srv->configure_fixed_vs_pe_retimer = link_configure_fixed_vs_pe_retimer; |
| link_srv->aux_transfer_with_retries_no_mutex = |
| link_aux_transfer_with_retries_no_mutex; |
| link_srv->is_in_aux_transaction_mode = link_is_in_aux_transaction_mode; |
| link_srv->get_aux_defer_delay = link_get_aux_defer_delay; |
| } |
| |
| /* link dp capability implements dp specific link capability retrieval sequence. |
| * It is responsible for retrieving, parsing, overriding, deciding capability |
| * obtained from dp link. Link capability consists of encoders, DPRXs, cables, |
| * retimers, usb and all other possible backend capabilities. |
| */ |
| static void construct_link_service_dp_capability(struct link_service *link_srv) |
| { |
| link_srv->dp_is_sink_present = dp_is_sink_present; |
| link_srv->dp_is_fec_supported = dp_is_fec_supported; |
| link_srv->dp_is_128b_132b_signal = dp_is_128b_132b_signal; |
| link_srv->dp_get_max_link_enc_cap = dp_get_max_link_enc_cap; |
| link_srv->dp_get_verified_link_cap = dp_get_verified_link_cap; |
| link_srv->dp_get_encoding_format = link_dp_get_encoding_format; |
| link_srv->dp_should_enable_fec = dp_should_enable_fec; |
| link_srv->dp_decide_link_settings = link_decide_link_settings; |
| link_srv->mst_decide_link_encoding_format = |
| mst_decide_link_encoding_format; |
| link_srv->edp_decide_link_settings = edp_decide_link_settings; |
| link_srv->bw_kbps_from_raw_frl_link_rate_data = |
| link_bw_kbps_from_raw_frl_link_rate_data; |
| link_srv->dp_overwrite_extended_receiver_cap = |
| dp_overwrite_extended_receiver_cap; |
| link_srv->dp_decide_lttpr_mode = dp_decide_lttpr_mode; |
| } |
| |
| /* link dp phy/dpia implements basic dp phy/dpia functionality such as |
| * enable/disable output and set lane/drive settings. It is responsible for |
| * maintaining and update software state representing current phy/dpia status |
| * such as current link settings. |
| */ |
| static void construct_link_service_dp_phy_or_dpia(struct link_service *link_srv) |
| { |
| link_srv->dpia_handle_usb4_bandwidth_allocation_for_link = |
| dpia_handle_usb4_bandwidth_allocation_for_link; |
| link_srv->dpia_handle_bw_alloc_response = dpia_handle_bw_alloc_response; |
| link_srv->dp_set_drive_settings = dp_set_drive_settings; |
| link_srv->dpcd_write_rx_power_ctrl = dpcd_write_rx_power_ctrl; |
| } |
| |
| /* link dp irq handler implements DP HPD short pulse handling sequence according |
| * to DP specifications |
| */ |
| static void construct_link_service_dp_irq_handler(struct link_service *link_srv) |
| { |
| link_srv->dp_parse_link_loss_status = dp_parse_link_loss_status; |
| link_srv->dp_should_allow_hpd_rx_irq = dp_should_allow_hpd_rx_irq; |
| link_srv->dp_handle_link_loss = dp_handle_link_loss; |
| link_srv->dp_read_hpd_rx_irq_data = dp_read_hpd_rx_irq_data; |
| link_srv->dp_handle_hpd_rx_irq = dp_handle_hpd_rx_irq; |
| } |
| |
| /* link edp panel control implements retrieval and configuration of eDP panel |
| * features such as PSR and ABM and it also manages specs defined eDP panel |
| * power sequences. |
| */ |
| static void construct_link_service_edp_panel_control(struct link_service *link_srv) |
| { |
| link_srv->edp_panel_backlight_power_on = edp_panel_backlight_power_on; |
| link_srv->edp_get_backlight_level = edp_get_backlight_level; |
| link_srv->edp_get_backlight_level_nits = edp_get_backlight_level_nits; |
| link_srv->edp_set_backlight_level = edp_set_backlight_level; |
| link_srv->edp_set_backlight_level_nits = edp_set_backlight_level_nits; |
| link_srv->edp_get_target_backlight_pwm = edp_get_target_backlight_pwm; |
| link_srv->edp_get_psr_state = edp_get_psr_state; |
| link_srv->edp_set_psr_allow_active = edp_set_psr_allow_active; |
| link_srv->edp_setup_psr = edp_setup_psr; |
| link_srv->edp_set_sink_vtotal_in_psr_active = |
| edp_set_sink_vtotal_in_psr_active; |
| link_srv->edp_get_psr_residency = edp_get_psr_residency; |
| |
| link_srv->edp_get_replay_state = edp_get_replay_state; |
| link_srv->edp_set_replay_allow_active = edp_set_replay_allow_active; |
| link_srv->edp_setup_replay = edp_setup_replay; |
| link_srv->edp_send_replay_cmd = edp_send_replay_cmd; |
| link_srv->edp_set_coasting_vtotal = edp_set_coasting_vtotal; |
| link_srv->edp_replay_residency = edp_replay_residency; |
| link_srv->edp_set_replay_power_opt_and_coasting_vtotal = edp_set_replay_power_opt_and_coasting_vtotal; |
| |
| link_srv->edp_wait_for_t12 = edp_wait_for_t12; |
| link_srv->edp_is_ilr_optimization_required = |
| edp_is_ilr_optimization_required; |
| link_srv->edp_backlight_enable_aux = edp_backlight_enable_aux; |
| link_srv->edp_add_delay_for_T9 = edp_add_delay_for_T9; |
| link_srv->edp_receiver_ready_T9 = edp_receiver_ready_T9; |
| link_srv->edp_receiver_ready_T7 = edp_receiver_ready_T7; |
| link_srv->edp_power_alpm_dpcd_enable = edp_power_alpm_dpcd_enable; |
| link_srv->edp_set_panel_power = edp_set_panel_power; |
| } |
| |
| /* link dp cts implements dp compliance test automation protocols and manual |
| * testing interfaces for debugging and certification purpose. |
| */ |
| static void construct_link_service_dp_cts(struct link_service *link_srv) |
| { |
| link_srv->dp_handle_automated_test = dp_handle_automated_test; |
| link_srv->dp_set_test_pattern = dp_set_test_pattern; |
| link_srv->dp_set_preferred_link_settings = |
| dp_set_preferred_link_settings; |
| link_srv->dp_set_preferred_training_settings = |
| dp_set_preferred_training_settings; |
| } |
| |
| /* link dp trace implements tracing interfaces for tracking major dp sequences |
| * including execution status and timestamps |
| */ |
| static void construct_link_service_dp_trace(struct link_service *link_srv) |
| { |
| link_srv->dp_trace_is_initialized = dp_trace_is_initialized; |
| link_srv->dp_trace_set_is_logged_flag = dp_trace_set_is_logged_flag; |
| link_srv->dp_trace_is_logged = dp_trace_is_logged; |
| link_srv->dp_trace_get_lt_end_timestamp = dp_trace_get_lt_end_timestamp; |
| link_srv->dp_trace_get_lt_counts = dp_trace_get_lt_counts; |
| link_srv->dp_trace_get_link_loss_count = dp_trace_get_link_loss_count; |
| link_srv->dp_trace_set_edp_power_timestamp = |
| dp_trace_set_edp_power_timestamp; |
| link_srv->dp_trace_get_edp_poweron_timestamp = |
| dp_trace_get_edp_poweron_timestamp; |
| link_srv->dp_trace_get_edp_poweroff_timestamp = |
| dp_trace_get_edp_poweroff_timestamp; |
| link_srv->dp_trace_source_sequence = dp_trace_source_sequence; |
| } |
| |
| static void construct_link_service(struct link_service *link_srv) |
| { |
| /* All link service functions should fall under some sub categories. |
| * If a new function doesn't perfectly fall under an existing sub |
| * category, it must be that you are either adding a whole new aspect of |
| * responsibility to link service or something doesn't belong to link |
| * service. In that case please contact the arch owner to arrange a |
| * design review meeting. |
| */ |
| construct_link_service_factory(link_srv); |
| construct_link_service_detection(link_srv); |
| construct_link_service_resource(link_srv); |
| construct_link_service_validation(link_srv); |
| construct_link_service_dpms(link_srv); |
| construct_link_service_ddc(link_srv); |
| construct_link_service_dp_capability(link_srv); |
| construct_link_service_dp_phy_or_dpia(link_srv); |
| construct_link_service_dp_irq_handler(link_srv); |
| construct_link_service_edp_panel_control(link_srv); |
| construct_link_service_dp_cts(link_srv); |
| construct_link_service_dp_trace(link_srv); |
| } |
| |
| struct link_service *link_create_link_service(void) |
| { |
| struct link_service *link_srv = kzalloc(sizeof(*link_srv), GFP_KERNEL); |
| |
| if (link_srv == NULL) |
| goto fail; |
| |
| construct_link_service(link_srv); |
| |
| return link_srv; |
| fail: |
| return NULL; |
| } |
| |
| void link_destroy_link_service(struct link_service **link_srv) |
| { |
| kfree(*link_srv); |
| *link_srv = NULL; |
| } |
| |
| static enum transmitter translate_encoder_to_transmitter( |
| struct graphics_object_id encoder) |
| { |
| switch (encoder.id) { |
| case ENCODER_ID_INTERNAL_UNIPHY: |
| switch (encoder.enum_id) { |
| case ENUM_ID_1: |
| return TRANSMITTER_UNIPHY_A; |
| case ENUM_ID_2: |
| return TRANSMITTER_UNIPHY_B; |
| default: |
| return TRANSMITTER_UNKNOWN; |
| } |
| break; |
| case ENCODER_ID_INTERNAL_UNIPHY1: |
| switch (encoder.enum_id) { |
| case ENUM_ID_1: |
| return TRANSMITTER_UNIPHY_C; |
| case ENUM_ID_2: |
| return TRANSMITTER_UNIPHY_D; |
| default: |
| return TRANSMITTER_UNKNOWN; |
| } |
| break; |
| case ENCODER_ID_INTERNAL_UNIPHY2: |
| switch (encoder.enum_id) { |
| case ENUM_ID_1: |
| return TRANSMITTER_UNIPHY_E; |
| case ENUM_ID_2: |
| return TRANSMITTER_UNIPHY_F; |
| default: |
| return TRANSMITTER_UNKNOWN; |
| } |
| break; |
| case ENCODER_ID_INTERNAL_UNIPHY3: |
| switch (encoder.enum_id) { |
| case ENUM_ID_1: |
| return TRANSMITTER_UNIPHY_G; |
| default: |
| return TRANSMITTER_UNKNOWN; |
| } |
| break; |
| case ENCODER_ID_EXTERNAL_NUTMEG: |
| switch (encoder.enum_id) { |
| case ENUM_ID_1: |
| return TRANSMITTER_NUTMEG_CRT; |
| default: |
| return TRANSMITTER_UNKNOWN; |
| } |
| break; |
| case ENCODER_ID_EXTERNAL_TRAVIS: |
| switch (encoder.enum_id) { |
| case ENUM_ID_1: |
| return TRANSMITTER_TRAVIS_CRT; |
| case ENUM_ID_2: |
| return TRANSMITTER_TRAVIS_LCD; |
| default: |
| return TRANSMITTER_UNKNOWN; |
| } |
| break; |
| default: |
| return TRANSMITTER_UNKNOWN; |
| } |
| } |
| |
| static void link_destruct(struct dc_link *link) |
| { |
| int i; |
| |
| if (link->hpd_gpio) { |
| dal_gpio_destroy_irq(&link->hpd_gpio); |
| link->hpd_gpio = NULL; |
| } |
| |
| if (link->ddc) |
| link_destroy_ddc_service(&link->ddc); |
| |
| if (link->panel_cntl) |
| link->panel_cntl->funcs->destroy(&link->panel_cntl); |
| |
| if (link->link_enc) { |
| /* Update link encoder resource tracking variables. These are used for |
| * the dynamic assignment of link encoders to streams. Virtual links |
| * are not assigned encoder resources on creation. |
| */ |
| if (link->link_id.id != CONNECTOR_ID_VIRTUAL && link->eng_id != ENGINE_ID_UNKNOWN) { |
| link->dc->res_pool->link_encoders[link->eng_id - ENGINE_ID_DIGA] = NULL; |
| link->dc->res_pool->dig_link_enc_count--; |
| } |
| link->link_enc->funcs->destroy(&link->link_enc); |
| } |
| |
| if (link->local_sink) |
| dc_sink_release(link->local_sink); |
| |
| for (i = 0; i < link->sink_count; ++i) |
| dc_sink_release(link->remote_sinks[i]); |
| } |
| |
| static enum channel_id get_ddc_line(struct dc_link *link) |
| { |
| struct ddc *ddc; |
| enum channel_id channel; |
| |
| channel = CHANNEL_ID_UNKNOWN; |
| |
| ddc = get_ddc_pin(link->ddc); |
| |
| if (ddc) { |
| switch (dal_ddc_get_line(ddc)) { |
| case GPIO_DDC_LINE_DDC1: |
| channel = CHANNEL_ID_DDC1; |
| break; |
| case GPIO_DDC_LINE_DDC2: |
| channel = CHANNEL_ID_DDC2; |
| break; |
| case GPIO_DDC_LINE_DDC3: |
| channel = CHANNEL_ID_DDC3; |
| break; |
| case GPIO_DDC_LINE_DDC4: |
| channel = CHANNEL_ID_DDC4; |
| break; |
| case GPIO_DDC_LINE_DDC5: |
| channel = CHANNEL_ID_DDC5; |
| break; |
| case GPIO_DDC_LINE_DDC6: |
| channel = CHANNEL_ID_DDC6; |
| break; |
| case GPIO_DDC_LINE_DDC_VGA: |
| channel = CHANNEL_ID_DDC_VGA; |
| break; |
| case GPIO_DDC_LINE_I2C_PAD: |
| channel = CHANNEL_ID_I2C_PAD; |
| break; |
| default: |
| BREAK_TO_DEBUGGER(); |
| break; |
| } |
| } |
| |
| return channel; |
| } |
| |
| static bool construct_phy(struct dc_link *link, |
| const struct link_init_data *init_params) |
| { |
| uint8_t i; |
| struct ddc_service_init_data ddc_service_init_data = { 0 }; |
| struct dc_context *dc_ctx = init_params->ctx; |
| struct encoder_init_data enc_init_data = { 0 }; |
| struct panel_cntl_init_data panel_cntl_init_data = { 0 }; |
| struct dc_bios *bios = init_params->dc->ctx->dc_bios; |
| const struct dc_vbios_funcs *bp_funcs = bios->funcs; |
| struct bp_disp_connector_caps_info disp_connect_caps_info = { 0 }; |
| |
| DC_LOGGER_INIT(dc_ctx->logger); |
| |
| link->irq_source_hpd = DC_IRQ_SOURCE_INVALID; |
| link->irq_source_hpd_rx = DC_IRQ_SOURCE_INVALID; |
| link->link_status.dpcd_caps = &link->dpcd_caps; |
| |
| link->dc = init_params->dc; |
| link->ctx = dc_ctx; |
| link->link_index = init_params->link_index; |
| |
| memset(&link->preferred_training_settings, 0, |
| sizeof(struct dc_link_training_overrides)); |
| memset(&link->preferred_link_setting, 0, |
| sizeof(struct dc_link_settings)); |
| |
| link->link_id = |
| bios->funcs->get_connector_id(bios, init_params->connector_index); |
| |
| link->ep_type = DISPLAY_ENDPOINT_PHY; |
| |
| DC_LOG_DC("BIOS object table - link_id: %d", link->link_id.id); |
| |
| if (bios->funcs->get_disp_connector_caps_info) { |
| bios->funcs->get_disp_connector_caps_info(bios, link->link_id, &disp_connect_caps_info); |
| link->is_internal_display = disp_connect_caps_info.INTERNAL_DISPLAY; |
| DC_LOG_DC("BIOS object table - is_internal_display: %d", link->is_internal_display); |
| } |
| |
| if (link->link_id.type != OBJECT_TYPE_CONNECTOR) { |
| dm_output_to_console("%s: Invalid Connector ObjectID from Adapter Service for connector index:%d! type %d expected %d\n", |
| __func__, init_params->connector_index, |
| link->link_id.type, OBJECT_TYPE_CONNECTOR); |
| goto create_fail; |
| } |
| |
| if (link->dc->res_pool->funcs->link_init) |
| link->dc->res_pool->funcs->link_init(link); |
| |
| link->hpd_gpio = link_get_hpd_gpio(link->ctx->dc_bios, link->link_id, |
| link->ctx->gpio_service); |
| |
| if (link->hpd_gpio) { |
| dal_gpio_open(link->hpd_gpio, GPIO_MODE_INTERRUPT); |
| dal_gpio_unlock_pin(link->hpd_gpio); |
| link->irq_source_hpd = dal_irq_get_source(link->hpd_gpio); |
| |
| DC_LOG_DC("BIOS object table - hpd_gpio id: %d", link->hpd_gpio->id); |
| DC_LOG_DC("BIOS object table - hpd_gpio en: %d", link->hpd_gpio->en); |
| } |
| |
| switch (link->link_id.id) { |
| case CONNECTOR_ID_HDMI_TYPE_A: |
| link->connector_signal = SIGNAL_TYPE_HDMI_TYPE_A; |
| |
| break; |
| case CONNECTOR_ID_SINGLE_LINK_DVID: |
| case CONNECTOR_ID_SINGLE_LINK_DVII: |
| link->connector_signal = SIGNAL_TYPE_DVI_SINGLE_LINK; |
| break; |
| case CONNECTOR_ID_DUAL_LINK_DVID: |
| case CONNECTOR_ID_DUAL_LINK_DVII: |
| link->connector_signal = SIGNAL_TYPE_DVI_DUAL_LINK; |
| break; |
| case CONNECTOR_ID_DISPLAY_PORT: |
| case CONNECTOR_ID_USBC: |
| link->connector_signal = SIGNAL_TYPE_DISPLAY_PORT; |
| |
| if (link->hpd_gpio) |
| link->irq_source_hpd_rx = |
| dal_irq_get_rx_source(link->hpd_gpio); |
| |
| break; |
| case CONNECTOR_ID_EDP: |
| link->connector_signal = SIGNAL_TYPE_EDP; |
| |
| if (link->hpd_gpio) { |
| if (!link->dc->config.allow_edp_hotplug_detection) |
| link->irq_source_hpd = DC_IRQ_SOURCE_INVALID; |
| |
| switch (link->dc->config.allow_edp_hotplug_detection) { |
| case HPD_EN_FOR_ALL_EDP: |
| link->irq_source_hpd_rx = |
| dal_irq_get_rx_source(link->hpd_gpio); |
| break; |
| case HPD_EN_FOR_PRIMARY_EDP_ONLY: |
| if (link->link_index == 0) |
| link->irq_source_hpd_rx = |
| dal_irq_get_rx_source(link->hpd_gpio); |
| else |
| link->irq_source_hpd = DC_IRQ_SOURCE_INVALID; |
| break; |
| case HPD_EN_FOR_SECONDARY_EDP_ONLY: |
| if (link->link_index == 1) |
| link->irq_source_hpd_rx = |
| dal_irq_get_rx_source(link->hpd_gpio); |
| else |
| link->irq_source_hpd = DC_IRQ_SOURCE_INVALID; |
| break; |
| default: |
| link->irq_source_hpd = DC_IRQ_SOURCE_INVALID; |
| break; |
| } |
| } |
| |
| break; |
| case CONNECTOR_ID_LVDS: |
| link->connector_signal = SIGNAL_TYPE_LVDS; |
| break; |
| default: |
| DC_LOG_WARNING("Unsupported Connector type:%d!\n", |
| link->link_id.id); |
| goto create_fail; |
| } |
| |
| LINK_INFO("Connector[%d] description: signal: %s\n", |
| init_params->connector_index, |
| signal_type_to_string(link->connector_signal)); |
| |
| ddc_service_init_data.ctx = link->ctx; |
| ddc_service_init_data.id = link->link_id; |
| ddc_service_init_data.link = link; |
| link->ddc = link_create_ddc_service(&ddc_service_init_data); |
| |
| if (!link->ddc) { |
| DC_ERROR("Failed to create ddc_service!\n"); |
| goto ddc_create_fail; |
| } |
| |
| if (!link->ddc->ddc_pin) { |
| DC_ERROR("Failed to get I2C info for connector!\n"); |
| goto ddc_create_fail; |
| } |
| |
| link->ddc_hw_inst = |
| dal_ddc_get_line(get_ddc_pin(link->ddc)); |
| |
| enc_init_data.ctx = dc_ctx; |
| bp_funcs->get_src_obj(dc_ctx->dc_bios, link->link_id, 0, |
| &enc_init_data.encoder); |
| enc_init_data.connector = link->link_id; |
| enc_init_data.channel = get_ddc_line(link); |
| enc_init_data.hpd_source = get_hpd_line(link); |
| |
| link->hpd_src = enc_init_data.hpd_source; |
| |
| enc_init_data.transmitter = |
| translate_encoder_to_transmitter(enc_init_data.encoder); |
| link->link_enc = |
| link->dc->res_pool->funcs->link_enc_create(dc_ctx, &enc_init_data); |
| |
| DC_LOG_DC("BIOS object table - DP_IS_USB_C: %d", link->link_enc->features.flags.bits.DP_IS_USB_C); |
| DC_LOG_DC("BIOS object table - IS_DP2_CAPABLE: %d", link->link_enc->features.flags.bits.IS_DP2_CAPABLE); |
| |
| if (!link->link_enc) { |
| DC_ERROR("Failed to create link encoder!\n"); |
| goto link_enc_create_fail; |
| } |
| |
| /* Update link encoder tracking variables. These are used for the dynamic |
| * assignment of link encoders to streams. |
| */ |
| link->eng_id = link->link_enc->preferred_engine; |
| link->dc->res_pool->link_encoders[link->eng_id - ENGINE_ID_DIGA] = link->link_enc; |
| link->dc->res_pool->dig_link_enc_count++; |
| |
| link->link_enc_hw_inst = link->link_enc->transmitter; |
| |
| if (link->dc->res_pool->funcs->panel_cntl_create && |
| (link->link_id.id == CONNECTOR_ID_EDP || |
| link->link_id.id == CONNECTOR_ID_LVDS)) { |
| panel_cntl_init_data.ctx = dc_ctx; |
| panel_cntl_init_data.inst = panel_cntl_init_data.ctx->dc_edp_id_count; |
| panel_cntl_init_data.eng_id = link->eng_id; |
| link->panel_cntl = |
| link->dc->res_pool->funcs->panel_cntl_create( |
| &panel_cntl_init_data); |
| panel_cntl_init_data.ctx->dc_edp_id_count++; |
| |
| if (link->panel_cntl == NULL) { |
| DC_ERROR("Failed to create link panel_cntl!\n"); |
| goto panel_cntl_create_fail; |
| } |
| } |
| for (i = 0; i < 4; i++) { |
| if (bp_funcs->get_device_tag(dc_ctx->dc_bios, |
| link->link_id, i, |
| &link->device_tag) != BP_RESULT_OK) { |
| DC_ERROR("Failed to find device tag!\n"); |
| goto device_tag_fail; |
| } |
| |
| /* Look for device tag that matches connector signal, |
| * CRT for rgb, LCD for other supported signal tyes |
| */ |
| if (!bp_funcs->is_device_id_supported(dc_ctx->dc_bios, |
| link->device_tag.dev_id)) |
| continue; |
| if (link->device_tag.dev_id.device_type == DEVICE_TYPE_CRT && |
| link->connector_signal != SIGNAL_TYPE_RGB) |
| continue; |
| if (link->device_tag.dev_id.device_type == DEVICE_TYPE_LCD && |
| link->connector_signal == SIGNAL_TYPE_RGB) |
| continue; |
| |
| DC_LOG_DC("BIOS object table - device_tag.acpi_device: %d", link->device_tag.acpi_device); |
| DC_LOG_DC("BIOS object table - device_tag.dev_id.device_type: %d", link->device_tag.dev_id.device_type); |
| DC_LOG_DC("BIOS object table - device_tag.dev_id.enum_id: %d", link->device_tag.dev_id.enum_id); |
| break; |
| } |
| |
| if (bios->integrated_info) { |
| /* Look for channel mapping corresponding to connector and device tag */ |
| for (i = 0; i < MAX_NUMBER_OF_EXT_DISPLAY_PATH; i++) { |
| struct external_display_path *path = |
| &bios->integrated_info->ext_disp_conn_info.path[i]; |
| |
| if (path->device_connector_id.enum_id == link->link_id.enum_id && |
| path->device_connector_id.id == link->link_id.id && |
| path->device_connector_id.type == link->link_id.type) { |
| if (link->device_tag.acpi_device != 0 && |
| path->device_acpi_enum == link->device_tag.acpi_device) { |
| link->ddi_channel_mapping = path->channel_mapping; |
| link->chip_caps = path->caps; |
| DC_LOG_DC("BIOS object table - ddi_channel_mapping: 0x%04X", |
| link->ddi_channel_mapping.raw); |
| DC_LOG_DC("BIOS object table - chip_caps: %d", |
| link->chip_caps); |
| } else if (path->device_tag == |
| link->device_tag.dev_id.raw_device_tag) { |
| link->ddi_channel_mapping = path->channel_mapping; |
| link->chip_caps = path->caps; |
| DC_LOG_DC("BIOS object table - ddi_channel_mapping: 0x%04X", |
| link->ddi_channel_mapping.raw); |
| DC_LOG_DC("BIOS object table - chip_caps: %d", |
| link->chip_caps); |
| } |
| |
| if (link->chip_caps & EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN) { |
| link->bios_forced_drive_settings.VOLTAGE_SWING = |
| (bios->integrated_info->ext_disp_conn_info.fixdpvoltageswing & 0x3); |
| link->bios_forced_drive_settings.PRE_EMPHASIS = |
| ((bios->integrated_info->ext_disp_conn_info.fixdpvoltageswing >> 2) & 0x3); |
| } |
| |
| break; |
| } |
| } |
| } |
| if (bios->funcs->get_atom_dc_golden_table) |
| bios->funcs->get_atom_dc_golden_table(bios); |
| |
| /* |
| * TODO check if GPIO programmed correctly |
| * |
| * If GPIO isn't programmed correctly HPD might not rise or drain |
| * fast enough, leading to bounces. |
| */ |
| program_hpd_filter(link); |
| |
| link->psr_settings.psr_vtotal_control_support = false; |
| link->psr_settings.psr_version = DC_PSR_VERSION_UNSUPPORTED; |
| |
| DC_LOG_DC("BIOS object table - %s finished successfully.\n", __func__); |
| return true; |
| device_tag_fail: |
| link->link_enc->funcs->destroy(&link->link_enc); |
| link_enc_create_fail: |
| if (link->panel_cntl != NULL) |
| link->panel_cntl->funcs->destroy(&link->panel_cntl); |
| panel_cntl_create_fail: |
| link_destroy_ddc_service(&link->ddc); |
| ddc_create_fail: |
| create_fail: |
| |
| if (link->hpd_gpio) { |
| dal_gpio_destroy_irq(&link->hpd_gpio); |
| link->hpd_gpio = NULL; |
| } |
| |
| DC_LOG_DC("BIOS object table - %s failed.\n", __func__); |
| return false; |
| } |
| |
| static bool construct_dpia(struct dc_link *link, |
| const struct link_init_data *init_params) |
| { |
| struct ddc_service_init_data ddc_service_init_data = { 0 }; |
| struct dc_context *dc_ctx = init_params->ctx; |
| |
| DC_LOGGER_INIT(dc_ctx->logger); |
| |
| /* Initialized irq source for hpd and hpd rx */ |
| link->irq_source_hpd = DC_IRQ_SOURCE_INVALID; |
| link->irq_source_hpd_rx = DC_IRQ_SOURCE_INVALID; |
| link->link_status.dpcd_caps = &link->dpcd_caps; |
| |
| link->dc = init_params->dc; |
| link->ctx = dc_ctx; |
| link->link_index = init_params->link_index; |
| |
| memset(&link->preferred_training_settings, 0, |
| sizeof(struct dc_link_training_overrides)); |
| memset(&link->preferred_link_setting, 0, |
| sizeof(struct dc_link_settings)); |
| |
| /* Dummy Init for linkid */ |
| link->link_id.type = OBJECT_TYPE_CONNECTOR; |
| link->link_id.id = CONNECTOR_ID_DISPLAY_PORT; |
| link->link_id.enum_id = ENUM_ID_1 + init_params->connector_index; |
| link->is_internal_display = false; |
| link->connector_signal = SIGNAL_TYPE_DISPLAY_PORT; |
| LINK_INFO("Connector[%d] description:signal %d\n", |
| init_params->connector_index, |
| link->connector_signal); |
| |
| link->ep_type = DISPLAY_ENDPOINT_USB4_DPIA; |
| link->is_dig_mapping_flexible = true; |
| |
| /* TODO: Initialize link : funcs->link_init */ |
| |
| ddc_service_init_data.ctx = link->ctx; |
| ddc_service_init_data.id = link->link_id; |
| ddc_service_init_data.link = link; |
| /* Set indicator for dpia link so that ddc wont be created */ |
| ddc_service_init_data.is_dpia_link = true; |
| |
| link->ddc = link_create_ddc_service(&ddc_service_init_data); |
| if (!link->ddc) { |
| DC_ERROR("Failed to create ddc_service!\n"); |
| goto ddc_create_fail; |
| } |
| |
| /* Set dpia port index : 0 to number of dpia ports */ |
| link->ddc_hw_inst = init_params->connector_index; |
| |
| // Assign Dpia preferred eng_id |
| if (link->dc->res_pool->funcs->get_preferred_eng_id_dpia) |
| link->dpia_preferred_eng_id = link->dc->res_pool->funcs->get_preferred_eng_id_dpia(link->ddc_hw_inst); |
| |
| /* TODO: Create link encoder */ |
| |
| link->psr_settings.psr_version = DC_PSR_VERSION_UNSUPPORTED; |
| |
| /* Some docks seem to NAK I2C writes to segment pointer with mot=0. */ |
| link->wa_flags.dp_mot_reset_segment = true; |
| |
| return true; |
| |
| ddc_create_fail: |
| return false; |
| } |
| |
| static bool link_construct(struct dc_link *link, |
| const struct link_init_data *init_params) |
| { |
| /* Handle dpia case */ |
| if (init_params->is_dpia_link == true) |
| return construct_dpia(link, init_params); |
| else |
| return construct_phy(link, init_params); |
| } |
| |
| struct dc_link *link_create(const struct link_init_data *init_params) |
| { |
| struct dc_link *link = |
| kzalloc(sizeof(*link), GFP_KERNEL); |
| |
| if (NULL == link) |
| goto alloc_fail; |
| |
| if (false == link_construct(link, init_params)) |
| goto construct_fail; |
| |
| return link; |
| |
| construct_fail: |
| kfree(link); |
| |
| alloc_fail: |
| return NULL; |
| } |
| |
| void link_destroy(struct dc_link **link) |
| { |
| link_destruct(*link); |
| kfree(*link); |
| *link = NULL; |
| } |