diff options
46 files changed, 16598 insertions, 1 deletions
diff --git a/drivers/gpu/drm/amd/dal/Makefile b/drivers/gpu/drm/amd/dal/Makefile index 9e1356875c12..dfe302dd7dbe 100644 --- a/drivers/gpu/drm/amd/dal/Makefile +++ b/drivers/gpu/drm/amd/dal/Makefile @@ -8,7 +8,7 @@ AMDDALPATH = $(RELATIVE_AMD_DAL_PATH) subdir-ccflags-y += -I$(AMDDALPATH)/ -I$(AMDDALPATH)/include -DDAL_CZ_BRINGUP DAL_LIBS = adapter amdgpu_dm audio asic_capability basics bios connector \ - controller encoder gpio gpu i2caux irq timing_service + controller dcs encoder gpio gpu i2caux irq timing_service AMD_DAL = $(addsuffix /Makefile, $(addprefix $(FULL_AMD_DAL_PATH)/,$(DAL_LIBS))) diff --git a/drivers/gpu/drm/amd/dal/dcs/Makefile b/drivers/gpu/drm/amd/dal/dcs/Makefile new file mode 100644 index 000000000000..9255add75d4f --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/Makefile @@ -0,0 +1,16 @@ +# +# Makefile for the 'gpu' sub-component of DAL. +# It provides the control and status of HW adapter resources, +# that are global for the ASIC and sharable between pipes. + +EDID = edid/edid_base.o edid/edid13.o edid/edid14.o edid/edid20.o \ + edid/edid_ext_cea.o edid/edid_ext_di.o edid/edid_ext_vtb.o \ + edid/edid_ext_unknown.o edid/edid.o edid_mgr.o edid_patch.o + +DCS = $(EDID) dcs.o dcs_list.o default_modes_dco.o vbios_dco.o ddc_service.o \ + ddc_i2caux_helper.o monitor_tables.o \ + remote_display_receiver_modes.o + +AMD_DAL_DCS = $(addprefix $(AMDDALPATH)/dcs/,$(DCS)) + +AMD_DAL_FILES += $(AMD_DAL_DCS) diff --git a/drivers/gpu/drm/amd/dal/dcs/dcs.c b/drivers/gpu/drm/amd/dal/dcs/dcs.c new file mode 100644 index 000000000000..bb3d111d6c7c --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/dcs.c @@ -0,0 +1,3142 @@ +/* + * Copyright 2012-15 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 + * + */ +#include "dal_services.h" + +#include "include/adapter_service_interface.h" +#include "include/timing_service_interface.h" +#include "include/dcs_interface.h" +#include "include/ddc_service_interface.h" +#include "include/asic_capability_types.h" +#include "include/audio_types.h" +#include "include/grph_object_ctrl_defs.h" +#include "include/bit_set.h" +#include "include/hw_sequencer_types.h" +#include "include/logger_interface.h" + +#include "edid_mgr.h" +#include "edid_patch.h" +#include "vbios_dco.h" +#include "default_modes_dco.h" +#include "ddc_service.h" +#include "remote_display_receiver_modes.h" /* Wireless */ + +struct dcs_flags { + uint32_t SS_SUPPORTED:1; + uint32_t DP_AUDIO_FORCED:1; + uint32_t DISABLE_DP_AUDIO:1; + uint32_t DISABLE_MONITOR_RANGE_LIMITS_ON_CRT:1; + uint32_t REPORT_SINGLE_SELECTED_TIMING:1; + uint32_t CONTAINER_ID_INITIALIZED:1; + uint32_t DEEP_COLOR_OVER_DP_DONGLE:1; + uint32_t HIGH_PIXEL_CLK_OVER_DP_DONGLE:1; + uint32_t TILED_DISPLAY_CAN_SCALE:1; + uint32_t DP_Y_ONLY:1; +}; + +struct dco_funcs { + bool (*add_mode_timing)( + struct dcs_mode_timing_list *list, + struct timing_service *ts); +}; + +struct dcs { + struct dal_context *dal; + struct adapter_service *as; + struct timing_service *ts; + struct ddc_service *ddc_service; + struct edid_mgr *edid_mgr; + struct dcs_flags flags; + struct dcs_container_id container_id; + struct dcs_cea_audio_mode_list *audio_modes; + struct dcs_cea_audio_mode_list *default_wireless_audio_modes; + struct remote_display_receiver_modes *rdrm; + struct display_sink_capability sink_caps; + enum dcs_interface_type display_type; + enum dcs_packed_pixel_format packed_pixel_format; + struct dcs_edid_supported_max_bw edid_max_bw; + struct timing_limits timing_limits; + struct drr_config drr_config; + + struct graphics_object_id graphics_object_id; + + struct vbios_dco *vbios_dco; + + struct dco_funcs dco_funcs; + + struct dcs_stereo_3d_features stereo_3d_features[TIMING_3D_FORMAT_MAX]; + union stereo_3d_support stereo_3d_support; + bool supports_miracast; + + bool dp_y_only; +}; + +static bool get_default_color_depth( + struct dcs *dcs, + struct display_color_depth_support *color_depth) +{ + struct edid_base *edid = NULL; + + color_depth->mask = 0; + color_depth->deep_color_native_res_only = false; + + /* Wireless has fixed color depth support */ + if (dcs->display_type == INTERFACE_TYPE_WIRELESS) { + color_depth->mask |= COLOR_DEPTH_INDEX_888; + return true; + } + + /* Get color depth from EDID base block first */ + if (dcs->edid_mgr != NULL) { + edid = dal_edid_mgr_get_edid(dcs->edid_mgr); + if (edid != NULL && edid->error.BAD_CHECKSUM) + edid = NULL; + } + + if (edid != NULL && + edid->funcs->get_display_color_depth( + edid, color_depth)) + return true; + + /* If EDID does not have color depth info, use fixed value */ + switch (dcs->display_type) { + case INTERFACE_TYPE_TV: + case INTERFACE_TYPE_CV: + color_depth->mask |= COLOR_DEPTH_INDEX_101010; + break; + case INTERFACE_TYPE_VGA: + case INTERFACE_TYPE_DVI: + case INTERFACE_TYPE_LVDS: + case INTERFACE_TYPE_WIRELESS: + color_depth->mask |= COLOR_DEPTH_INDEX_888; + break; + case INTERFACE_TYPE_DP: + color_depth->mask |= COLOR_DEPTH_INDEX_666; + break; + case INTERFACE_TYPE_EDP: + color_depth->mask |= COLOR_DEPTH_INDEX_888; + color_depth->mask |= COLOR_DEPTH_INDEX_666; + break; + + default: + /* unhandled signal */ + dal_logger_write(dcs->dal->logger, + LOG_MAJOR_DCS, + LOG_MINOR_COMPONENT_DISPLAY_CAPABILITY_SERVICE, + "%s: unhandled signal type", + __func__); + break; + } + + return color_depth->mask != 0; +} + +static bool get_default_pixel_encoding( + struct dcs *dcs, + struct display_pixel_encoding_support *pixel_encoding) +{ + struct edid_base *edid = NULL; + + pixel_encoding->mask = 0; + + /* Wireless has fixed color depth support */ + if (dcs->display_type == INTERFACE_TYPE_WIRELESS) { + pixel_encoding->mask |= PIXEL_ENCODING_MASK_YCBCR444; + return true; + } + + /* Get pixel encoding from EDID base block first */ + if (dcs->edid_mgr != NULL) { + edid = dal_edid_mgr_get_edid(dcs->edid_mgr); + if (edid != NULL && edid->error.BAD_CHECKSUM) + edid = NULL; + } + + if (edid != NULL && + edid->funcs->get_display_pixel_encoding( + edid, pixel_encoding)) + return true; + + + switch (dcs->display_type) { + case INTERFACE_TYPE_TV: + case INTERFACE_TYPE_CV: + pixel_encoding->mask |= PIXEL_ENCODING_MASK_YCBCR422; + break; + case INTERFACE_TYPE_VGA: + case INTERFACE_TYPE_DVI: + case INTERFACE_TYPE_LVDS: + case INTERFACE_TYPE_EDP: + case INTERFACE_TYPE_DP: + pixel_encoding->mask |= PIXEL_ENCODING_MASK_RGB; + break; + default: + /* unhandled signal */ + dal_logger_write(dcs->dal->logger, + LOG_MAJOR_DCS, + LOG_MINOR_COMPONENT_DISPLAY_CAPABILITY_SERVICE, + "%s: unhandled signal type", + __func__); + break; + } + + return pixel_encoding->mask != 0; +} + +static void add_dp_default_audio_modes( + struct dcs_cea_audio_mode_list *audio_modes) +{ + /* CEA-861 short audio descriptor + * byte[0]:[6-3] - Audio Format Code + * byte[0]:[2-0] - (Channels - 1) + * byte[1]:[6-0] - sample rate + * bit 0 - 32kHz, + * bit 1 - 44.1 kHz, + * bit 2 - 48 kHz + * bit 3 - 88.2 kHz + * bit 4 - 96 kHz + * bit 5 - 176.4 kHz + * bit 6 - 192 kHz + * byte[2]:[2-0] for Linear PCM defines sample size + * bit 0 - 16 bit + * bit 1 - 20 bit + * bit 2 - 24 bit*/ + struct cea_audio_mode audio_mode = { 0 }; + + audio_mode.format_code = AUDIO_FORMAT_CODE_LINEARPCM; + audio_mode.channel_count = 2;/* 2 channels */ + audio_mode.sample_rate = 7;/* 32, 44.1, 48 kHz supported */ + audio_mode.sample_size = 1;/* 16bit */ + dal_dcs_cea_audio_mode_list_append(audio_modes, &audio_mode); +} + +static void add_dp_forced_audio_modes( + struct dcs_cea_audio_mode_list *audio_modes) +{ + /* CEA-861 short audio descriptor*/ + struct cea_audio_mode audio_mode = { 0 }; + + audio_mode.format_code = AUDIO_FORMAT_CODE_LINEARPCM; + audio_mode.channel_count = 8;/* 8 channels */ + /*32, 44.1, 48, 88.2, 96, 176.4 kHz */ + audio_mode.sample_rate = 0x3F; + audio_mode.sample_size = 5;/* 16bit + 24bit*/ + dal_dcs_cea_audio_mode_list_append(audio_modes, &audio_mode); + + audio_mode.format_code = AUDIO_FORMAT_CODE_AC3; + audio_mode.channel_count = 8;/* 8 channels */ + audio_mode.sample_rate = 7;/* 32, 44.1, 48 kHz supported */ + audio_mode.max_bit_rate = 1;/*maximum bit rate in [8kHz] units*/ + dal_dcs_cea_audio_mode_list_append(audio_modes, &audio_mode); +} + +static void add_hdmi_default_audio_modes( + struct dcs_cea_audio_mode_list *audio_modes) +{ + /* CEA-861 short audio descriptor*/ + struct cea_audio_mode audio_mode = { 0 }; + + audio_mode.format_code = AUDIO_FORMAT_CODE_LINEARPCM; + audio_mode.channel_count = 2;/* 2 channels */ + audio_mode.sample_rate = 7;/* 32, 44.1, 48 kHz supported */ + audio_mode.sample_size = 1;/* 16bit */ + dal_dcs_cea_audio_mode_list_append(audio_modes, &audio_mode); +} + +static void add_wireless_default_audio_modes( + struct dcs *dcs) +{ + /* whether wireless display supports audio or + * not depends solely on Receiver cap */ + if (dcs->default_wireless_audio_modes != NULL && dcs->rdrm != NULL) { + uint32_t i = 0; + + for (i = 0; i < dal_dcs_cea_audio_mode_list_get_count( + dcs->default_wireless_audio_modes); i++) { + + /* + * For wireless displays, it is possible + * that the receiver only supports a + * subset of the audio modes that are + * encoded in the sampling rate and + * bitdepth fields (if LPCM). The + * interface will return true if some + * subset is supported, and return + * the actual subset. This is the + * mode that should be appended. + */ + struct cea_audio_mode resultant_cea_audio_mode; + + if (dal_rdr_get_supported_cea_audio_mode( + dcs->rdrm, + dal_dcs_cea_audio_mode_list_at_index( + dcs->default_wireless_audio_modes, i), + &resultant_cea_audio_mode)) + dal_dcs_cea_audio_mode_list_append( + dcs->audio_modes, + &resultant_cea_audio_mode); + } + } + +} + +static void build_audio_modes( + struct dcs *dcs) +{ + enum dcs_edid_connector_type connector_type; + struct edid_base *edid_base = NULL; + + if (!dcs->audio_modes) + return; + + if (dcs->edid_mgr) + edid_base = dal_edid_mgr_get_edid(dcs->edid_mgr); + + dal_dcs_cea_audio_mode_list_clear(dcs->audio_modes); + + /* Wireless display does not consider EDID since receiver + * can support secondary sink*/ + if (edid_base && dcs->display_type != INTERFACE_TYPE_WIRELESS) + dal_edid_get_cea_audio_modes(edid_base, dcs->audio_modes); + + switch (dcs->display_type) { + case INTERFACE_TYPE_LVDS: + case INTERFACE_TYPE_EDP: + /*eDP Audio is not supported, + * disable it to enable Audio on external DP monitor */ + dal_dcs_cea_audio_mode_list_clear(dcs->audio_modes); + return; + case INTERFACE_TYPE_WIRELESS: + add_wireless_default_audio_modes(dcs); + return; + default: + break; + } + + connector_type = dal_dcs_get_connector_type(dcs); + + switch (connector_type) { + case EDID_CONNECTOR_DISPLAYPORT: + /*DP Audio is disabled by default (till DP Audio in production)*/ + if (dcs->flags.DISABLE_DP_AUDIO) { + dal_dcs_cea_audio_mode_list_clear(dcs->audio_modes); + return; + } + /* If panel has any Audio support, we must add default + * fail-safe audio for VESA Compliance for DP*/ + /* The fail-safe audio mode is 32, 44, 48, uncompressed PCM */ + if (dal_dcs_cea_audio_mode_list_get_count(dcs->audio_modes)) + add_dp_default_audio_modes(dcs->audio_modes); + /*If DP Audio is forced even though panel doesn't support + * Audio we add it */ + else if (dcs->flags.DP_AUDIO_FORCED) + add_dp_forced_audio_modes(dcs->audio_modes); + + break; + case EDID_CONNECTOR_HDMIA: { + struct cea861_support cea861_support = { 0 }; + + if (dal_dcs_cea_audio_mode_list_get_count(dcs->audio_modes)) + return; + + dal_dcs_get_cea861_support(dcs, &cea861_support); + + if (!dal_adapter_service_is_feature_supported( + FEATURE_ALLOW_HDMI_WITHOUT_AUDIO) || + cea861_support.features.BASE_AUDIO) + add_hdmi_default_audio_modes(dcs->audio_modes); + } + break; + default: + break; + } +} + +static bool is_miracast_supported(struct dcs *dcs) +{ + if (CONNECTOR_ID_MIRACAST != dal_graphics_object_id_get_connector_id( + dcs->graphics_object_id)) + return false; + + if (0 == dal_adapter_service_get_asic_runtime_flags(dcs->as).bits. + MIRACAST_SUPPORTED) + return false; + + return true; +} + +static bool construct( + struct dcs *dcs, + const struct dcs_init_data *init_data) +{ + if (!init_data->as) + return false; + + if (!init_data->ts) + return false; + + dcs->dal = init_data->dal; + dcs->as = init_data->as; + dcs->ts = init_data->ts; + dcs->display_type = init_data->interface_type; + dcs->graphics_object_id = init_data->grph_obj_id; + + if (dcs->display_type != INTERFACE_TYPE_CF && + dcs->display_type != INTERFACE_TYPE_CV && + dcs->display_type != INTERFACE_TYPE_TV) { + + dcs->edid_mgr = dal_edid_mgr_create(dcs->ts, dcs->as); + if (!dcs->edid_mgr) + return false; + } + + /*TODO: add initializers*/ + switch (dcs->display_type) { + case INTERFACE_TYPE_VGA: + dcs->dco_funcs.add_mode_timing = + dal_default_modes_dco_multi_sync_dco_add_mode_timing; + break; + case INTERFACE_TYPE_DP: + case INTERFACE_TYPE_DVI: + dcs->audio_modes = dal_dcs_cea_audio_mode_list_create(64); + if (!dcs->audio_modes) + goto fail; + dcs->dco_funcs.add_mode_timing = + dal_default_modes_dco_add_mode_timing; + break; + + case INTERFACE_TYPE_EDP: + case INTERFACE_TYPE_LVDS: + dcs->vbios_dco = dal_vbios_dco_create(dcs->as); + if (!dcs->vbios_dco) + goto fail; + dcs->flags.SS_SUPPORTED = + dal_vbios_dco_is_pixel_clk_ss_supported(dcs->vbios_dco); + break; + case INTERFACE_TYPE_WIRELESS: + dcs->audio_modes = dal_dcs_cea_audio_mode_list_create(64); + if (!dcs->audio_modes) + goto fail; + + dcs->supports_miracast = is_miracast_supported(dcs); + + { + struct remote_display_receiver_modes_init_data + rdrm_init_data; + + dal_memset(&rdrm_init_data, 0, sizeof(rdrm_init_data)); + + rdrm_init_data.supports_miracast = + dcs->supports_miracast; + + rdrm_init_data.ts = dcs->ts; + + dcs->rdrm = dal_remote_display_receiver_modes_create( + &rdrm_init_data); + if (!dcs->rdrm) + goto fail; + } + + dcs->default_wireless_audio_modes = + dal_dcs_cea_audio_mode_list_create(64); + if (!dcs->default_wireless_audio_modes) + goto fail; + { + struct cea_audio_mode audio_modes_uncompressed; + /*uncompressed PCM*/ + audio_modes_uncompressed.format_code = + AUDIO_FORMAT_CODE_LINEARPCM; + /*stereo - two channels*/ + audio_modes_uncompressed.channel_count = 2; + /*only 48 kHz supported*/ + audio_modes_uncompressed.sample_rate = 4; + /*sample size in [bit]s*/ + audio_modes_uncompressed.sample_size = 7; + + dal_dcs_cea_audio_mode_list_append( + dcs->default_wireless_audio_modes, + &audio_modes_uncompressed); + + /*TODO: Add wireless + * compressed audio + * feature code here*/ + } + + dcs->dco_funcs.add_mode_timing = + dal_default_wireless_dco_add_mode_timing; + break; + + default: + break; + } + + if (dal_graphics_object_id_get_connector_id(dcs->graphics_object_id) == + CONNECTOR_ID_DISPLAY_PORT && + dal_adapter_service_is_feature_supported( + FEATURE_ALLOW_HDMI_HIGH_CLK_DP_DONGLE)) { + dcs->flags.DEEP_COLOR_OVER_DP_DONGLE = true; + dcs->flags.HIGH_PIXEL_CLK_OVER_DP_DONGLE = true; + } + + return true; + +fail: + if (dcs->edid_mgr) + dal_edid_mgr_destroy(&dcs->edid_mgr); + + if (dcs->rdrm) + dal_remote_display_receiver_modes_destroy(&dcs->rdrm); + + return false; + +} + +struct dcs *dal_dcs_create(const struct dcs_init_data *init_data) +{ + struct dcs *dcs = dal_alloc(sizeof(struct dcs)); + + if (!dcs) + return NULL; + + if (construct(dcs, init_data)) + return dcs; + + dal_free(dcs); + return NULL; +} + +static void destruct( + struct dcs *dcs) +{ + if (dcs->edid_mgr) + dal_edid_mgr_destroy(&dcs->edid_mgr); + + if (dcs->audio_modes) + dal_dcs_cea_audio_mode_list_destroy(&dcs->audio_modes); + + if (dcs->vbios_dco) + dal_vbios_dco_destroy(&dcs->vbios_dco); + + if (dcs->rdrm) + dal_remote_display_receiver_modes_destroy(&dcs->rdrm); + + if (dcs->default_wireless_audio_modes) + dal_dcs_cea_audio_mode_list_destroy( + &dcs->default_wireless_audio_modes); +} + +void dal_dcs_destroy(struct dcs **dcs) +{ + if (!dcs || !*dcs) { + BREAK_TO_DEBUGGER(); + return; + } + + destruct(*dcs); + dal_free(*dcs); + *dcs = NULL; +} + +static void apply_non_edid_based_monitor_patches(struct dcs *dcs) +{ + struct dp_receiver_id_info info = { 0 }; + + if (!dcs->ddc_service || !dcs->edid_mgr) + return; + /* apply dp receiver id based patches*/ + /* get dp receiver id info from DdcService*/ + if (!dal_ddc_service_get_dp_receiver_id_info(dcs->ddc_service, &info)) { + BREAK_TO_DEBUGGER(); + return; + } + /* call edid manager to update patches based on dp receiver ids.*/ + dal_edid_mgr_update_dp_receiver_id_based_monitor_patches( + dcs->edid_mgr, &info); + + /* apply other non-edid based patches (for future) */ +} + +enum edid_retrieve_status dal_dcs_retrieve_raw_edid(struct dcs *dcs) +{ + enum edid_retrieve_status ret = EDID_RETRIEVE_FAIL; + + if (!dcs->edid_mgr) + return EDID_RETRIEVE_FAIL; + + /* vbios has always higher priority */ + /* If VBios has Edid, we use it instead reading DDC line + * (typically for embedded panels only)*/ + if (dcs->vbios_dco && + dal_vbios_dco_get_edid_buff(dcs->vbios_dco) && + dal_vbios_dco_get_edid_buff_len(dcs->vbios_dco) && + !dal_adapter_service_is_feature_supported( + FEATURE_EDID_STRESS_READ)) + ret = EDID_RETRIEVE_SAME_EDID; + /* Read Edid from DDC line (ask DDC service to sync up + * with the display connected to the DDC line) */ + else if (dcs->ddc_service) { + union dcs_monitor_patch_flags patch_flags = + dal_dcs_get_monitor_patch_flags(dcs); + + if (patch_flags.flags.DELAY_BEFORE_READ_EDID) { + + const struct monitor_patch_info *patch_info = + dal_dcs_get_monitor_patch_info( + dcs, + MONITOR_PATCH_TYPE_DELAY_BEFORE_READ_EDID); + if (patch_info) + dal_sleep_in_milliseconds(patch_info->param); + + } + + dal_ddc_service_optimized_edid_query(dcs->ddc_service); + + ret = dal_edid_mgr_update_edid_raw_data(dcs->edid_mgr, + dal_ddc_service_get_edid_buf_len(dcs->ddc_service), + dal_ddc_service_get_edid_buf(dcs->ddc_service)); + } + /* DDC serivce (temporary) not available - reset EDID*/ + else + ret = dal_edid_mgr_update_edid_raw_data( + dcs->edid_mgr, 0, NULL); + + if (ret == EDID_RETRIEVE_FAIL_WITH_PREVIOUS_SUCCESS) + build_audio_modes(dcs); + + apply_non_edid_based_monitor_patches(dcs); + + return ret; +} + +uint32_t dal_dcs_get_edid_raw_data_size(struct dcs *dcs) +{ + if (!dcs->edid_mgr) + return 0; + + return dal_edid_mgr_get_edid_raw_data_size(dcs->edid_mgr); +} + +const uint8_t *dal_dcs_get_edid_raw_data( + struct dcs *dcs, + uint32_t *buff_size) +{ + if (!dcs->edid_mgr) + return NULL; + + return dal_edid_mgr_get_edid_raw_data(dcs->edid_mgr, buff_size); +} + +static void update_monitor_packed_pixel_format(struct dcs *dcs) +{ + struct display_color_depth_support color_depth = { 0 }; + const struct monitor_patch_info *patch_info; + struct edid_base *edid_base; + struct asic_feature_flags feature_flags; + + feature_flags = dal_adapter_service_get_feature_flags(dcs->as); + + /* Verify Edid present and feature supported*/ + dcs->packed_pixel_format = DCS_PACKED_PIXEL_FORMAT_NOT_PACKED; + + if (!dcs->edid_mgr || !feature_flags.bits.PACKED_PIXEL_FORMAT) + return; + + edid_base = dal_edid_mgr_get_edid(dcs->edid_mgr); + + /* Verify this is not native 10 bit*/ + if (edid_base && + dal_edid_get_display_color_depth(edid_base, &color_depth)) + if (color_depth.mask & COLOR_DEPTH_INDEX_101010) + return; + + /* Obtain packed pixel info from relevant patch*/ + patch_info = dal_edid_mgr_get_monitor_patch_info( + dcs->edid_mgr, MONITOR_PATCH_TYPE_PACKED_PIXEL_FORMAT); + + if (!patch_info) + patch_info = dal_edid_mgr_get_monitor_patch_info( + dcs->edid_mgr, MONITOR_PATCH_TYPE_MULTIPLE_PACKED_TYPE); + + if (patch_info) + dcs->packed_pixel_format = patch_info->param; +} + +static void build_drr_settings(struct dcs *dcs) +{ + uint32_t feature_value = 0; + struct edid_base *edid_base = NULL; + + dal_memset(&dcs->timing_limits, 0, sizeof(struct timing_limits)); + dal_memset(&dcs->drr_config, 0, sizeof(struct drr_config)); + + /* PART A */ + /* FID8860, check whether OEM specified percentage for maximum safe + * pixel clock get original pixel clock for LVDS panel from VBIOS*/ + if ((INTERFACE_TYPE_LVDS == dcs->display_type) || + (INTERFACE_TYPE_EDP == dcs->display_type)) { + uint32_t pixel_clk = 0; + uint64_t factor = 10000; + /* Calculate safe range factor for max pixel clock*/ + + /* maximum value for safe pixel clock is given in [0.01%] units + * get percentage factor, for example 325 (3.25%) the factor is + * 1.0325 (or 10325 in [0.01%] units)*/ + + if (dal_adapter_service_get_feature_value( + FEATURE_LVDS_SAFE_PIXEL_CLOCK_RANGE, + &feature_value, + sizeof(feature_value))) + factor += feature_value; + + if (NULL != dcs->vbios_dco) + pixel_clk = dal_vbios_dco_get_pixel_clk_for_drr_khz( + dcs->vbios_dco); + + if (dcs->edid_mgr) + edid_base = dal_edid_mgr_get_edid(dcs->edid_mgr); + + /* Check EDID if not found in VBIOS */ + if (0 == pixel_clk && edid_base) + pixel_clk = dal_edid_get_drr_pixel_clk_khz(edid_base); + + /* set timing limits */ + dcs->timing_limits.min_pixel_clock_in_khz = pixel_clk; + dcs->timing_limits.max_pixel_clock_in_khz = + div_u64(factor * pixel_clk, 10000); + } + + /* PART B - DRR Feature */ + /* VBIOS override and runtime parameter override is only for embedded */ + if ((INTERFACE_TYPE_LVDS == dcs->display_type) || + (INTERFACE_TYPE_EDP == dcs->display_type)) { + /* First try to find DRR capability from runtime parameter. + * DRR runtime parameter may disable feature. */ + if (!dal_adapter_service_get_feature_value( + FEATURE_DRR_SUPPORT, + &feature_value, + sizeof(feature_value))) + return; + + /* DRR is not supported if disabled by runtime parameter. */ + if (feature_value == AS_DRR_SUPPORT_DISABLED) + return; + else if (feature_value >= AS_DRR_SUPPORT_MIN_FORCED_FPS) { + dcs->drr_config.min_fps_in_microhz = feature_value; + if (dcs->drr_config.min_fps_in_microhz > 0) + dcs->drr_config.support_method.FORCED_BY_REGKEY_OR_ESCAPE + = true; + } + + + /* Check VBIOS if not forced by runtime parameter */ + if (0 == dcs->drr_config.min_fps_in_microhz && + NULL != dcs->vbios_dco) { + dcs->drr_config.min_fps_in_microhz = + dal_vbios_dco_get_min_fps_for_drr( + dcs->vbios_dco); + if (dcs->drr_config.min_fps_in_microhz > 0) { + dcs->drr_config.support_method.FORCED_BY_VBIOS + = true; + } + } + + } else { + /* For non-embedded displays, check if DRR support is disabled + * by runtime parameter */ + if (dal_adapter_service_is_feature_supported( + FEATURE_SUPPORT_EXTERNAL_PANEL_DRR)) { + /* DRR is not supported on external panels if disabled + * by runtime parameter. */ + return; + } + } + + /* Finally check EDID if not found in VBIOS or runtime parameters. + * EDID method of supporting DRR is possible on both external and + * internal panels. */ + if (0 == dcs->drr_config.min_fps_in_microhz && NULL != edid_base) { + dcs->drr_config.min_fps_in_microhz = + dal_edid_get_min_drr_fps(edid_base); + if (dcs->drr_config.min_fps_in_microhz > 0) { + dcs->drr_config.support_method.SUPPORTED_BY_EDID = + true; + } + } + + dcs->drr_config.min_fps_in_microhz *= 1000000;/*convert to microHz*/ + dcs->drr_config.force_lock_on_event = 0;/* default no flip lock*/ + dcs->drr_config.lock_to_master_vsync = 0;/* default no vsync lock*/ +} + +static void update_cached_data(struct dcs *dcs) +{ + /* build audio modes for this display*/ + build_audio_modes(dcs); + + /* Update cached display packed pixel format from Edid */ + update_monitor_packed_pixel_format(dcs); + + /* update ranged timing information */ + build_drr_settings(dcs); + +} + +enum edid_retrieve_status dal_dcs_update_edid_from_last_retrieved( + struct dcs *dcs) +{ + enum edid_retrieve_status ret; + + if (!dcs->edid_mgr) { + BREAK_TO_DEBUGGER(); + return EDID_RETRIEVE_FAIL; + } + + ret = dal_edid_mgr_update_edid_from_last_retrieved(dcs->edid_mgr); + + if (ret == EDID_RETRIEVE_SUCCESS) { + + if (dcs->display_type != INTERFACE_TYPE_DP) { + /* For any other interface then DP we fail detection + * if EDID has bad checksum */ + if (dal_edid_get_errors( + dal_edid_mgr_get_edid( + dcs->edid_mgr))->BAD_CHECKSUM) + return EDID_RETRIEVE_FAIL; + + /* new range limit dco or update the range limit */ + /*TODO: add range limit dco */ + } + + update_cached_data(dcs); + + } else if (ret == EDID_RETRIEVE_FAIL_WITH_PREVIOUS_SUCCESS) + build_audio_modes(dcs); + + + return ret; +} + +/*Update DDC Service. returns the old DdcService being replaced*/ +struct ddc_service *dal_dcs_update_ddc( + struct dcs *dcs, + struct ddc_service *ddc) +{ + struct ddc_service *old_ddc_service = dcs->ddc_service; + + dcs->ddc_service = ddc; + return old_ddc_service; +} + +union dcs_monitor_patch_flags dal_dcs_get_monitor_patch_flags(struct dcs *dcs) +{ + union dcs_monitor_patch_flags flags = { { 0 } }; + + if (dcs->edid_mgr) + return dal_edid_mgr_get_monitor_patch_flags(dcs->edid_mgr); + + return flags; +} + +void dal_dcs_set_transaction_type( + struct dcs *dcs, + enum ddc_transaction_type type) +{ + union dcs_monitor_patch_flags patch_flags = + dal_dcs_get_monitor_patch_flags(dcs); + + if (!dcs->ddc_service) + return; + + /* overwrite transaction type to increase retry attempt*/ + if (DDC_TRANSACTION_TYPE_I2C != type && + patch_flags.flags.INCREASE_DEFER_WRITE_RETRY_I2C_OVER_AUX) + type = DDC_TRANSACTION_TYPE_I2C_OVER_AUX_RETRY_DEFER; + + dal_ddc_service_set_transaction_type(dcs->ddc_service, type); +} + +static void update_edid_supported_max_bw( + struct dcs *dcs, + struct mode_timing *mt) +{ + uint32_t bpp = 24; + + switch (mt->mode_info.timing_source) { + case TIMING_SOURCE_EDID_DETAILED: + case TIMING_SOURCE_EDID_ESTABLISHED: + case TIMING_SOURCE_EDID_STANDARD: + case TIMING_SOURCE_EDID_CEA_SVD: + case TIMING_SOURCE_EDID_CEA_SVD_3D: + case TIMING_SOURCE_EDID_CVT_3BYTE: + case TIMING_SOURCE_EDID_4BYTE: + break; + default: + return; + } + + switch (mt->crtc_timing.display_color_depth) { + case DISPLAY_COLOR_DEPTH_666: + bpp = 18; + break; + case DISPLAY_COLOR_DEPTH_888: + bpp = 24; + break; + case DISPLAY_COLOR_DEPTH_101010: + bpp = 30; + break; + case DISPLAY_COLOR_DEPTH_121212: + bpp = 36; + break; + case DISPLAY_COLOR_DEPTH_141414: + bpp = 42; + break; + case DISPLAY_COLOR_DEPTH_161616: + bpp = 48; + break; + default: + bpp = 24; + break; + } + + if ((dcs->edid_max_bw.pix_clock_khz * dcs->edid_max_bw.bits_per_pixel) < + (mt->crtc_timing.pix_clk_khz * bpp)) { + dcs->edid_max_bw.pix_clock_khz = mt->crtc_timing.pix_clk_khz; + dcs->edid_max_bw.bits_per_pixel = bpp; + } +} + +static bool is_mode_timing_tiled( + struct dcs_display_tile *display_tile, + struct mode_timing *mode_timing) +{ + uint32_t tw_mh = + display_tile->width * mode_timing->mode_info.pixel_height; + uint32_t th_mw = + display_tile->height * mode_timing->mode_info.pixel_width; + + return (tw_mh * 90 < th_mw * 100) && (tw_mh * 110 < th_mw * 100); +} + +static void get_tile_info( + struct dcs *dcs, + struct edid_base *edid_base, + struct dcs_display_tile *display_tile) +{ + const struct monitor_patch_info *patch_info; + struct vendor_product_id_info tiled_vendor_info = { 0 }; + + if (dal_edid_get_display_tile_info(edid_base, display_tile)) { + dcs->flags.TILED_DISPLAY_CAN_SCALE = + display_tile->flags.CAN_SCALE; + return; + } + + patch_info = dal_dcs_get_monitor_patch_info( + dcs, MONITOR_PATCH_TYPE_TILED_DISPLAY); + + dcs->flags.TILED_DISPLAY_CAN_SCALE = false; + + if (!patch_info || patch_info->param == EDID_TILED_DISPLAY_NONE) + return; + + if (!dal_dcs_get_vendor_product_id_info(dcs, &tiled_vendor_info)) + return; + + display_tile->flags.CAN_SCALE = 0; + display_tile->height = TILED_DISPLAY_VERTICAL; + display_tile->width = TILED_DISPLAY_HORIZONTAL; + /*row and column not important in here.*/ + /* No bezel Info */ + /* No single Enclosure */ + display_tile->topology_id.manufacturer_id = + tiled_vendor_info.manufacturer_id; + display_tile->topology_id.product_id = + tiled_vendor_info.product_id; + display_tile->topology_id.serial_id = dcs->graphics_object_id.enum_id; +} + +static enum pixel_encoding dcs_pixel_encoding_to_ts_pixel_encoding( + enum pixel_encoding_mask encoding_value) +{ + switch (encoding_value) { + case PIXEL_ENCODING_MASK_YCBCR444: + return PIXEL_ENCODING_YCBCR444; + case PIXEL_ENCODING_MASK_YCBCR422: + return PIXEL_ENCODING_YCBCR422; + case PIXEL_ENCODING_MASK_RGB: + return PIXEL_ENCODING_RGB; + default: + break; + } + + return PIXEL_ENCODING_UNDEFINED; +} + +static enum display_color_depth dcs_color_depth_to_ts_color_depth( + uint32_t color_value) +{ + switch (color_value) { + case COLOR_DEPTH_INDEX_666: + return DISPLAY_COLOR_DEPTH_666; + case COLOR_DEPTH_INDEX_888: + return DISPLAY_COLOR_DEPTH_888; + case COLOR_DEPTH_INDEX_101010: + return DISPLAY_COLOR_DEPTH_101010; + case COLOR_DEPTH_INDEX_121212: + return DISPLAY_COLOR_DEPTH_121212; + case COLOR_DEPTH_INDEX_141414: + return DISPLAY_COLOR_DEPTH_141414; + case COLOR_DEPTH_INDEX_161616: + return DISPLAY_COLOR_DEPTH_161616; + default: + break; + } + + return DISPLAY_COLOR_DEPTH_UNDEFINED; +} +static bool should_insert_mode( + struct dcs *dcs, + struct mode_timing *mode_timing, + bool deep_color) +{ + bool ce_mode = dal_timing_service_is_ce_timing_standard( + mode_timing->crtc_timing.timing_standard); + bool insert_mode = false; + + switch (mode_timing->crtc_timing.pixel_encoding) { + case PIXEL_ENCODING_YCBCR444: + /* 3) for CE timing, we allow all color depth for YCbCr444 + * if deep color for YCbCr supported. + * 888 color depths always supported + * 4) VCE/Wireless only supports YCbCr444 so always allow it */ + if (ce_mode || dcs->display_type == INTERFACE_TYPE_WIRELESS) + if (deep_color || + mode_timing->crtc_timing.display_color_depth <= + DISPLAY_COLOR_DEPTH_888) + insert_mode = true; + + if (dcs->display_type == INTERFACE_TYPE_DP && + dcs->flags.DP_Y_ONLY) + mode_timing->crtc_timing.flags.YONLY = 1; + + break; + case PIXEL_ENCODING_YCBCR422: + /* 2) for CE timing, we allow 888 only for YCbCr422. + * All other depths can be supported by YCbCr444 */ + if (ce_mode && mode_timing->crtc_timing.display_color_depth <= + DISPLAY_COLOR_DEPTH_121212) + insert_mode = true; + + break; + case PIXEL_ENCODING_RGB: + /* 1) for RGB, we allow all color depth.*/ + insert_mode = true; + break; + + + default: + break; + + } + return insert_mode; +} + +static void add_edid_mode( + struct dcs *dcs, + struct dcs_mode_timing_list *list, + struct mode_timing *mode_timing, + struct display_color_and_pixel_support *support) +{ + + uint32_t color_value; + uint32_t pixel_value; + struct bit_set_iterator_32 color_depth_it; + struct bit_set_iterator_32 pixel_encoding_it; + + bit_set_iterator_construct( + &color_depth_it, + support->color_depth_support.mask); + + while ((color_value = get_next_significant_bit(&color_depth_it)) != 0) { + + mode_timing->crtc_timing.display_color_depth = + dcs_color_depth_to_ts_color_depth(color_value); + + /* Skip deep color on non-native timing (typically DVI only) */ + if (mode_timing->mode_info.timing_source != + TIMING_SOURCE_EDID_DETAILED && + mode_timing->crtc_timing.display_color_depth > + DISPLAY_COLOR_DEPTH_888 && + support->color_depth_support.deep_color_native_res_only) + continue; + + bit_set_iterator_construct( + &pixel_encoding_it, + support->pixel_encoding_support.mask); + + while ((pixel_value = + get_next_significant_bit(&pixel_encoding_it)) != 0) { + + mode_timing->crtc_timing.pixel_encoding = + dcs_pixel_encoding_to_ts_pixel_encoding( + pixel_value); + + if (should_insert_mode( + dcs, + mode_timing, + support->deep_color_y444_support)) + dal_dcs_mode_timing_list_append( + list, mode_timing); + } + } +} + +static void add_edid_modes( + struct dcs *dcs, + struct dcs_mode_timing_list *list, + bool *preffered_found) +{ + struct edid_base *edid_base; + struct display_color_and_pixel_support clr_pix_support = { { 0 } }; + struct cea_vendor_specific_data_block vendor_block = { 0 }; + struct dcs_display_tile display_tile = { 0 }; + bool ret_color_depth, ret_pixel_encoding; + struct dcs_mode_timing_list *edid_list; + + if (!dcs->edid_mgr) + return; + + edid_base = dal_edid_mgr_get_edid(dcs->edid_mgr); + + if (!edid_base) + return; + + edid_list = dal_dcs_mode_timing_list_create(128); + + dal_edid_get_supported_mode_timing( + edid_base, edid_list, preffered_found); + + ret_color_depth = dal_edid_get_display_color_depth( + edid_base, &clr_pix_support.color_depth_support); + + ret_pixel_encoding = dal_edid_get_display_pixel_encoding( + edid_base, &clr_pix_support.pixel_encoding_support); + + if (dcs->display_type == INTERFACE_TYPE_WIRELESS) { + clr_pix_support.color_depth_support.mask = + COLOR_DEPTH_INDEX_888; + + clr_pix_support.pixel_encoding_support.mask = + PIXEL_ENCODING_MASK_YCBCR444; + } + + if (dal_edid_get_cea_vendor_specific_data_block( + edid_base, &vendor_block)) + clr_pix_support.deep_color_y444_support = + vendor_block.byte6.DC_Y444; + + get_tile_info(dcs, edid_base, &display_tile); + + if (ret_color_depth && ret_pixel_encoding) { + + uint32_t i; + uint32_t list_count = + dal_dcs_mode_timing_list_get_count(edid_list); + + for (i = 0; i < list_count; ++i) { + + struct mode_timing *mode_timing = + dal_dcs_mode_timing_list_at_index(edid_list, i); + mode_timing->mode_info.flags.TILED_MODE = + is_mode_timing_tiled( + &display_tile, mode_timing); + + add_edid_mode(dcs, list, mode_timing, &clr_pix_support); + } + } + + dal_dcs_mode_timing_list_destroy(&edid_list); +} + +static void add_overriden_modes( + struct dcs *dcs, + struct dcs_mode_timing_list *list, + bool *preffered_found) +{ + /* TODO: add implementation */ +} + +enum { DEFAULT_3D_RIGHT_EYE_POLARITY = false }; + +/** + ***************************************************************************** + * setup_projector_stereo_3d_timings + * + * For Projectors we have following policy: If it supports (reported in EDID) + * 120Hz progressive mode, this mode considered as Frame Alternate 3D. + * To make sure 3D mode gets higher priority in ModeMgr we force each such 3D + * mode to be considered as coming from Detailed Timing section + * NOTE: This function can be called only if Frame Alternate 3D format supported + * in current config + * + * list: DCS list of modes/timings + ***************************************************************************** + */ +static void setup_projector_stereo_3d_timings(struct dcs_mode_timing_list *list) +{ + uint32_t i; + + for (i = 0; i < dal_dcs_mode_timing_list_get_count(list); i++) { + struct mode_timing *mt = + dal_dcs_mode_timing_list_at_index(list, i); + + /* 120Hz projector timing considered to be stereo capable + * (FrameAlternate, no stereosync) */ + bool progresive_120hz = mt->mode_info.field_rate == 120 && + !mt->mode_info.flags.INTERLACE; + bool edid_source = false; + + switch (mt->mode_info.timing_source) { + case TIMING_SOURCE_EDID_CEA_SVD_3D: + case TIMING_SOURCE_EDID_DETAILED: + case TIMING_SOURCE_EDID_ESTABLISHED: + case TIMING_SOURCE_EDID_STANDARD: + case TIMING_SOURCE_EDID_CEA_SVD: + case TIMING_SOURCE_EDID_CVT_3BYTE: + case TIMING_SOURCE_EDID_4BYTE: + edid_source = true; + break; + + default: + break; + } + + if (mt->crtc_timing.timing_3d_format == TIMING_3D_FORMAT_NONE && + progresive_120hz && edid_source) { + mt->mode_info.timing_source = + TIMING_SOURCE_EDID_DETAILED; + mt->crtc_timing.timing_3d_format = + TIMING_3D_FORMAT_FRAME_ALTERNATE; + mt->crtc_timing.flags.EXCLUSIVE_3D = false; + mt->crtc_timing.flags.RIGHT_EYE_3D_POLARITY = + DEFAULT_3D_RIGHT_EYE_POLARITY; + /* We will not create corresponding 2D timing */ + mt->crtc_timing.flags.USE_IN_3D_VIEW_ONLY = false; + } + } +} + +/* + * get_supported_3d_format + * + * Given requested 3D format, returns supported 3D format (in most cases same + * as given), considering validation of supported 3D features + * + * format - requested 3D format + * connectType - display connection type + * interlace - true if requested supported for interlace timing + * + */ +static enum timing_3d_format get_supported_3d_format( + struct dcs *dcs, + enum timing_3d_format format, + enum dcs_edid_connector_type connect_type, + bool interlace) +{ + enum timing_3d_format supported_format = TIMING_3D_FORMAT_NONE; + + /* Special case is when there is an active DP->HDMI converter. + * If converter has the "Frame Sequential-to-Frame Pack" capability, + * then positively validate *only* the TIMING_3D_FORMAT_HW_FRAME_PACKING + * format (because it will be converted to + * TIMING_3D_FORMAT_HW_FRAME_PACKING by the active converter). */ + if (connect_type == EDID_CONNECTOR_HDMIA && + DISPLAY_DONGLE_DP_HDMI_CONVERTER == + dcs->sink_caps.dongle_type) { + supported_format = TIMING_3D_FORMAT_NONE; + + if (TIMING_3D_FORMAT_HW_FRAME_PACKING == format + && dcs->sink_caps.is_dp_hdmi_s3d_converter) { + if (dcs->stereo_3d_support. + bits.DISPLAY_PORT_FRAME_ALT) + supported_format = + TIMING_3D_FORMAT_DP_HDMI_INBAND_FA; + } + } + + /* TODO: finish implementation */ + + return supported_format; +} + +static bool are_3d_formats_compatible( + enum timing_3d_format f1, + enum timing_3d_format f2) +{ + bool compatible = true; + + /* First invert the order since the comparison operation itself is not + * symmetric */ + if (f2 == TIMING_3D_FORMAT_SW_FRAME_PACKING) { + enum timing_3d_format tmp = f1; + + f1 = f2; + f2 = tmp; + } + + /* Now do comparision */ + if (f1 == TIMING_3D_FORMAT_SW_FRAME_PACKING) + switch (f2) { + case TIMING_3D_FORMAT_FRAME_ALTERNATE: + case TIMING_3D_FORMAT_INBAND_FA: + case TIMING_3D_FORMAT_DP_HDMI_INBAND_FA: + case TIMING_3D_FORMAT_SIDEBAND_FA: + case TIMING_3D_FORMAT_HW_FRAME_PACKING: + case TIMING_3D_FORMAT_ROW_INTERLEAVE: + case TIMING_3D_FORMAT_COLUMN_INTERLEAVE: + case TIMING_3D_FORMAT_PIXEL_INTERLEAVE: + compatible = false; + break; + + default: + break; + } + + return compatible; +} + +static void update_stereo_3d_features( + struct dcs *dcs, + struct dcs_mode_timing_list *list) +{ + bool override_per_timing_format = false; + bool all_right_eye_polarity = DEFAULT_3D_RIGHT_EYE_POLARITY; + enum timing_3d_format all_timings_format = TIMING_3D_FORMAT_NONE; + enum dcs_edid_connector_type conn_type = + dal_dcs_get_connector_type(dcs); + uint32_t i; + + uint32_t list_count = dal_dcs_mode_timing_list_get_count(list); + + dal_memset( + &dcs->stereo_3d_features, 0, sizeof(dcs->stereo_3d_features)); + + /* Setup 3D timings for projector (Projectors default format is Frame + * Alternate) */ + if (dcs->stereo_3d_support.bits.FRAME_ALTERNATE && dcs->edid_mgr && + dal_edid_mgr_get_edid(dcs->edid_mgr)) { + struct edid_screen_info screen_info = { + EDID_SCREEN_AR_UNKNOWN }; + dal_edid_get_screen_info( + dal_edid_mgr_get_edid(dcs->edid_mgr), + &screen_info); + + if (screen_info.aspect_ratio == EDID_SCREEN_AR_PROJECTOR) + setup_projector_stereo_3d_timings(list); + } + + /* Sideband Frame Alternate we treat as global 3D properties (applies to + * all timings) */ + if (dcs->stereo_3d_support.bits.SIDEBAND_FRAME_ALT) { + struct gpio *stereo_gpio = + dal_adapter_service_obtain_stereo_gpio(dcs->as); + + all_timings_format = TIMING_3D_FORMAT_SIDEBAND_FA; + override_per_timing_format = true; + dcs->stereo_3d_features[all_timings_format].flags.ALL_TIMINGS = + 1; + + if (stereo_gpio) { + all_right_eye_polarity = + dal_gpio_get_output_state(stereo_gpio) == + GPIO_PIN_OUTPUT_STATE_ACTIVE_HIGH; + dal_adapter_service_release_gpio(dcs->as, stereo_gpio); + } + } + + /* Initialize global 3D support (only if not defined yet as Sideband FA) + */ + if (all_timings_format == TIMING_3D_FORMAT_NONE && dcs->edid_mgr && + dal_edid_mgr_get_edid(dcs->edid_mgr)) { + /* Obtain Edid for global stereo support + * If this format applies to all timings then we can cache it + * now */ + struct edid_stereo_3d_capability stereo_capability = { + TIMING_3D_FORMAT_NONE }; + if (dal_edid_get_stereo_3d_support( + dal_edid_mgr_get_edid(dcs->edid_mgr), + &stereo_capability)) + all_timings_format = + get_supported_3d_format( + dcs, + stereo_capability.timing_3d_format, + conn_type, + false); + + if (all_timings_format != TIMING_3D_FORMAT_NONE) { + override_per_timing_format = + stereo_capability.override_per_timing_format; + + switch (stereo_capability.timing_3d_format) { + case TIMING_3D_FORMAT_FRAME_ALTERNATE: + case TIMING_3D_FORMAT_SIDEBAND_FA: + case TIMING_3D_FORMAT_INBAND_FA: + case TIMING_3D_FORMAT_DP_HDMI_INBAND_FA: + all_right_eye_polarity = + stereo_capability.frame_alternate_data. + right_eye_polarity; + break; + + case TIMING_3D_FORMAT_ROW_INTERLEAVE: + case TIMING_3D_FORMAT_COLUMN_INTERLEAVE: + case TIMING_3D_FORMAT_PIXEL_INTERLEAVE: + all_right_eye_polarity = + stereo_capability.interleaved_data. + right_eye_polarity; + break; + default: + break; + } + + dcs->stereo_3d_features[all_timings_format]. + flags.ALL_TIMINGS = 1; + } + } + + /* Go over the whole list and patch every timing with 3D info */ + for (i = 0; i < list_count; ++i) { + struct mode_timing *mt = + dal_dcs_mode_timing_list_at_index(list, i); + enum timing_3d_format timing_3d_format = + mt->crtc_timing.timing_3d_format; + + /* Override 3D format with global format */ + if (all_timings_format != TIMING_3D_FORMAT_NONE && + (override_per_timing_format || timing_3d_format == + TIMING_3D_FORMAT_NONE)) { + /* If this is extra 3D timing (i.e. there is also + * corresponding 2D timing which will be overridden), + * remove it from the list, so we do not get 2 identical + * timings with overridden 3D format */ + if (timing_3d_format != TIMING_3D_FORMAT_NONE && + mt->crtc_timing.flags.USE_IN_3D_VIEW_ONLY) { + dal_dcs_mode_timing_list_remove_at_index( + list, i); + /* TODO: this code should be refactored */ + --i; + --list_count; + continue; + } + + timing_3d_format = all_timings_format; + mt->crtc_timing.flags.RIGHT_EYE_3D_POLARITY = + all_right_eye_polarity; + mt->crtc_timing.flags.EXCLUSIVE_3D = false; + mt->crtc_timing.flags.SUB_SAMPLE_3D = true; + /* This timing used for both 3D and 2D */ + mt->crtc_timing.flags.USE_IN_3D_VIEW_ONLY = false; + } + + /* Force default right eye polarity according to DP spec */ + else if (timing_3d_format == TIMING_3D_FORMAT_INBAND_FA && + conn_type == EDID_CONNECTOR_DISPLAYPORT) + mt->crtc_timing.flags.RIGHT_EYE_3D_POLARITY = + DEFAULT_3D_RIGHT_EYE_POLARITY; + + /* Update supported 3D format */ + timing_3d_format = get_supported_3d_format( + dcs, + timing_3d_format, + conn_type, mt->crtc_timing.flags.INTERLACE); + if (timing_3d_format != TIMING_3D_FORMAT_NONE) { + struct dcs_stereo_3d_features *s3d_features = + &dcs->stereo_3d_features[timing_3d_format]; + mt->crtc_timing.timing_3d_format = timing_3d_format; + + if (!s3d_features->flags.SUPPORTED) { + s3d_features->flags.SUPPORTED = true; + + if (timing_3d_format != + TIMING_3D_FORMAT_SW_FRAME_PACKING) { + s3d_features->flags.CLONE_MODE = true; + s3d_features->flags.SCALING = true; + } else + s3d_features->flags. + SINGLE_FRAME_SW_PACKED = true; + } + } else if (mt->crtc_timing.timing_3d_format != + TIMING_3D_FORMAT_NONE) { + /* Remove timing which intended to be used for 3D only, + * but now it cannot be used in 3D (means cannot be used + * at all) */ + if (mt->crtc_timing.flags.USE_IN_3D_VIEW_ONLY || + mt->crtc_timing.flags.EXCLUSIVE_3D) { + dal_dcs_mode_timing_list_remove_at_index( + list, i); + /* TODO: this code should be refactored */ + --i; + --list_count; + continue; + } + + mt->crtc_timing.timing_3d_format = + TIMING_3D_FORMAT_NONE; + } + } + + /* Disallow all the rest formats when SW Frame Pack enabled */ + if (dcs->stereo_3d_features[TIMING_3D_FORMAT_SW_FRAME_PACKING]. + flags.SUPPORTED) { + for (i = 0; i < list_count; ++i) { + struct mode_timing *mt = + dal_dcs_mode_timing_list_at_index(list, i); + enum timing_3d_format timing_3d_format = + mt->crtc_timing.timing_3d_format; + + if (!are_3d_formats_compatible( + TIMING_3D_FORMAT_SW_FRAME_PACKING, + timing_3d_format)) { + dcs->stereo_3d_features[timing_3d_format]. + flags.SUPPORTED = false; + mt->crtc_timing.timing_3d_format = + TIMING_3D_FORMAT_NONE; + mt->crtc_timing.flags.EXCLUSIVE_3D = false; + mt->crtc_timing. + flags.USE_IN_3D_VIEW_ONLY = false; + } + } + } +} + +static void add_default_modes( + struct dcs *dcs, + struct dcs_mode_timing_list *list) +{ + bool add_default_timings = false; + union dcs_monitor_patch_flags flags; + enum dcs_edid_connector_type conn_type = EDID_CONNECTOR_UNKNOWN; + + flags = dal_edid_mgr_get_monitor_patch_flags(dcs->edid_mgr); + + if (flags.flags.NO_DEFAULT_TIMINGS) + return; + + conn_type = dal_dcs_get_connector_type(dcs); + + if (conn_type == EDID_CONNECTOR_DISPLAYPORT || + conn_type == EDID_CONNECTOR_HDMIA) + add_default_timings = true; + else { + bool explicit_mode_exists = false; + struct mode_timing *mode_timing = NULL; + uint32_t list_count = dal_dcs_mode_timing_list_get_count(list); + uint32_t i; + + for (i = list_count; i > 0; --i) { + mode_timing = + dal_dcs_mode_timing_list_at_index(list, i-1); + + switch (mode_timing->mode_info.timing_source) { + case TIMING_SOURCE_EDID_CEA_SVD_3D: + case TIMING_SOURCE_EDID_DETAILED: + case TIMING_SOURCE_EDID_ESTABLISHED: + case TIMING_SOURCE_EDID_STANDARD: + case TIMING_SOURCE_EDID_CEA_SVD: + case TIMING_SOURCE_EDID_CVT_3BYTE: + case TIMING_SOURCE_EDID_4BYTE: + case TIMING_SOURCE_VBIOS: + case TIMING_SOURCE_CV: + case TIMING_SOURCE_TV: + case TIMING_SOURCE_HDMI_VIC: + explicit_mode_exists = true; + break; + + default: + break; + } + } + + if (!explicit_mode_exists) + add_default_timings = true; + } + + if (add_default_timings && dcs->dco_funcs.add_mode_timing != NULL) + dcs->dco_funcs.add_mode_timing(list, dcs->ts); +} + +static void get_intersect_for_timing_lists( + struct dcs *dcs, + struct dcs_mode_timing_list *list_1, + struct dcs_mode_timing_list *list_2, + struct dcs_mode_timing_list *output_list) +{ + struct mode_timing *mt_1 = NULL; + struct mode_timing *mt_2 = NULL; + uint32_t i = 0; + uint32_t j = 0; + uint32_t list_count_1 = 0; + uint32_t list_count_2 = 0; + + if (list_1 == NULL || list_2 == NULL || output_list == NULL) { + dal_logger_write(dcs->dal->logger, LOG_MAJOR_DCS, + LOG_MINOR_COMPONENT_DISPLAY_CAPABILITY_SERVICE, + "%s: Invalid input", __func__); + return; + } + + dal_dcs_mode_timing_list_clear(output_list); + + list_count_1 = dal_dcs_mode_timing_list_get_count(list_1); + for (i = 0; i < list_count_1; ++i) { + mt_1 = dal_dcs_mode_timing_list_at_index(list_1, i); + list_count_2 = dal_dcs_mode_timing_list_get_count(list_2); + + for (j = 0; j < list_count_2; ++j) { + mt_2 = dal_dcs_mode_timing_list_at_index(list_2, j); + + if (mt_1->mode_info.pixel_height == + mt_2->mode_info.pixel_height && + mt_1->mode_info.pixel_width == + mt_2->mode_info.pixel_width && + mt_1->mode_info.field_rate == + mt_2->mode_info.field_rate && + mt_1->mode_info.flags.INTERLACE == + mt_2->mode_info.flags.INTERLACE && + mt_1->mode_info.flags.VIDEO_OPTIMIZED_RATE == + mt_2->mode_info.flags.VIDEO_OPTIMIZED_RATE) { + dal_dcs_mode_timing_list_append( + output_list, mt_1); + break; + } + } + } +} + +static void filter_default_modes_for_wireless(struct dcs *dcs, + struct dcs_mode_timing_list *list) +{ + /* 128 is just a magic number. Should be updated */ + struct dcs_mode_timing_list *edid_mode_list = + dal_dcs_mode_timing_list_create(128); + struct dcs_mode_timing_list *receiver_mode_list = + dal_dcs_mode_timing_list_create(128); + bool is_limited_to_720p = false; + bool include_unverified_timings = false; + uint32_t i = 0; + uint32_t list_count = 0; + struct mode_timing new_timing; + + dal_memset(&new_timing, 0, sizeof(new_timing)); + + ASSERT_CRITICAL(edid_mode_list != NULL); + ASSERT_CRITICAL(receiver_mode_list != NULL); + + if (list == NULL || dcs->rdrm == NULL) + dal_logger_write(dcs->dal->logger, LOG_MAJOR_DCS, + LOG_MINOR_COMPONENT_DISPLAY_CAPABILITY_SERVICE, + "%s: Invalid input or receiver modes"); + + /* Get the feature values for wireless modes */ + dal_adapter_service_get_feature_value( + FEATURE_WIRELESS_LIMIT_720P, + &is_limited_to_720p, + sizeof(is_limited_to_720p)); + + dal_adapter_service_get_feature_value( + FEATURE_WIRELESS_INCLUDE_UNVERIFIED_TIMINGS, + &include_unverified_timings, + sizeof(include_unverified_timings)); + + /* Filter out unwanted timing. Order matters. + * - Modes greater than 1920*1080 or interlaced + * - For 720p limited, remove modes higher than 720 pixel in height + * - Add additional 30Hz/25Hz option + */ + list_count = dal_dcs_mode_timing_list_get_count(list); + for (i = 0; i < list_count; ++i) { + const struct mode_timing *timing = + dal_dcs_mode_timing_list_at_index(list, i); + + if (timing->mode_info.pixel_width > 1920 || + timing->mode_info.pixel_height > 1080 || + timing->mode_info.flags.INTERLACE) + continue; + + if (is_limited_to_720p && timing->mode_info.pixel_height > 720) + continue; + + if (timing->mode_info.field_rate == 60 || + timing->mode_info.field_rate == 50) { + + /* Add 30/25 Hz */ + new_timing = *timing; + new_timing.mode_info.field_rate /= 2; + new_timing.crtc_timing.pix_clk_khz /= 2; + new_timing.crtc_timing.vic = 0; + new_timing.crtc_timing.hdmi_vic = 0; + + dal_dcs_mode_timing_list_append( + edid_mode_list, &new_timing); + + /* Special case: Modes bigger than 720p at 60Hz is not + * added as they are unverified. ex. 1080p@60 */ + if (!include_unverified_timings && + (timing->mode_info.pixel_height > 720 || + timing->mode_info.pixel_width > 1280)) + continue; + } + + /* Add the timing if not filtered out by above conditions */ + dal_dcs_mode_timing_list_append(edid_mode_list, timing); + } + + /* Find intersection between EDID mode list and receiver mode list */ + dal_remote_display_receiver_get_supported_mode_timing( + dcs->rdrm, receiver_mode_list); + get_intersect_for_timing_lists( + dcs, edid_mode_list, receiver_mode_list, list); + + /* If the result of intersect is empty, include 640x480 WFD mandatory */ + if (dal_dcs_mode_timing_list_get_count(list) == 0) + dcs->dco_funcs.add_mode_timing(list, dcs->ts); + + if (edid_mode_list != NULL) + dal_dcs_mode_timing_list_destroy(&edid_mode_list); + + if (receiver_mode_list != NULL) + dal_dcs_mode_timing_list_destroy(&receiver_mode_list); +} + +static void insert_edid_dco_mode_timing( + struct dcs *dcs, + struct dcs_mode_timing_list *mtl, + struct mode_timing *mt, + struct display_color_and_pixel_support *color_and_pixel_support) +{ + bool is_ce_mode = + dal_timing_service_is_ce_timing_standard( + mt->crtc_timing.timing_standard); + enum color_depth_index color_depth; + + for (color_depth = COLOR_DEPTH_INDEX_666; + color_depth < COLOR_DEPTH_INDEX_LAST; + color_depth <<= 1) { + enum pixel_encoding_mask pixel_encoding; + + if ((color_depth & + color_and_pixel_support->color_depth_support.mask) == 0) + continue; + + mt->crtc_timing.display_color_depth = + dcs_color_depth_to_ts_color_depth(color_depth); + + /* Skip deep color on non-native timing (typically DVI only) */ + if (mt->mode_info.timing_source != + TIMING_SOURCE_EDID_DETAILED && + mt->crtc_timing.display_color_depth > + DISPLAY_COLOR_DEPTH_888 && + color_and_pixel_support->color_depth_support. + deep_color_native_res_only) + continue; + + for (pixel_encoding = PIXEL_ENCODING_MASK_YCBCR444; + pixel_encoding <= PIXEL_ENCODING_MASK_RGB; + pixel_encoding <<= 1) { + bool insert_mode = false; + + if (!(pixel_encoding & + color_and_pixel_support-> + pixel_encoding_support.mask)) + continue; + + mt->crtc_timing.pixel_encoding = + dcs_pixel_encoding_to_ts_pixel_encoding( + pixel_encoding); + + switch (pixel_encoding) { + case PIXEL_ENCODING_MASK_RGB: + /* 1) for RGB, we allow all color depth. */ + insert_mode = true; + break; + case PIXEL_ENCODING_MASK_YCBCR422: + /* 2) for CE timing, we allow 888 only for + * YCbCr422. All other depths can be supported + * by YCbCr444 + */ + if (is_ce_mode && + (color_depth <= + COLOR_DEPTH_INDEX_121212)) + insert_mode = true; + break; + case PIXEL_ENCODING_MASK_YCBCR444: + /* 3) for CE timing, we allow all color depth + * for YCbCr444 if deep color for YCbCr + * supported. 888 color depths always supported + * 4) VCE/Wireless only supports YCbCr444 so + * always allow it + */ + if ((is_ce_mode || + (dcs->display_type == + INTERFACE_TYPE_WIRELESS)) && + (color_and_pixel_support-> + deep_color_y444_support || + (color_depth <= + COLOR_DEPTH_INDEX_888))) + insert_mode = true; + /* Note: currently we don't have any feature to + * support DP Yonly, this is for HW testing only + * following logic should be replaced once + * display spec about YOnly available, at this + * implementation time, no Edid spec or DP spec + * report the display capability. + */ + if ((dcs->display_type == INTERFACE_TYPE_DP) && + dcs->dp_y_only) + mt->crtc_timing.flags.YONLY = 1; + break; + default: + break; + } + + if (insert_mode) + dal_dcs_mode_timing_list_append(mtl, mt); + } + } +} + +/* + * Search through the supported mode timing list and update the pixel encoding + * and display color depth for all modes that are undefined. The value to set is + * determined by the interface type. + */ +static void update_undefined_timing_parameters_with_defaults( + struct dcs *dcs, + struct dcs_mode_timing_list *mtl) +{ + /* determine the default PixelEncoding and DisplayColorDepth for + * undefined mode timings */ + struct mode_timing *mt; + struct display_pixel_encoding_support default_encoding_support; + struct display_color_depth_support default_color_depth_support; + struct display_color_and_pixel_support color_and_pixel_support; + struct cea_vendor_specific_data_block vendor_block = {0}; + bool insert_mode = false; + uint32_t i; + + if (!mtl) + return; + + get_default_color_depth(dcs, &default_color_depth_support); + get_default_pixel_encoding(dcs, &default_encoding_support); + + dal_memset( + &color_and_pixel_support, + 0, + sizeof(color_and_pixel_support)); + + if (dal_dcs_get_cea_vendor_specific_data_block(dcs, &vendor_block)) + color_and_pixel_support.deep_color_y444_support = + vendor_block.byte6.DC_Y444; + + /* update the pixel encoding and display color depth for all undefined + * mode timings + */ + for (i = 0; i < dal_dcs_mode_timing_list_get_count(mtl); + /* not always incremented */) { + mt = dal_dcs_mode_timing_list_at_index(mtl, i); + insert_mode = false; + + color_and_pixel_support.color_depth_support.mask = + mt->crtc_timing.display_color_depth; + color_and_pixel_support.pixel_encoding_support.mask = + mt->crtc_timing.pixel_encoding; + + if (mt->crtc_timing.display_color_depth == + DISPLAY_COLOR_DEPTH_UNDEFINED) { + color_and_pixel_support.color_depth_support = + default_color_depth_support; + insert_mode = true; + } + if (mt->crtc_timing.pixel_encoding == + PIXEL_ENCODING_UNDEFINED) { + color_and_pixel_support.pixel_encoding_support = + default_encoding_support; + insert_mode = true; + } + + if (insert_mode) { + dal_dcs_mode_timing_list_remove_at_index(mtl, i); + insert_edid_dco_mode_timing( + dcs, + mtl, + mt, + &color_and_pixel_support); + } else + i++; + } +} + +static bool build_mode_timing_list( + struct dcs *dcs, + struct dcs_mode_timing_list *list) +{ + bool preferred_found = false; + uint32_t list_count; + + if (!list) + return false; + + add_edid_modes(dcs, list, &preferred_found); + + add_overriden_modes(dcs, list, &preferred_found); + + if (dcs->vbios_dco) + dal_vbios_dco_add_mode_timing( + dcs->vbios_dco, list, &preferred_found); + + /*TODO: add other dco*/ + + /* Add default modes if there is no mode coming from EDID, TV or BIOS. + * For non-wireless interface and wireless without valid timing. + */ + if (dcs->display_type != INTERFACE_TYPE_WIRELESS) { + add_default_modes(dcs, list); + /* TODO: Multiple Refresh Rate Timing for DRR */ + } else { + filter_default_modes_for_wireless(dcs, list); + } + + list_count = dal_dcs_mode_timing_list_get_count(list); + /* If preferred mode yet not found, + * try to choose maximum progressive mode as preferred */ + if (!preferred_found) { + int32_t i; + + for (i = list_count; i > 0; --i) { + struct mode_timing *mode_timing = + dal_dcs_mode_timing_list_at_index(list, i-1); + if (!mode_timing->mode_info.flags.INTERLACE) { + mode_timing->mode_info.flags.PREFERRED = 1; + preferred_found = true; + break; + } + } + } + + /*If preferred mode yet not found, + * try to choose maximum mode as preferred*/ + if (!preferred_found && list_count) { + struct mode_timing *mode_timing = + dal_dcs_mode_timing_list_at_index(list, list_count-1); + mode_timing->mode_info.flags.PREFERRED = 1; + preferred_found = true; + } + + update_undefined_timing_parameters_with_defaults(dcs, list); + + update_stereo_3d_features(dcs, list); + + return true; +} + +/*updates the dcs_mode_timing_list of given path with +mode_timing reported by this DCS*/ +void dal_dcs_update_ts_timing_list_on_display( + struct dcs *dcs, + uint32_t display_index) +{ + uint32_t i; + uint32_t size; + struct dcs_mode_timing_list *list = + dal_dcs_mode_timing_list_create(256); + + if (!list) + return; + + if (!build_mode_timing_list(dcs, list)) + goto update_list_exit; + + dal_timing_service_clear_mode_timing_list_for_path( + dcs->ts, display_index); + + size = dal_dcs_mode_timing_list_get_count(list); + + for (i = 0; i < size; ++i) { + + struct mode_timing mode_timing_2d; + + struct mode_timing *mode_timing = + dal_dcs_mode_timing_list_at_index(list, i); + + bool added = dal_timing_service_add_mode_timing_to_path( + dcs->ts, display_index, mode_timing); + + if (!added && mode_timing->crtc_timing.timing_3d_format != + TIMING_3D_FORMAT_NONE) { + mode_timing_2d = *mode_timing; + mode_timing_2d.crtc_timing.timing_3d_format = + TIMING_3D_FORMAT_NONE; + mode_timing = &mode_timing_2d; + added = dal_timing_service_add_mode_timing_to_path( + dcs->ts, display_index, mode_timing); + } + + if (added) + update_edid_supported_max_bw(dcs, mode_timing); + + } + /*TODO: add customized mode updates*/ +update_list_exit: + + if (list) + dal_dcs_mode_timing_list_destroy(&list); +} + +bool dal_dcs_query_ddc_data( + struct dcs *dcs, + uint32_t address, + uint8_t *write_buf, + uint32_t write_buff_size, + uint8_t *read_buff, + uint32_t read_buff_size) +{ + if (!dcs->ddc_service) + return false; + + return dal_ddc_service_query_ddc_data( + dcs->ddc_service, + address, + write_buf, write_buff_size, + read_buff, read_buff_size); +} + +bool dal_dcs_get_vendor_product_id_info( + struct dcs *dcs, + struct vendor_product_id_info *info) +{ + struct edid_base *edid_base; + + if (!dcs->edid_mgr) + return false; + + edid_base = dal_edid_mgr_get_edid(dcs->edid_mgr); + + if (!edid_base) + return false; + + return dal_edid_get_vendor_product_id_info(edid_base, info); +} + +bool dal_dcs_get_display_name( + struct dcs *dcs, + uint8_t *name, + uint32_t size) +{ + struct edid_base *edid_base; + + if (!dcs->edid_mgr) + return false; + + edid_base = dal_edid_mgr_get_edid(dcs->edid_mgr); + + if (!edid_base) + return false; + + return dal_edid_get_display_name(edid_base, name, size); +} + +bool dal_dcs_get_display_characteristics( + struct dcs *dcs, + struct display_characteristics *characteristics) +{ + struct edid_base *edid_base; + + if (!dcs->edid_mgr) + return false; + + edid_base = dal_edid_mgr_get_edid(dcs->edid_mgr); + + if (!edid_base) + return false; + + return dal_edid_get_display_characteristics(edid_base, characteristics); +} + +bool dal_dcs_get_screen_info( + struct dcs *dcs, + struct edid_screen_info *info) +{ + struct edid_base *edid_base; + + if (!dcs->edid_mgr) + return false; + + edid_base = dal_edid_mgr_get_edid(dcs->edid_mgr); + + if (!edid_base) + return false; + + return dal_edid_get_screen_info(edid_base, info); +} + +enum dcs_edid_connector_type dal_dcs_get_connector_type(struct dcs *dcs) +{ + struct edid_base *edid_base; + + if (!dcs->edid_mgr) + return false; + + edid_base = dal_edid_mgr_get_edid(dcs->edid_mgr); + + if (!edid_base) + return false; + + return dal_edid_get_connector_type(edid_base); +} + +enum display_dongle_type dal_dcs_get_dongle_type(struct dcs *dcs) +{ + return dcs->sink_caps.dongle_type; +} + +static void calculate_av_sync( + struct display_sink_capability *sink_cap, + struct av_sync_data *sync_data) +{ + uint32_t granularity_factor = 0; /* in microsecond (us) */ + /* Get Audio Decode Latency and Granularity Factor for DP 1.2 or greater + */ + if (sink_cap->dpcd_revision < DCS_DPCD_REV_12) + return; + + { + uint32_t a_decode_latency; + uint32_t a_post_process_latency; + uint32_t a_delay_insert; + /* Convert DPCD registers to proper value av_granularity[3:0] */ + switch (sync_data->av_granularity & 0xF) { + case 0: + granularity_factor = 3000; + break; + case 1: + granularity_factor = 2000; + break; + case 2: + granularity_factor = 1000; + break; + case 3: + granularity_factor = 500; + break; + case 4: + granularity_factor = 200; + break; + case 5: + granularity_factor = 100; + break; + case 6: + granularity_factor = 10; + break; + case 7: + granularity_factor = 1; + break; + default: + granularity_factor = 2000; + break; + } + + a_decode_latency = sync_data->aud_dec_lat1 + + (sync_data->aud_dec_lat2 << 8); + a_decode_latency *= granularity_factor; + + a_post_process_latency = sync_data->aud_pp_lat1 + + (sync_data->aud_pp_lat2 << 8); + a_post_process_latency *= granularity_factor; + + a_delay_insert = sync_data->aud_del_ins1 + + (sync_data->aud_del_ins2 << 8) + + (sync_data->aud_del_ins3 << 16); + a_delay_insert *= granularity_factor; + + sink_cap->audio_latency = a_decode_latency + + a_post_process_latency; + + sink_cap->additional_audio_delay = (uint8_t)a_delay_insert; + + } + + { + /* av_granularity[7:4] */ + switch ((sync_data->av_granularity >> 4) & 0x0F) { + case 0: + granularity_factor = 3000; + break; + case 1: + granularity_factor = 2000; + break; + case 2: + granularity_factor = 1000; + break; + case 3: + granularity_factor = 500; + break; + case 4: + granularity_factor = 200; + break; + case 5: + granularity_factor = 100; + break; + default: + granularity_factor = 2000; + break; + } + + sink_cap->video_latency_interlace = sync_data->vid_inter_lat * + granularity_factor; + sink_cap->video_latency_progressive = sync_data->vid_prog_lat * + granularity_factor; + } +} + +static void setup_default_hdmi_sink_cap( + struct dcs *dcs, + struct display_sink_capability *sink_cap) +{ + if (!sink_cap) + return; + + switch (dal_graphics_object_id_get_connector_id( + dcs->graphics_object_id)) { + case CONNECTOR_ID_HDMI_TYPE_A: + sink_cap->max_hdmi_deep_color = HW_COLOR_DEPTH_121212; + sink_cap->max_hdmi_pixel_clock = + NATIVE_HDMI_MAX_PIXEL_CLOCK_IN_KHZ; + break; + + default: + sink_cap->max_hdmi_deep_color = HW_COLOR_DEPTH_888; + sink_cap->max_hdmi_pixel_clock = TMDS_MAX_PIXEL_CLOCK_IN_KHZ; + break; + } +} + +void dal_dcs_query_sink_capability( + struct dcs *dcs, + struct display_sink_capability *sink_cap, + bool hpd_sense_bit) +{ + /* We allow passing NULL pointer */ + struct display_sink_capability dummy_sink_cap; + enum connector_id connector = + dal_graphics_object_id_get_connector_id( + dcs->graphics_object_id); + if (sink_cap == NULL || hpd_sense_bit == false) { + dal_memset(&dummy_sink_cap, 0, sizeof(dummy_sink_cap)); + sink_cap = &dummy_sink_cap; + } else + dal_memset(sink_cap, 0, sizeof(struct display_sink_capability)); + + /* reset the dp receiver id info */ + if (dcs->ddc_service) + dal_ddc_service_reset_dp_receiver_id_info(dcs->ddc_service); + + switch (connector) { + case CONNECTOR_ID_SINGLE_LINK_DVII: + case CONNECTOR_ID_DUAL_LINK_DVII: + case CONNECTOR_ID_SINGLE_LINK_DVID: + case CONNECTOR_ID_DUAL_LINK_DVID: + case CONNECTOR_ID_LVDS: + case CONNECTOR_ID_HDMI_TYPE_A: + sink_cap->ss_supported = true; + break; + case CONNECTOR_ID_DISPLAY_PORT: + if (dcs->ddc_service) + sink_cap->ss_supported = + !dal_ddc_service_is_in_aux_transaction_mode( + dcs->ddc_service); + break; + + default: + sink_cap->ss_supported = false; + break; + } + + setup_default_hdmi_sink_cap(dcs, sink_cap); + + if (!dcs->ddc_service) { + /* save currently retrieved capabilities */ + dcs->sink_caps = *sink_cap; + return; + } + + switch (connector) { + case CONNECTOR_ID_EDP: + /* eDP's capability not change. */ + *sink_cap = dcs->sink_caps; + if (dcs->sink_caps.is_edp_sink_cap_valid == false) { + struct av_sync_data av_sync_data = {0}; + + /* query some PSR etc info. */ + dal_ddc_service_aux_query_dp_sink_capability( + dcs->ddc_service, sink_cap); + dal_ddc_service_retrieve_dpcd_data( + dcs->ddc_service, &av_sync_data); + + calculate_av_sync(sink_cap, &av_sync_data); + /* query once. */ + sink_cap->is_edp_sink_cap_valid = true; + } + break; + + case CONNECTOR_ID_DISPLAY_PORT: + if (dal_ddc_service_is_in_aux_transaction_mode( + dcs->ddc_service)) { + struct av_sync_data av_sync_data = {0}; + + dal_ddc_service_aux_query_dp_sink_capability( + dcs->ddc_service, sink_cap); + dal_ddc_service_retrieve_dpcd_data( + dcs->ddc_service, &av_sync_data); + calculate_av_sync(sink_cap, &av_sync_data); + sink_cap->is_edp_sink_cap_valid = true; + } else { + dal_ddc_service_i2c_query_dp_dual_mode_adaptor( + dcs->ddc_service, sink_cap); + + if (sink_cap->dongle_type == + DISPLAY_DONGLE_DP_HDMI_DONGLE) { + if (dcs->flags.DEEP_COLOR_OVER_DP_DONGLE || + sink_cap->max_hdmi_pixel_clock > + TMDS_MAX_PIXEL_CLOCK_IN_KHZ) + sink_cap->max_hdmi_deep_color = + HW_COLOR_DEPTH_121212; + + if (dcs->flags.HIGH_PIXEL_CLK_OVER_DP_DONGLE) + sink_cap->max_hdmi_pixel_clock = + NATIVE_HDMI_MAX_PIXEL_CLOCK_IN_KHZ; + } + + } + + /* Update dongle data even if dongle was removed */ + if (sink_cap->dongle_type != dcs->sink_caps.dongle_type) { + /* for DP->VGA or DP->DualLink DVI dongles we want to + * use the HiRes set of default modes */ + if (sink_cap->dongle_type == + DISPLAY_DONGLE_DP_VGA_CONVERTER || + sink_cap->dongle_type == + DISPLAY_DONGLE_DP_DVI_CONVERTER) + dcs->dco_funcs.add_mode_timing = + dal_default_modes_dco_multi_sync_dco_add_mode_timing; + else + dcs->dco_funcs.add_mode_timing = + dal_default_modes_dco_add_mode_timing; + } + break; + + case CONNECTOR_ID_VGA: + /* DP2RGB translator */ + if (dal_ddc_service_is_in_aux_transaction_mode( + dcs->ddc_service)) { + /* For on-board DP translator, board connector is VGA or + * LCD. DAL treat this display as CRT or LCD different + * from DP dongle case which DAL treat it as DP based + * on sink_cap.dongle_type. */ + dal_ddc_service_aux_query_dp_sink_capability( + dcs->ddc_service, sink_cap); + sink_cap->dongle_type = DISPLAY_DONGLE_NONE; + + } + break; + + case CONNECTOR_ID_LVDS: + /* DP2LVDS translator */ + if (dal_ddc_service_is_in_aux_transaction_mode( + dcs->ddc_service)) { + /* For on-board DP translator, board connector is VGA or + * LCD. DAL treat this display as CRT or LCD different + * from DP dongle case which DAL treat it as DP based on + * sink_cap.dongle_type. */ + dal_ddc_service_aux_query_dp_sink_capability( + dcs->ddc_service, sink_cap); + sink_cap->dongle_type = DISPLAY_DONGLE_NONE; + } + break; + default: + break; + } + + /* save currently retrieved capabilities */ + dcs->sink_caps = *sink_cap; +} + +void dal_dcs_reset_sink_capability(struct dcs *dcs) +{ + dal_memset(&dcs->sink_caps, 0, sizeof(dcs->sink_caps)); + setup_default_hdmi_sink_cap(dcs, &dcs->sink_caps); + + if (dcs->ddc_service) + /* reset the dp receiver id info */ + dal_ddc_service_reset_dp_receiver_id_info(dcs->ddc_service); + + dcs->flags.CONTAINER_ID_INITIALIZED = false; +} + +bool dal_dcs_get_sink_capability( + struct dcs *dcs, + struct display_sink_capability *sink_cap) +{ + if (!sink_cap) + return false; + + *sink_cap = dcs->sink_caps; + + return true; +} + +bool dal_dcs_emulate_sink_capability( + struct dcs *dcs, + struct display_sink_capability *sink_cap) +{ + if (!sink_cap) + return false; + + dcs->sink_caps = *sink_cap; + + return true; +} + +bool dal_dcs_get_display_color_depth( + struct dcs *dcs, + struct display_color_depth_support *color_depth) +{ + bool ret; + struct edid_base *edid_base; + + if (!dcs->edid_mgr) + return false; + + edid_base = dal_edid_mgr_get_edid(dcs->edid_mgr); + + if (!edid_base) + return false; + + ret = dal_edid_get_display_color_depth(edid_base, color_depth); + + if (!ret) + ret = get_default_color_depth(dcs, color_depth); + + return ret; +} + +bool dal_dcs_get_display_pixel_encoding( + struct dcs *dcs, + struct display_pixel_encoding_support *pixel_encoding) +{ + bool ret; + struct edid_base *edid_base; + + if (!dcs->edid_mgr) + return false; + + edid_base = dal_edid_mgr_get_edid(dcs->edid_mgr); + + if (!edid_base) + return false; + + ret = dal_edid_get_display_pixel_encoding(edid_base, pixel_encoding); + + if (!ret) + ret = get_default_pixel_encoding(dcs, pixel_encoding); + + return ret; +} + +bool dal_dcs_get_cea861_support( + struct dcs *dcs, + struct cea861_support *cea861_support) +{ + struct edid_base *edid_base; + + if (!dcs->edid_mgr) + return false; + + edid_base = dal_edid_mgr_get_edid(dcs->edid_mgr); + + if (!edid_base) + return false; + + return dal_edid_get_cea861_support(edid_base, cea861_support); +} + +bool dal_dcs_get_cea_vendor_specific_data_block( + struct dcs *dcs, + struct cea_vendor_specific_data_block *vendor_block) +{ + struct edid_base *edid_base; + + if (!dcs->edid_mgr) + return false; + + edid_base = dal_edid_mgr_get_edid(dcs->edid_mgr); + + if (!edid_base) + return false; + + return dal_edid_get_cea_vendor_specific_data_block( + edid_base, vendor_block); +} + +bool dal_dcs_get_cea_speaker_allocation_data_block( + struct dcs *dcs, + enum signal_type signal, + union cea_speaker_allocation_data_block *spkr_data) +{ + bool ret = false; + struct edid_base *edid_base = NULL; + + if (dcs->edid_mgr) + edid_base = dal_edid_mgr_get_edid(dcs->edid_mgr); + + ASSERT(spkr_data != NULL); + + + if (edid_base) + ret = dal_edid_get_cea_speaker_allocation_data_block( + edid_base, spkr_data); + + switch (signal) { + case SIGNAL_TYPE_EDP: + ret = false; + break; + + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_DISPLAY_PORT_MST: + if (dcs->flags.DISABLE_DP_AUDIO) { + ret = false; + break; + } + + if (edid_base) { + /*TODO: ??????*/ + if (dal_edid_get_cea_audio_modes(edid_base, NULL)) { + spkr_data->bits.FL_FR = 1; + ret = true; + } + } else if (!ret && dcs->flags.DP_AUDIO_FORCED) { + /* These are the speakers for default audio modes*/ + spkr_data->raw = 0; + spkr_data->bits.FL_FR = 1; + spkr_data->bits.FLC_FRC = 1; + spkr_data->bits.RL_RR = 1; + spkr_data->bits.RC = 1; + spkr_data->bits.LFE = 1; + ret = true; + } + break; + + case SIGNAL_TYPE_WIRELESS: + case SIGNAL_TYPE_HDMI_TYPE_A: + if (!ret) { + /*HDMI should always have audio modes, + since some panels do not support HDMI signal w/o audio + these are the speakers for default audio modes*/ + spkr_data->raw = 0; + spkr_data->bits.FL_FR = 1; + ret = true; + } + break; + + default: + break; + } + + return ret; +} + +bool dal_dcs_get_cea_colorimetry_data_block( + struct dcs *dcs, + struct cea_colorimetry_data_block *colorimetry_data_block) +{ + struct edid_base *edid_base; + + if (!dcs->edid_mgr) + return false; + + edid_base = dal_edid_mgr_get_edid(dcs->edid_mgr); + + if (!edid_base) + return false; + + return dal_edid_get_cea_colorimetry_data_block( + edid_base, colorimetry_data_block); +} + +bool dal_dcs_get_cea_video_capability_data_block( + struct dcs *dcs, + union cea_video_capability_data_block *video_capability_data_block) +{ + struct edid_base *edid_base; + + if (!dcs->edid_mgr) + return false; + + edid_base = dal_edid_mgr_get_edid(dcs->edid_mgr); + + if (!edid_base) + return false; + + return dal_edid_get_cea_video_capability_data_block( + edid_base, video_capability_data_block); +} + +uint32_t dal_dcs_get_extensions_num(struct dcs *dcs) +{ + struct edid_base *edid_base; + + if (!dcs->edid_mgr) + return false; + + edid_base = dal_edid_mgr_get_edid(dcs->edid_mgr); + + if (!edid_base) + return false; + + return dal_edid_get_num_of_extension(edid_base); +} + +const struct dcs_cea_audio_mode_list *dal_dcs_get_cea_audio_modes( + struct dcs *dcs, + enum signal_type signal) +{ + if (!dcs->audio_modes || + dal_dcs_cea_audio_mode_list_get_count(dcs->audio_modes) == 0) + return NULL; + + return dcs->audio_modes; +} + +bool dal_dcs_is_audio_supported(struct dcs *dcs) +{ + if (!dcs->audio_modes || + dal_dcs_cea_audio_mode_list_get_count(dcs->audio_modes) == 0) + return false; + + return true; +} + +bool dal_dcs_validate_customized_mode( + struct dcs *dcs, + const struct dcs_customized_mode *customized_mode) +{ + /*TODO: add implementation */ + return false; +} + +bool dal_dcs_add_customized_mode( + struct dcs *dcs, + struct dcs_customized_mode *customized_mode) +{ + /*TODO: add implementation */ + return false; +} + +bool dal_dcs_delete_customized_mode(struct dcs *dcs, uint32_t index) +{ + /*TODO: add implementation*/ + return false; +} + +const struct dcs_customized_mode_list *dal_dcs_get_customized_modes( + struct dcs *dcs) +{ + /*TODO: add implementation*/ + return NULL; +} + +bool dal_dcs_delete_mode_timing_override( + struct dcs *dcs, + struct dcs_override_mode_timing *dcs_mode_timing) +{ + /*TODO: add implementation*/ + return false; +} + +bool dal_dcs_set_mode_timing_override( + struct dcs *dcs, + uint32_t display_index, + struct dcs_override_mode_timing *dcs_mode_timing) +{ + /*TODO: add implementation*/ + return false; +} + +bool dal_dcs_get_timing_override_for_mode( + struct dcs *dcs, + uint32_t display_index, + struct mode_info *mode_info, + struct dcs_override_mode_timing_list *dcs_mode_timing_list) +{ + /*TODO: add implementation*/ + return false; +} + +uint32_t dal_dcs_get_num_mode_timing_overrides(struct dcs *dcs) +{ + /*TODO: add implementation*/ + return 0; +} + +bool dal_dcs_get_timing_override_list( + struct dcs *dcs, + uint32_t display_index, + struct dcs_override_mode_timing_list *dcs_mode_timing_list, + uint32_t size) +{ + /*TODO: add implementation*/ + return false; +} + +bool dal_dcs_get_supported_force_hdtv_mode( + struct dcs *dcs, + union hdtv_mode_support *hdtv_mode) +{ + /*TODO: add implementation*/ + return false; +} + +bool dal_dcs_get_user_force_hdtv_mode( + struct dcs *dcs, + union hdtv_mode_support *hdtv_mode) +{ + /*TODO: add implementation*/ + return false; +} + +bool dal_dcs_set_user_force_hdtv_mode( + struct dcs *dcs, + const union hdtv_mode_support *hdtv_mode) +{ + /*TODO: add implementation*/ + return false; +} + +bool dal_dcs_get_fid9204_allow_ce_mode_only_option( + struct dcs *dcs, + bool is_hdmi, + bool *enable) +{ + /*TODO: add implementation*/ + return false; +} + +bool dal_dcs_set_fid9204_allow_ce_mode_only_option( + struct dcs *dcs, + bool is_hdmi, + bool enable) +{ + /*TODO: add implementation*/ + return false; +} + +bool dal_dcs_get_panel_misc_info( + struct dcs *dcs, + union panel_misc_info *panel_info) +{ + if (!dcs->vbios_dco) + return false; + + return dal_vbios_dco_get_panel_misc_info(dcs->vbios_dco, panel_info); +} + +enum ddc_result dal_dcs_dpcd_read( + struct dcs *dcs, + uint32_t address, + uint8_t *buffer, + uint32_t length) +{ + if (!dcs->ddc_service) + return DDC_RESULT_UNKNOWN; + + return dal_ddc_service_read_dpcd_data( + dcs->ddc_service, address, buffer, length); +} + +enum ddc_result dal_dcs_dpcd_write( + struct dcs *dcs, + uint32_t address, + const uint8_t *buffer, + uint32_t length) +{ + if (!dcs->ddc_service) + return DDC_RESULT_UNKNOWN; + + return dal_ddc_service_write_dpcd_data( + dcs->ddc_service, address, buffer, length); +} + +bool dal_dcs_get_range_limit( + struct dcs *dcs, + struct display_range_limits *limit) +{ + /*TODO: add implementation*/ + return false; +} + +bool dal_dcs_set_range_limit_override( + struct dcs *dcs, + struct display_range_limits *limit) +{ + /*TODO: add implementation*/ + return false; +} + +bool dal_dcs_get_user_select_limit( + struct dcs *dcs, + struct monitor_user_select_limits *limit) +{ + /*TODO: add implementation*/ + return false; +} + +bool dal_dcs_set_user_select_limit( + struct dcs *dcs, + struct monitor_user_select_limits *limit) +{ + /*TODO: add implementation*/ + return false; +} + +bool dal_dcs_get_dongle_mode_support( + struct dcs *dcs, + union hdtv_mode_support *hdtv_mode) +{ + /*TODO: add implementation*/ + return false; +} + +bool dal_dcs_get_timing_limits( + struct dcs *dcs, + struct timing_limits *timing_limits) +{ + if (!timing_limits) + return false; + + *timing_limits = dcs->timing_limits; + return true; +} + +bool dal_dcs_get_drr_config( + struct dcs *dcs, + struct drr_config *config) +{ + if (!config) + return false; + + *config = dcs->drr_config; + return true; +} + +bool dal_dcs_force_dp_audio(struct dcs *dcs, bool force_audio_on) +{ + dcs->flags.DP_AUDIO_FORCED = force_audio_on; + build_audio_modes(dcs); + return true; +} + +bool dal_dcs_is_dp_audio_forced(struct dcs *dcs) +{ + return dcs->flags.DP_AUDIO_FORCED; +} + +const struct monitor_patch_info *dal_dcs_get_monitor_patch_info( + struct dcs *dcs, + enum monitor_patch_type patch_type) +{ + if (!dcs->edid_mgr) + return NULL; + + return dal_edid_mgr_get_monitor_patch_info(dcs->edid_mgr, patch_type); +} + +bool dal_dcs_set_monitor_patch_info( + struct dcs *dcs, + struct monitor_patch_info *patch_info) +{ + if (!dcs->edid_mgr) + return false; + + return dal_edid_mgr_set_monitor_patch_info(dcs->edid_mgr, patch_info); +} + +enum dcs_packed_pixel_format dal_dcs_get_enabled_packed_pixel_format( + struct dcs *dcs) +{ + return dcs->packed_pixel_format; +} + +enum dcs_packed_pixel_format dal_dcs_get_monitor_packed_pixel_format( + struct dcs *dcs) +{ + /* Default to unpacked.*/ + enum dcs_packed_pixel_format format = + DCS_PACKED_PIXEL_FORMAT_NOT_PACKED; + const struct monitor_patch_info *patch_info; + struct display_color_depth_support color_depth; + struct edid_base *edid_base; + + if (!dcs->edid_mgr) + return DCS_PACKED_PIXEL_FORMAT_NOT_PACKED; + + /* Obtain packed pixel info from relevant patch*/ + patch_info = dal_edid_mgr_get_monitor_patch_info( + dcs->edid_mgr, MONITOR_PATCH_TYPE_PACKED_PIXEL_FORMAT); + + if (!patch_info) + /* Try other patch instead */ + patch_info = dal_edid_mgr_get_monitor_patch_info( + dcs->edid_mgr, MONITOR_PATCH_TYPE_MULTIPLE_PACKED_TYPE); + + /* Retrieve packed pixel format from patch*/ + if (patch_info) + format = patch_info->param; + + /* Check for native 10-bit support */ + edid_base = dal_edid_mgr_get_edid(dcs->edid_mgr); + + if (!edid_base) + return format; + + if (dal_edid_get_display_color_depth(edid_base, &color_depth)) { + if (color_depth.mask & COLOR_DEPTH_INDEX_101010) { + /* No current 10-bit display supports packed pixel.*/ + ASSERT(format == DCS_PACKED_PIXEL_FORMAT_NOT_PACKED); + format = DCS_PACKED_PIXEL_FORMAT_NOT_PACKED; + } + } + return format; +} + +bool dal_dcs_report_single_selected_timing(struct dcs *dcs) +{ + return dcs->flags.REPORT_SINGLE_SELECTED_TIMING; +} + +bool dal_dcs_can_tile_scale(struct dcs *dcs) +{ + return dcs->flags.TILED_DISPLAY_CAN_SCALE; +} + +void dal_dcs_set_single_selected_timing_restriction( + struct dcs *dcs, + bool value) +{ + dcs->flags.REPORT_SINGLE_SELECTED_TIMING = value; +} + +const struct dcs_edid_supported_max_bw *dal_dcs_get_edid_supported_max_bw( + struct dcs *dcs) +{ + return &dcs->edid_max_bw; +} + +bool dal_dcs_get_container_id(struct dcs *dcs, + struct dcs_container_id *container_id) +{ + if (!container_id || !dcs->flags.CONTAINER_ID_INITIALIZED) + return false; + + *container_id = dcs->container_id; + return true; +} + +bool dal_dcs_set_container_id(struct dcs *dcs, + struct dcs_container_id *container_id) +{ + if (!container_id) + return false; + + dcs->container_id = *container_id; + dcs->flags.CONTAINER_ID_INITIALIZED = 1; + return true; +} + +bool dal_dcs_is_non_continous_frequency(struct dcs *dcs) +{ + struct edid_base *edid_base; + + if (!dcs->edid_mgr) + return false; + + edid_base = dal_edid_mgr_get_edid(dcs->edid_mgr); + + if (!edid_base) + return false; + + return dal_edid_is_non_continous_frequency(edid_base); +} + +struct dcs_stereo_3d_features dal_dcs_get_stereo_3d_features( + struct dcs *dcs, + enum timing_3d_format format) +{ + if (format < TIMING_3D_FORMAT_MAX) + return dcs->stereo_3d_features[format]; + + return dcs->stereo_3d_features[TIMING_3D_FORMAT_NONE]; +} + +union stereo_3d_support dal_dcs_get_stereo_3d_support(struct dcs *dcs) +{ + return dcs->stereo_3d_support; +} + +void dal_dcs_override_stereo_3d_support( + struct dcs *dcs, + union stereo_3d_support support) +{ + /*TODO: add implementation*/ +} + +void dal_dcs_set_remote_display_receiver_capabilities( + struct dcs *dcs, + const struct dal_remote_display_receiver_capability *cap) +{ + if (dcs->rdrm != NULL) { + dal_remote_display_receiver_set_capabilities(dcs->rdrm, cap); + build_audio_modes(dcs); + } +} + +void dal_dcs_clear_remote_display_receiver_capabilities(struct dcs *dcs) +{ + if (dcs->rdrm != NULL) + dal_remote_display_receiver_clear_capabilities(dcs->rdrm); +} + +static bool patch_tiled_display_info( + struct dcs *dcs, + struct dcs_display_tile *display_tile, + struct vendor_product_id_info *tiled_vendor_info, + bool first_display) +{ + const struct monitor_patch_info *patch_info; + bool status = false; + /* search for the first Tiled Display data block*/ + dal_memset(display_tile, 0, sizeof(struct dcs_display_tile)); + patch_info = dal_dcs_get_monitor_patch_info( + dcs, MONITOR_PATCH_TYPE_TILED_DISPLAY); + + if (!patch_info) + return false; + + if ((patch_info->param == EDID_TILED_DISPLAY_1) || + (patch_info->param == EDID_TILED_DISPLAY_2)) { + + display_tile->flags.CAN_SCALE = 0; + display_tile->height = TILED_DISPLAY_VERTICAL; + display_tile->width = TILED_DISPLAY_HORIZONTAL; + display_tile->rows = 1; + display_tile->cols = 2; + display_tile->row = 0; + display_tile->col = (first_display) ? 0 : 1; + + /* No bezel Info */ + /* No single Enclosure */ + + display_tile->topology_id.manufacturer_id = + tiled_vendor_info->manufacturer_id; + display_tile->topology_id.product_id = + tiled_vendor_info->product_id; + display_tile->topology_id.serial_id = + dcs->graphics_object_id.enum_id; + status = true; + } + return status; +} + +bool dal_dcs_get_display_tile_info( + struct dcs *dcs, + struct dcs_display_tile *display_tile, + bool first_display) +{ + bool success; + struct edid_base *edid_base; + + if (!dcs->edid_mgr) + return false; + + edid_base = dal_edid_mgr_get_edid(dcs->edid_mgr); + + if (!edid_base) + return false; + + success = dal_edid_get_display_tile_info(edid_base, display_tile); + + if (!success) { + struct vendor_product_id_info tiled_vendor_info = { 0 }; + + if (dal_edid_get_vendor_product_id_info( + edid_base, &tiled_vendor_info)) + success = patch_tiled_display_info( + dcs, + display_tile, + &tiled_vendor_info, + first_display); + } + + if (success) { + /* use 2 bytes from manufacturerId*/ + /* use 2 bytes from productId*/ + /* use 4 bytes from serialId*/ + display_tile->id = display_tile->topology_id.manufacturer_id + + (display_tile->topology_id.product_id << 16) + + ((uint64_t)(display_tile->topology_id.serial_id) << 32); + + } + + return success; +} + +enum edid_retrieve_status dal_dcs_override_raw_edid( + struct dcs *dcs, + uint32_t len, + uint8_t *data) +{ + enum edid_retrieve_status ret = EDID_RETRIEVE_FAIL; + + if (dcs->edid_mgr) + ret = dal_edid_mgr_override_raw_data(dcs->edid_mgr, len, data); + + if (ret != EDID_RETRIEVE_SUCCESS) + return ret; + + if (!dal_edid_mgr_get_edid(dcs->edid_mgr)) + return ret; + + update_cached_data(dcs); + + /*TODO: update range limits for VGA*/ + + return ret; +} diff --git a/drivers/gpu/drm/amd/dal/dcs/dcs_list.c b/drivers/gpu/drm/amd/dal/dcs/dcs_list.c new file mode 100644 index 000000000000..2e6a6db69a3a --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/dcs_list.c @@ -0,0 +1,180 @@ +/* + * Copyright 2012-15 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 + * + */ + +#include "dal_services.h" +#include "include/dcs_interface.h" +#include "include/timing_service_types.h" +#include "include/flat_set.h" +#include "include/vector.h" + +struct dcs_mode_timing_list *dal_dcs_mode_timing_list_create(uint32_t list_size) +{ + struct dcs_mode_timing_list *list; + struct flat_set_init_data init_data = { + .capacity = list_size, + .struct_size = sizeof(struct mode_timing), + .funcs = { .less_than = dal_mode_timing_less_than } }; + + list = dal_alloc(sizeof(struct dcs_mode_timing_list)); + + if (!list) + return NULL; + + if (dal_flat_set_construct(&list->list, &init_data)) + return list; + + dal_free(list); + return NULL; +} + +void dal_dcs_mode_timing_list_destroy( + struct dcs_mode_timing_list **list) +{ + if (!list || !*list) + return; + + dal_flat_set_destruct(&(*list)->list); + dal_free(*list); + *list = NULL; +} + +bool dal_dcs_mode_timing_list_append( + struct dcs_mode_timing_list *list, + const struct mode_timing *mode_timing) +{ + return dal_flat_set_insert(&list->list, mode_timing); +} +uint32_t dal_dcs_mode_timing_list_get_count( + const struct dcs_mode_timing_list *list) +{ + return dal_flat_set_get_count(&list->list); +} + +void dal_dcs_mode_timing_list_remove_at_index( + struct dcs_mode_timing_list *list, + uint32_t index) +{ + dal_flat_set_remove_at_index(&list->list, index); +} + +struct mode_timing *dal_dcs_mode_timing_list_at_index( + struct dcs_mode_timing_list *list, + uint32_t index) +{ + return dal_flat_set_at_index(&list->list, index); +} + +void dal_dcs_mode_timing_list_clear(struct dcs_mode_timing_list *list) +{ + dal_flat_set_clear(&list->list); +} + +struct dcs_cea_audio_mode_list { + struct vector list; +}; + +struct dcs_cea_audio_mode_list *dal_dcs_cea_audio_mode_list_create( + uint32_t list_size) +{ + struct dcs_cea_audio_mode_list *list; + + list = dal_alloc(sizeof(struct dcs_cea_audio_mode_list)); + + if (!list) + return NULL; + + if (dal_vector_construct( + &list->list, list_size, sizeof(struct cea_audio_mode))) + return list; + + dal_free(list); + return NULL; +} + +void dal_dcs_cea_audio_mode_list_destroy( + struct dcs_cea_audio_mode_list **list) +{ + if (!list || !*list) + return; + + dal_vector_destruct(&(*list)->list); + dal_free(*list); + *list = NULL; +} + +bool dal_dcs_cea_audio_mode_list_append( + struct dcs_cea_audio_mode_list *list, + struct cea_audio_mode *cea_audio_mode) +{ + return dal_vector_append(&list->list, cea_audio_mode); +} +uint32_t dal_dcs_cea_audio_mode_list_get_count( + const struct dcs_cea_audio_mode_list *list) +{ + return dal_vector_get_count(&list->list); +} + +void dal_dcs_cea_audio_mode_list_clear( + struct dcs_cea_audio_mode_list *list) +{ + list->list.count = 0; +} + +struct cea_audio_mode *dal_dcs_cea_audio_mode_list_at_index( + const struct dcs_cea_audio_mode_list *list, + uint32_t index) +{ + return dal_vector_at_index(&list->list, index); +} + +struct dcs_customized_mode_list { + struct flat_set list; +}; + +struct dcs_customized_mode_list *dal_dcs_customized_mode_list_create( + uint32_t list_size) +{ + /*TODO: add implementation*/ + return NULL; +} + +bool dal_dcs_customized_mode_list_append( + struct dcs_customized_mode_list *list, + struct dcs_customized_mode *customized_mode) +{ + return dal_flat_set_insert(&list->list, customized_mode); +} +uint32_t dal_dcs_customized_mode_list_get_count( + struct dcs_customized_mode_list *list) +{ + return dal_flat_set_get_count(&list->list); +} + +struct dcs_customized_mode *dal_dcs_customized_mode_list_at_index( + struct dcs_customized_mode_list *list, + uint32_t index) +{ + return dal_flat_set_at_index(&list->list, index); +} diff --git a/drivers/gpu/drm/amd/dal/dcs/ddc_i2caux_helper.c b/drivers/gpu/drm/amd/dal/dcs/ddc_i2caux_helper.c new file mode 100644 index 000000000000..ea2a6751b408 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/ddc_i2caux_helper.c @@ -0,0 +1,158 @@ +/* + * Copyright 2012-15 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 + * + */ + +#include "dal_services.h" +#include "ddc_i2caux_helper.h" +#include "include/ddc_service_types.h" +#include "include/vector.h" + +struct i2c_payloads { + struct vector payloads; +}; + +struct aux_payloads { + struct vector payloads; +}; + +struct i2c_payloads *dal_ddc_i2c_payloads_create(uint32_t count) +{ + struct i2c_payloads *payloads; + + payloads = dal_alloc(sizeof(struct i2c_payloads)); + + if (!payloads) + return NULL; + + if (dal_vector_construct( + &payloads->payloads, count, sizeof(struct i2c_payload))) + return payloads; + + dal_free(payloads); + return NULL; + +} + +struct i2c_payload *dal_ddc_i2c_payloads_get(struct i2c_payloads *p) +{ + return (struct i2c_payload *)p->payloads.container; +} + +uint32_t dal_ddc_i2c_payloads_get_count(struct i2c_payloads *p) +{ + return p->payloads.count; +} + +void dal_ddc_i2c_payloads_destroy(struct i2c_payloads **p) +{ + if (!p || !*p) + return; + dal_vector_destruct(&(*p)->payloads); + dal_free(*p); + *p = NULL; + +} + +struct aux_payloads *dal_ddc_aux_payloads_create(uint32_t count) +{ + struct aux_payloads *payloads; + + payloads = dal_alloc(sizeof(struct aux_payloads)); + + if (!payloads) + return NULL; + + if (dal_vector_construct( + &payloads->payloads, count, sizeof(struct aux_payloads))) + return payloads; + + dal_free(payloads); + return NULL; +} + +struct aux_payload *dal_ddc_aux_payloads_get(struct aux_payloads *p) +{ + return (struct aux_payload *)p->payloads.container; +} + +uint32_t dal_ddc_aux_payloads_get_count(struct aux_payloads *p) +{ + return p->payloads.count; +} + + +void dal_ddc_aux_payloads_destroy(struct aux_payloads **p) +{ + if (!p || !*p) + return; + + dal_vector_destruct(&(*p)->payloads); + dal_free(*p); + *p = NULL; + +} + +#define DDC_MIN(a, b) (((a) < (b)) ? (a) : (b)) + +void dal_ddc_i2c_payloads_add( + struct i2c_payloads *payloads, + uint32_t address, + uint32_t len, + uint8_t *data, + bool write) +{ + uint32_t payload_size = EDID_SEGMENT_SIZE; + uint32_t pos; + + for (pos = 0; pos < len; pos += payload_size) { + struct i2c_payload payload = { + .write = write, + .address = address, + .length = DDC_MIN(payload_size, len - pos), + .data = data + pos }; + dal_vector_append(&payloads->payloads, &payload); + } + +} + +void dal_ddc_aux_payloads_add( + struct aux_payloads *payloads, + uint32_t address, + uint32_t len, + uint8_t *data, + bool write) +{ + uint32_t payload_size = DEFAULT_AUX_MAX_DATA_SIZE; + uint32_t pos; + + for (pos = 0; pos < len; pos += payload_size) { + struct aux_payload payload = { + .i2c_over_aux = true, + .write = write, + .address = address, + .length = DDC_MIN(payload_size, len - pos), + .data = data + pos }; + dal_vector_append(&payloads->payloads, &payload); + } +} diff --git a/drivers/gpu/drm/amd/dal/dcs/ddc_i2caux_helper.h b/drivers/gpu/drm/amd/dal/dcs/ddc_i2caux_helper.h new file mode 100644 index 000000000000..17a088da3c38 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/ddc_i2caux_helper.h @@ -0,0 +1,61 @@ +/* + * Copyright 2012-15 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 + * + */ + +#ifndef __DAL_I2CAUX_HELPER_H__ +#define __DAL_I2CAUX_HELPER_H__ + +#include "include/i2caux_interface.h" + +#define MAX_EDID_BUFFER_SIZE 512 +#define EDID_SEGMENT_SIZE 256 + +struct i2c_payloads; +struct aux_payloads; + +struct i2c_payloads *dal_ddc_i2c_payloads_create(uint32_t count); +struct i2c_payload *dal_ddc_i2c_payloads_get(struct i2c_payloads *p); +uint32_t dal_ddc_i2c_payloads_get_count(struct i2c_payloads *p); +void dal_ddc_i2c_payloads_destroy(struct i2c_payloads **p); + +struct aux_payloads *dal_ddc_aux_payloads_create(uint32_t count); +struct aux_payload *dal_ddc_aux_payloads_get(struct aux_payloads *p); +uint32_t dal_ddc_aux_payloads_get_count(struct aux_payloads *p); +void dal_ddc_aux_payloads_destroy(struct aux_payloads **p); + +void dal_ddc_i2c_payloads_add( + struct i2c_payloads *payloads, + uint32_t address, + uint32_t len, + uint8_t *data, + bool write); + +void dal_ddc_aux_payloads_add( + struct aux_payloads *payloads, + uint32_t address, + uint32_t len, + uint8_t *data, + bool write); + +#endif /* __DAL_I2CAUX_HELPER_H__ */ diff --git a/drivers/gpu/drm/amd/dal/dcs/ddc_service.c b/drivers/gpu/drm/amd/dal/dcs/ddc_service.c new file mode 100644 index 000000000000..43789d766771 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/ddc_service.c @@ -0,0 +1,1338 @@ +/* + * Copyright 2012-15 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 + * + */ + +#include "dal_services.h" +#include "include/adapter_service_interface.h" +#include "include/i2caux_interface.h" +#include "include/ddc_service_interface.h" +#include "include/ddc_service_types.h" +#include "include/grph_object_id.h" +#include "include/dpcd_defs.h" +#include "include/logger_interface.h" +#include "ddc_i2caux_helper.h" +#include "ddc_service.h" + +#define AUX_POWER_UP_WA_DELAY 500 +#define I2C_OVER_AUX_DEFER_WA_DELAY 70 + +/* CV smart dongle slave address for retrieving supported HDTV modes*/ +#define CV_SMART_DONGLE_ADDRESS 0x20 +/* DVI-HDMI dongle slave address for retrieving dongle signature*/ +#define DVI_HDMI_DONGLE_ADDRESS 0x68 +static const int8_t dvi_hdmi_dongle_signature_str[] = "6140063500G"; +struct dvi_hdmi_dongle_signature_data { + int8_t vendor[3];/* "AMD" */ + uint8_t version[2]; + uint8_t size; + int8_t id[11];/* "6140063500G"*/ +}; +/* DP-HDMI dongle slave address for retrieving dongle signature*/ +#define DP_HDMI_DONGLE_ADDRESS 0x40 +static const uint8_t dp_hdmi_dongle_signature_str[] = "DP-HDMI ADAPTOR"; +#define DP_HDMI_DONGLE_SIGNATURE_EOT 0x04 + +struct dp_hdmi_dongle_signature_data { + int8_t id[15];/* "DP-HDMI ADAPTOR"*/ + uint8_t eot;/* end of transmition '\x4' */ +}; + +/* Address range from 0x00 to 0x1F.*/ +#define DP_ADAPTOR_TYPE2_SIZE 0x20 +#define DP_ADAPTOR_TYPE2_REG_ID 0x10 +#define DP_ADAPTOR_TYPE2_REG_MAX_TMDS_CLK 0x1D +/* Identifies adaptor as Dual-mode adaptor */ +#define DP_ADAPTOR_TYPE2_ID 0xA0 +/* MHz*/ +#define DP_ADAPTOR_TYPE2_MAX_TMDS_CLK 30 +/* MHz*/ +#define DP_ADAPTOR_TYPE2_MIN_TMDS_CLK 2 + +#define DDC_I2C_COMMAND_ENGINE I2C_COMMAND_ENGINE_SW + +enum edid_read_result { + EDID_READ_RESULT_EDID_MATCH = 0, + EDID_READ_RESULT_EDID_MISMATCH, + EDID_READ_RESULT_CHECKSUM_READ_ERR, + EDID_READ_RESULT_VENDOR_READ_ERR +}; + +union dp_downstream_port_present { + uint8_t byte; + struct { + uint8_t PORT_PRESENT:1; + uint8_t PORT_TYPE:2; + uint8_t FMT_CONVERSION:1; + uint8_t DETAILED_CAPS:1; + uint8_t RESERVED:3; + } fields; +}; + +union ddc_wa { + struct { + uint32_t DP_SKIP_POWER_OFF:1; + uint32_t DP_AUX_POWER_UP_WA_DELAY:1; + } bits; + uint32_t raw; +}; + +struct ddc_flags { + uint8_t EDID_QUERY_DONE_ONCE:1; + uint8_t IS_INTERNAL_DISPLAY:1; + uint8_t FORCE_READ_REPEATED_START:1; + uint8_t EDID_STRESS_READ:1; + +}; + +struct ddc_service { + struct ddc *ddc_pin; + struct ddc_flags flags; + union ddc_wa wa; + enum ddc_transaction_type transaction_type; + enum display_dongle_type dongle_type; + struct dp_receiver_id_info dp_receiver_id_info; + struct adapter_service *as; + struct dal_context *ctx; + + uint32_t address; + uint32_t edid_buf_len; + uint8_t edid_buf[MAX_EDID_BUFFER_SIZE]; +}; + +static bool construct( + struct ddc_service *ddc_service, + struct ddc_service_init_data *init_data) +{ + enum connector_id connector_id = + dal_graphics_object_id_get_connector_id(init_data->id); + + ddc_service->ctx = init_data->ctx; + ddc_service->as = init_data->as; + ddc_service->ddc_pin = dal_adapter_service_obtain_ddc( + init_data->as, init_data->id); + + ddc_service->flags.EDID_QUERY_DONE_ONCE = false; + + ddc_service->flags.FORCE_READ_REPEATED_START = + dal_adapter_service_is_feature_supported( + FEATURE_DDC_READ_FORCE_REPEATED_START); + + ddc_service->flags.EDID_STRESS_READ = + dal_adapter_service_is_feature_supported( + FEATURE_EDID_STRESS_READ); + + + ddc_service->flags.IS_INTERNAL_DISPLAY = + connector_id == CONNECTOR_ID_EDP || + connector_id == CONNECTOR_ID_LVDS; + + ddc_service->wa.raw = 0; + return true; +} + +struct ddc_service *dal_ddc_service_create( + struct ddc_service_init_data *init_data) +{ + struct ddc_service *ddc_service; + + ddc_service = dal_alloc(sizeof(struct ddc_service)); + + if (!ddc_service) + return NULL; + + if (construct(ddc_service, init_data)) + return ddc_service; + + dal_free(ddc_service); + return NULL; +} + +static void destruct(struct ddc_service *ddc) +{ + if (ddc->ddc_pin) + dal_adapter_service_release_ddc(ddc->as, ddc->ddc_pin); +} + +void dal_ddc_service_destroy(struct ddc_service **ddc) +{ + if (!ddc || !*ddc) { + BREAK_TO_DEBUGGER(); + return; + } + destruct(*ddc); + dal_free(*ddc); + *ddc = NULL; +} + +enum ddc_service_type dal_ddc_service_get_type(struct ddc_service *ddc) +{ + return DDC_SERVICE_TYPE_CONNECTOR; +} + +void dal_ddc_service_set_transaction_type( + struct ddc_service *ddc, + enum ddc_transaction_type type) +{ + ddc->transaction_type = type; +} + +bool dal_ddc_service_is_in_aux_transaction_mode(struct ddc_service *ddc) +{ + switch (ddc->transaction_type) { + case DDC_TRANSACTION_TYPE_I2C_OVER_AUX: + case DDC_TRANSACTION_TYPE_I2C_OVER_AUX_WITH_DEFER: + case DDC_TRANSACTION_TYPE_I2C_OVER_AUX_RETRY_DEFER: + return true; + default: + break; + } + return false; +} + +static uint32_t defer_delay_converter_wa( + struct ddc_service *ddc, + uint32_t defer_delay) +{ + struct dp_receiver_id_info dp_rec_info = {0}; + + if (dal_ddc_service_get_dp_receiver_id_info(ddc, &dp_rec_info) && + (dp_rec_info.branch_id == DP_BRANCH_DEVICE_ID_4) && + !dal_strncmp(dp_rec_info.branch_name, + DP_DVI_CONVERTER_ID_4, + sizeof(dp_rec_info.branch_name))) + return defer_delay > I2C_OVER_AUX_DEFER_WA_DELAY ? + defer_delay : I2C_OVER_AUX_DEFER_WA_DELAY; + + return defer_delay; + +} + +#define DP_TRANSLATOR_DELAY 5 + +static uint32_t get_defer_delay(struct ddc_service *ddc) +{ + uint32_t defer_delay = 0; + + switch (ddc->transaction_type) { + case DDC_TRANSACTION_TYPE_I2C_OVER_AUX: + if ((DISPLAY_DONGLE_DP_VGA_CONVERTER == ddc->dongle_type) || + (DISPLAY_DONGLE_DP_DVI_CONVERTER == ddc->dongle_type) || + (DISPLAY_DONGLE_DP_HDMI_CONVERTER == + ddc->dongle_type)) { + + defer_delay = DP_TRANSLATOR_DELAY; + + defer_delay = + defer_delay_converter_wa(ddc, defer_delay); + + } else /*sink has a delay different from an Active Converter*/ + defer_delay = 0; + break; + case DDC_TRANSACTION_TYPE_I2C_OVER_AUX_WITH_DEFER: + defer_delay = DP_TRANSLATOR_DELAY; + break; + default: + break; + } + return defer_delay; +} + +static bool i2c_read( + struct ddc_service *ddc, + uint32_t address, + uint8_t *buffer, + uint32_t len) +{ + uint8_t offs_data = 0; + struct i2c_payload payloads[2] = { + { + .write = true, + .address = address, + .length = 1, + .data = &offs_data }, + { + .write = false, + .address = address, + .length = len, + .data = buffer } }; + + struct i2c_command command = { + .payloads = payloads, + .number_of_payloads = 2, + .engine = DDC_I2C_COMMAND_ENGINE, + .speed = dal_adapter_service_get_sw_i2c_speed(ddc->as) }; + + return dal_i2caux_submit_i2c_command( + dal_adapter_service_get_i2caux(ddc->as), + ddc->ddc_pin, + &command); +} + +static bool ddc_edid_cmp(uint8_t *lhs, uint8_t *rhs, int32_t len) +{ + for (; len > 0 && *lhs++ == *rhs++; --len) + ; + return !len; +} + +static bool retreive_edid_data_at_offset( + struct ddc_service *ddc, + uint8_t offset, + uint8_t *buf, + uint8_t len) +{ + if (dal_ddc_service_is_in_aux_transaction_mode(ddc)) { + + struct aux_payload payloads[2] = { + { + .i2c_over_aux = true, + .write = true, + .address = ddc->address, + .length = 1, + .data = &offset }, + { + .i2c_over_aux = true, + .write = false, + .address = ddc->address, + .length = len, + .data = buf } }; + + struct aux_command cmd = { + .payloads = payloads, + .number_of_payloads = 2, + .defer_delay = get_defer_delay(ddc), + .max_defer_write_retry = 0 }; + + if (ddc->transaction_type == + DDC_TRANSACTION_TYPE_I2C_OVER_AUX_RETRY_DEFER) + cmd.max_defer_write_retry = AUX_MAX_DEFER_WRITE_RETRY; + + return dal_i2caux_submit_aux_command( + dal_adapter_service_get_i2caux(ddc->as), + ddc->ddc_pin, + &cmd); + } else { + struct i2c_payload payloads[2] = { + { + .write = true, + .address = ddc->address, + .length = 1, + .data = &offset }, + { + .write = false, + .address = ddc->address, + .length = len, + .data = buf } }; + + struct i2c_command cmd = { + .payloads = payloads, + .number_of_payloads = 2, + .engine = DDC_I2C_COMMAND_ENGINE, + .speed = dal_adapter_service_get_sw_i2c_speed( + ddc->as) }; + + return dal_i2caux_submit_i2c_command( + dal_adapter_service_get_i2caux(ddc->as), + ddc->ddc_pin, + &cmd); + } +} + +static enum edid_read_result verify_edid_1x_signature( + struct ddc_service *ddc) +{ + /* + * verify checksum and extension + */ + { + uint8_t data[DDC_EDID1X_EXT_CNT_AND_CHECKSUM_LEN]; + + if (!retreive_edid_data_at_offset( + ddc, + DDC_EDID1X_EXT_CNT_AND_CHECKSUM_OFFSET, + data, + DDC_EDID1X_EXT_CNT_AND_CHECKSUM_LEN)) + return EDID_READ_RESULT_CHECKSUM_READ_ERR; + + if (!ddc_edid_cmp( + data, + &ddc->edid_buf[DDC_EDID1X_EXT_CNT_AND_CHECKSUM_OFFSET], + DDC_EDID1X_EXT_CNT_AND_CHECKSUM_LEN)) + return EDID_READ_RESULT_EDID_MISMATCH; + } + + /* + * verify manufacture and product ID + */ + { + uint8_t data[DDC_EDID1X_VENDORID_SIGNATURE_LEN]; + + if (!retreive_edid_data_at_offset( + ddc, + DDC_EDID1X_VENDORID_SIGNATURE_OFFSET, + data, + DDC_EDID1X_VENDORID_SIGNATURE_LEN)) + return EDID_READ_RESULT_CHECKSUM_READ_ERR; + + if (!ddc_edid_cmp( + data, + &ddc->edid_buf[DDC_EDID1X_VENDORID_SIGNATURE_OFFSET], + DDC_EDID1X_VENDORID_SIGNATURE_LEN)) + return EDID_READ_RESULT_EDID_MISMATCH; + } + return EDID_READ_RESULT_EDID_MATCH; +} + +static enum edid_read_result verify_edid_20_signature( + struct ddc_service *ddc) +{ + /* + * verify checksum and extension + */ + { + uint8_t data[DDC_EDID20_CHECKSUM_LEN]; + + if (!retreive_edid_data_at_offset( + ddc, + DDC_EDID20_CHECKSUM_OFFSET, + data, + DDC_EDID20_CHECKSUM_LEN)) + return EDID_READ_RESULT_CHECKSUM_READ_ERR; + + if (!ddc_edid_cmp( + data, + &ddc->edid_buf[DDC_EDID20_CHECKSUM_OFFSET], + DDC_EDID20_CHECKSUM_LEN)) + return EDID_READ_RESULT_EDID_MISMATCH; + } + + /* + * verify manufacture and product ID + */ + { + uint8_t data[DDC_EDID20_VENDORID_SIGNATURE_LEN]; + + if (!retreive_edid_data_at_offset( + ddc, + DDC_EDID20_VENDORID_SIGNATURE_OFFSET, + data, + DDC_EDID20_VENDORID_SIGNATURE_LEN)) + return EDID_READ_RESULT_CHECKSUM_READ_ERR; + + if (!ddc_edid_cmp( + data, + &ddc->edid_buf[DDC_EDID20_VENDORID_SIGNATURE_LEN], + DDC_EDID20_VENDORID_SIGNATURE_LEN)) + return EDID_READ_RESULT_EDID_MISMATCH; + } + return EDID_READ_RESULT_EDID_MATCH; +} + +static enum edid_read_result check_edid_the_same(struct ddc_service *ddc) +{ + enum edid_read_result ret = EDID_READ_RESULT_EDID_MISMATCH; + + if (ddc->edid_buf_len > DDC_EDID_20_SIGNATURE_OFFSET) { + if (ddc->edid_buf[DDC_EDID_20_SIGNATURE_OFFSET] == + DDC_EDID_20_SIGNATURE) + ret = verify_edid_20_signature(ddc); + else + ret = verify_edid_1x_signature(ddc); + } + + return ret; +} + +static uint8_t aux_read_edid_block( + struct ddc_service *ddc, + uint8_t address, + uint8_t index, + uint8_t *buf) +{ + struct aux_command cmd = { + .payloads = NULL, + .number_of_payloads = 0, + .defer_delay = get_defer_delay(ddc), + .max_defer_write_retry = 0 }; + + uint8_t retrieved = 0; + uint8_t base_offset = + (index % DDC_EDID_BLOCKS_PER_SEGMENT) * DDC_EDID_BLOCK_SIZE; + uint8_t segment = index / DDC_EDID_BLOCKS_PER_SEGMENT; + + for (retrieved = 0; retrieved < DDC_EDID_BLOCK_SIZE; + retrieved += DEFAULT_AUX_MAX_DATA_SIZE) { + + uint8_t offset = base_offset + retrieved; + + struct aux_payload payloads[3] = { + { + .i2c_over_aux = true, + .write = true, + .address = DDC_EDID_SEGMENT_ADDRESS, + .length = 1, + .data = &segment }, + { + .i2c_over_aux = true, + .write = true, + .address = address, + .length = 1, + .data = &offset }, + { + .i2c_over_aux = true, + .write = false, + .address = address, + .length = DEFAULT_AUX_MAX_DATA_SIZE, + .data = &buf[retrieved] } }; + + if (segment == 0) { + cmd.payloads = &payloads[1]; + cmd.number_of_payloads = 2; + } else { + cmd.payloads = payloads; + cmd.number_of_payloads = 3; + } + + if (!dal_i2caux_submit_aux_command( + dal_adapter_service_get_i2caux(ddc->as), + ddc->ddc_pin, + &cmd)) + /* cannot read, break*/ + break; + } + + /* Reset segment to 0. Needed by some panels */ + if (0 != segment) { + struct aux_payload payloads[1] = { { + .i2c_over_aux = true, + .write = true, + .address = DDC_EDID_SEGMENT_ADDRESS, + .length = 1, + .data = &segment } }; + bool result = false; + + segment = 0; + + cmd.number_of_payloads = ARRAY_SIZE(payloads); + cmd.payloads = payloads; + + result = dal_i2caux_submit_aux_command( + dal_adapter_service_get_i2caux(ddc->as), + ddc->ddc_pin, + &cmd); + + if (false == result) + dal_logger_write( + ddc->ctx->logger, + LOG_MAJOR_ERROR, + LOG_MINOR_COMPONENT_DISPLAY_CAPABILITY_SERVICE, + "%s: Writing of EDID Segment (0x30) failed!\n", + __func__); + } + + return retrieved; +} + +static uint8_t i2c_read_edid_block( + struct ddc_service *ddc, + uint8_t address, + uint8_t index, + uint8_t *buf) +{ + bool ret = false; + uint8_t offset = (index % DDC_EDID_BLOCKS_PER_SEGMENT) * + DDC_EDID_BLOCK_SIZE; + uint8_t segment = index / DDC_EDID_BLOCKS_PER_SEGMENT; + + struct i2c_command cmd = { + .payloads = NULL, + .number_of_payloads = 0, + .engine = DDC_I2C_COMMAND_ENGINE, + .speed = dal_adapter_service_get_sw_i2c_speed(ddc->as) }; + + struct i2c_payload payloads[3] = { + { + .write = true, + .address = DDC_EDID_SEGMENT_ADDRESS, + .length = 1, + .data = &segment }, + { + .write = true, + .address = address, + .length = 1, + .data = &offset }, + { + .write = false, + .address = address, + .length = DDC_EDID_BLOCK_SIZE, + .data = buf } }; +/* + * Some I2C engines don't handle stop/start between write-offset and read-data + * commands properly. For those displays, we have to force the newer E-DDC + * behavior of repeated-start which can be enabled by runtime parameter. */ +/* Originally implemented for OnLive using NXP receiver chip */ + + if (index == 0 && !ddc->flags.FORCE_READ_REPEATED_START) { + /* base block, use use DDC2B, submit as 2 commands */ + cmd.payloads = &payloads[1]; + cmd.number_of_payloads = 1; + + if (dal_i2caux_submit_i2c_command( + dal_adapter_service_get_i2caux(ddc->as), + ddc->ddc_pin, + &cmd)) { + + cmd.payloads = &payloads[2]; + cmd.number_of_payloads = 1; + + ret = dal_i2caux_submit_i2c_command( + dal_adapter_service_get_i2caux(ddc->as), + ddc->ddc_pin, + &cmd); + } + + } else { + /* + * extension block use E-DDC, submit as 1 command + * or if repeated-start is forced by runtime parameter + */ + if (segment != 0) { + /* include segment offset in command*/ + cmd.payloads = payloads; + cmd.number_of_payloads = 3; + } else { + /* we are reading first segment, + * segment offset is not required */ + cmd.payloads = &payloads[1]; + cmd.number_of_payloads = 2; + } + + ret = dal_i2caux_submit_i2c_command( + dal_adapter_service_get_i2caux(ddc->as), + ddc->ddc_pin, + &cmd); + } + + return ret ? DDC_EDID_BLOCK_SIZE : 0; +} + +static uint32_t query_edid_block( + struct ddc_service *ddc, + uint8_t address, + uint8_t index, + uint8_t *buf, + uint32_t size) +{ + uint32_t size_retrieved = 0; + + if (size < DDC_EDID_BLOCK_SIZE) + return 0; + + if (dal_ddc_service_is_in_aux_transaction_mode(ddc)) { + + ASSERT(index < 2); + size_retrieved = + aux_read_edid_block(ddc, address, index, buf); + } else { + size_retrieved = + i2c_read_edid_block(ddc, address, index, buf); + } + + return size_retrieved; +} + +#define DDC_DPCD_EDID_CHECKSUM_WRITE_ADDRESS 0x261 +#define DDC_TEST_ACK_ADDRESS 0x260 +#define DDC_DPCD_EDID_TEST_ACK 0x04 +#define DDC_DPCD_EDID_TEST_MASK 0x04 +#define DDC_DPCD_TEST_REQUEST_ADDRESS 0x218 + +static void write_dp_edid_checksum( + struct ddc_service *ddc, + uint8_t checksum) +{ + uint8_t dpcd_data; + + dal_ddc_service_read_dpcd_data( + ddc, + DDC_DPCD_TEST_REQUEST_ADDRESS, + &dpcd_data, + 1); + + if (dpcd_data & DDC_DPCD_EDID_TEST_MASK) { + + dal_ddc_service_write_dpcd_data( + ddc, + DDC_DPCD_EDID_CHECKSUM_WRITE_ADDRESS, + &checksum, + 1); + + dpcd_data = DDC_DPCD_EDID_TEST_ACK; + + dal_ddc_service_write_dpcd_data( + ddc, + DDC_TEST_ACK_ADDRESS, + &dpcd_data, + 1); + } +} + +static void edid_query(struct ddc_service *ddc) +{ + uint32_t bytes_read = 0; + uint32_t ext_cnt = 0; + + uint8_t address; + uint32_t i; + + for (address = DDC_EDID_ADDRESS_START; + address <= DDC_EDID_ADDRESS_END; ++address) { + + bytes_read = query_edid_block( + ddc, + address, + 0, + ddc->edid_buf, + sizeof(ddc->edid_buf) - bytes_read); + + if (bytes_read != DDC_EDID_BLOCK_SIZE) + continue; + + /* get the number of ext blocks*/ + ext_cnt = ddc->edid_buf[DDC_EDID_EXT_COUNT_OFFSET]; + + /* EDID 2.0, need to read 1 more block because EDID2.0 is + * 256 byte in size*/ + if (ddc->edid_buf[DDC_EDID_20_SIGNATURE_OFFSET] == + DDC_EDID_20_SIGNATURE) + ext_cnt = 1; + + for (i = 0; i < ext_cnt; i++) { + /* read additional ext blocks accordingly */ + bytes_read += query_edid_block( + ddc, + address, + i+1, + &ddc->edid_buf[bytes_read], + sizeof(ddc->edid_buf) - bytes_read); + } + + /*this is special code path for DP compliance*/ + if (DDC_TRANSACTION_TYPE_I2C_OVER_AUX == ddc->transaction_type) + write_dp_edid_checksum( + ddc, + ddc->edid_buf[(ext_cnt * DDC_EDID_BLOCK_SIZE) + + DDC_EDID1X_CHECKSUM_OFFSET]); + + /*remembers the address where we fetch the EDID from + * for later signature check use */ + ddc->address = address; + + break;/* already read edid, done*/ + } + + ddc->edid_buf_len = bytes_read; +} + +void dal_ddc_service_optimized_edid_query(struct ddc_service *ddc) +{ + enum edid_read_result error = EDID_READ_RESULT_EDID_MISMATCH; + + if (dal_adapter_service_is_feature_supported( + FEATURE_EDID_STRESS_READ)) + /* different EDIDs, we need to query the new one */ + error = EDID_READ_RESULT_EDID_MISMATCH; + else if (ddc->flags.IS_INTERNAL_DISPLAY && + ddc->flags.EDID_QUERY_DONE_ONCE) + /* InternalDisplay includes LVDS, eDP and ALL-In-One + * system's display-video bios report deviceTag as Internal).*/ + return; + else { + error = check_edid_the_same(ddc); + /* signature check result shows we have the same EDID + connected, we can skip retrieving the full EDID */ + if (error == EDID_READ_RESULT_EDID_MATCH) + return; + } + + if (error == EDID_READ_RESULT_CHECKSUM_READ_ERR + || error == EDID_READ_RESULT_VENDOR_READ_ERR) { + dal_memset(ddc->edid_buf, 0, sizeof(ddc->edid_buf)); + ddc->edid_buf_len = 0; + dal_logger_write(ddc->ctx->logger, + LOG_MAJOR_DCS, + LOG_MINOR_DCS_EDID_EMULATOR, + "EDID read error: %i. Skipping EDID query.\n", error); + } else { + /* retireve EDID*/ + edid_query(ddc); + ddc->flags.EDID_QUERY_DONE_ONCE = true; + } +} + +uint32_t dal_ddc_service_get_edid_buf_len(struct ddc_service *ddc) +{ + return ddc->edid_buf_len; +} + +const uint8_t *dal_ddc_service_get_edid_buf(struct ddc_service *ddc) +{ + return ddc->edid_buf; +} + +bool dal_ddc_service_i2c_query_dp_dual_mode_adaptor( + struct ddc_service *ddc, + struct display_sink_capability *sink_cap) +{ + enum display_dongle_type dummy_dongle; + /* We allow passing NULL pointer when caller only wants + * to know dongle presence*/ + enum display_dongle_type *dongle = + (sink_cap ? &sink_cap->dongle_type : &dummy_dongle); + uint8_t type2_dongle_buf[DP_ADAPTOR_TYPE2_SIZE]; + bool is_type2_dongle = false; + bool is_valid_hdmi_signature = false; + struct dp_hdmi_dongle_signature_data *dongle_signature; + uint32_t i; + + /* Assume we have valid DP-HDMI dongle connected */ + *dongle = DISPLAY_DONGLE_DP_HDMI_DONGLE; + + /* Read DP-HDMI dongle I2c */ + if (!i2c_read( + ddc, + DP_HDMI_DONGLE_ADDRESS, + type2_dongle_buf, + sizeof(type2_dongle_buf))) { + *dongle = DISPLAY_DONGLE_DP_DVI_DONGLE; + return false; + } + + /* Check if Type 2 dongle.*/ + /*If It is a Type 2 dongle, but we don't know yet + * is it DP-HDMI or DP-DVI */ + if (type2_dongle_buf[DP_ADAPTOR_TYPE2_REG_ID] == DP_ADAPTOR_TYPE2_ID) + is_type2_dongle = true; + + dongle_signature = + (struct dp_hdmi_dongle_signature_data *)type2_dongle_buf; + + /* Check EOT */ + if (dongle_signature->eot != DP_HDMI_DONGLE_SIGNATURE_EOT) { + if (is_type2_dongle == false) { + + dal_logger_write(ddc->ctx->logger, + LOG_MAJOR_DCS, + LOG_MINOR_DCS_DONGLE_DETECTION, + "Detected Type 1 DP-HDMI dongle (no valid HDMI signature EOT).\n"); + + *dongle = DISPLAY_DONGLE_DP_HDMI_MISMATCHED_DONGLE; + return true; + } + } + + /* Assume there is a valid HDMI signature, if this is not the case, + * set this flag to false. */ + is_valid_hdmi_signature = true; + + /* Check signature */ + for (i = 0; i < sizeof(dongle_signature->id); ++i) { + /* If its not the right signature, + * skip mismatch in subversion byte.*/ + if (dongle_signature->id[i] != + dp_hdmi_dongle_signature_str[i] && i != 3) { + + if (is_type2_dongle) { + is_valid_hdmi_signature = false; + break; + } + + dal_logger_write(ddc->ctx->logger, + LOG_MAJOR_DCS, + LOG_MINOR_DCS_DONGLE_DETECTION, + "Detected Type 1 DP-HDMI dongle (no valid HDMI signature EOT).\n"); + + *dongle = DISPLAY_DONGLE_DP_HDMI_MISMATCHED_DONGLE; + return true; + + } + } + + if (sink_cap && is_type2_dongle) { + + uint32_t max_tmds_clk = + type2_dongle_buf[DP_ADAPTOR_TYPE2_REG_MAX_TMDS_CLK]; + + max_tmds_clk = max_tmds_clk * 2 + max_tmds_clk / 2; + + if (max_tmds_clk < DP_ADAPTOR_TYPE2_MAX_TMDS_CLK && + max_tmds_clk > DP_ADAPTOR_TYPE2_MIN_TMDS_CLK) { + + if (is_valid_hdmi_signature == true) + *dongle = DISPLAY_DONGLE_DP_HDMI_DONGLE; + else + *dongle = DISPLAY_DONGLE_DP_DVI_DONGLE; + + /* Multiply by 1000 to convert to kHz. */ + sink_cap->max_hdmi_pixel_clock = + max_tmds_clk * 1000; + } else + dal_logger_write(ddc->ctx->logger, + LOG_MAJOR_DCS, + LOG_MINOR_DCS_DONGLE_DETECTION, + "Invalid Maximum TMDS clock"); + + } + + if (is_type2_dongle == false && is_valid_hdmi_signature == true) + dal_logger_write(ddc->ctx->logger, + LOG_MAJOR_DCS, + LOG_MINOR_DCS_DONGLE_DETECTION, + "Detected Type 1 DP-HDMI dongle.\n"); + + return true; +} + +void dal_ddc_service_retrieve_dpcd_data( + struct ddc_service *ddc, + struct av_sync_data *av_sync_data) +{ + const uint32_t size = DPCD_ADDRESS_AUDIO_DELAY_INSERT3 - + DPCD_ADDRESS_AV_GRANULARITY + 1; + uint8_t av_sync_caps[size]; + + if (ddc->dp_receiver_id_info.dpcd_rev < DCS_DPCD_REV_12) + return; + + dal_ddc_service_read_dpcd_data( + ddc, DPCD_ADDRESS_AV_GRANULARITY, av_sync_caps, size); + + av_sync_data->av_granularity = av_sync_caps[0]; + av_sync_data->aud_dec_lat1 = av_sync_caps[1]; + av_sync_data->aud_dec_lat2 = av_sync_caps[2]; + av_sync_data->aud_pp_lat1 = av_sync_caps[3]; + av_sync_data->aud_pp_lat2 = av_sync_caps[4]; + av_sync_data->vid_inter_lat = av_sync_caps[5]; + av_sync_data->vid_prog_lat = av_sync_caps[6]; + av_sync_data->aud_del_ins1 = av_sync_caps[8]; + av_sync_data->aud_del_ins2 = av_sync_caps[9]; + av_sync_data->aud_del_ins3 = av_sync_caps[10]; +} + +static void get_active_converter_info( + struct ddc_service *ddc, + uint8_t data, + struct display_sink_capability *sink_cap) +{ + union dp_downstream_port_present ds_port = { .byte = data }; + + /* decode converter info*/ + if (!ds_port.fields.PORT_PRESENT) { + + ddc->dongle_type = DISPLAY_DONGLE_NONE; + return; + } + switch (ds_port.fields.PORT_TYPE) { + case DOWNSTREAM_VGA: + sink_cap->dongle_type = DISPLAY_DONGLE_DP_VGA_CONVERTER; + break; + case DOWNSTREAM_DVI_HDMI: + /* At this point we don't know is it DVI or HDMI, + * assume DVI.*/ + sink_cap->dongle_type = DISPLAY_DONGLE_DP_DVI_CONVERTER; + break; + default: + sink_cap->dongle_type = DISPLAY_DONGLE_NONE; + break; + } + + if (sink_cap->dpcd_revision >= DCS_DPCD_REV_11) { + uint8_t det_caps[4]; + union dwnstream_port_caps_byte0 *port_caps = + (union dwnstream_port_caps_byte0 *)det_caps; + dal_ddc_service_read_dpcd_data(ddc, + DPCD_ADDRESS_DWN_STRM_PORT0_CAPS, det_caps, + sizeof(det_caps)); + + switch (port_caps->bits.DWN_STRM_PORTX_TYPE) { + case DOWN_STREAM_DETAILED_VGA: + sink_cap->dongle_type = + DISPLAY_DONGLE_DP_VGA_CONVERTER; + break; + case DOWN_STREAM_DETAILED_DVI: + sink_cap->dongle_type = + DISPLAY_DONGLE_DP_DVI_CONVERTER; + break; + case DOWN_STREAM_DETAILED_HDMI: + sink_cap->dongle_type = + DISPLAY_DONGLE_DP_HDMI_CONVERTER; + + if (ds_port.fields.DETAILED_CAPS) { + + union dwnstream_port_caps_byte3_hdmi + hdmi_caps = {.raw = det_caps[3] }; + + sink_cap->is_dp_hdmi_s3d_converter = + hdmi_caps.bits.FRAME_SEQ_TO_FRAME_PACK; + } + break; + } + } + ddc->dongle_type = sink_cap->dongle_type; +} + + +enum { + DP_SINK_CAP_SIZE = + DPCD_ADDRESS_EDP_CONFIG_CAP - DPCD_ADDRESS_DPCD_REV + 1 +}; + +bool dal_ddc_service_aux_query_dp_sink_capability( + struct ddc_service *ddc, + struct display_sink_capability *sink_cap) +{ + /* We allow passing NULL pointer when caller only wants + * to know converter presence */ + struct display_sink_capability dummy_sink_cap; + union dp_downstream_port_present ds_port = { 0 }; + struct dp_device_vendor_id dp_id; + struct dp_sink_hw_fw_revision revision; + uint8_t buf_caps[DP_SINK_CAP_SIZE] = { 0 }; + uint8_t dp_data; + + if (!sink_cap) + sink_cap = &dummy_sink_cap; + + /* Some receiver needs 5ms to power up, not DP compliant*/ + if (ddc->wa.bits.DP_SKIP_POWER_OFF) { + /* DP receiver has probably powered down AUX communication + when powering down data link (not-compliant), power it up*/ + uint32_t retry = 0; + uint8_t power_state = 1; + + /* up to 4 tries to let DP receiver wake up*/ + while (DDC_RESULT_SUCESSFULL != + dal_ddc_service_write_dpcd_data( + ddc, + DPCD_ADDRESS_POWER_STATE, + &power_state, + sizeof(power_state)) && retry++ < 4) + ; + } + + /* Some mDP->VGA dongle needs 500ms to power up from S4 */ + if (ddc->wa.bits.DP_AUX_POWER_UP_WA_DELAY) + dal_sleep_in_milliseconds(AUX_POWER_UP_WA_DELAY); + + if (DDC_RESULT_SUCESSFULL != + dal_ddc_service_read_dpcd_data( + ddc, DPCD_ADDRESS_DPCD_REV, buf_caps, DP_SINK_CAP_SIZE)) + return false; + + /*This for DP1.1 CTS core1.2 test. TE will check if Source read 0x200. + Current AMD implementation reads EDID to decide if display connected + to active dongle. Value of 0x200 is not really used by AMD driver.*/ + + dal_ddc_service_read_dpcd_data( + ddc, DPCD_ADDRESS_SINK_COUNT, &dp_data, sizeof(dp_data)); + + sink_cap->downstrm_sink_count = dp_data; + + /* parse caps*/ + /* TODO: for DP1.2, SW driver may need parser number of downstream + * port DPCD offset 0x7 DOWN_STREAM_PORT_COUNT */ + sink_cap->dpcd_revision = buf_caps[DPCD_ADDRESS_DPCD_REV]; + sink_cap->dp_link_rate = buf_caps[DPCD_ADDRESS_MAX_LINK_RATE]; + sink_cap->dp_link_lane_count = + buf_caps[DPCD_ADDRESS_MAX_LANE_COUNT] & 0x1F; + sink_cap->ss_supported = + buf_caps[DPCD_ADDRESS_MAX_DOWNSPREAD] & 0x01 ? + LINK_SPREAD_05_DOWNSPREAD_30KHZ : LINK_SPREAD_DISABLED; + + ds_port.byte = buf_caps[DPCD_ADDRESS_DOWNSTREAM_PORT_PRESENT]; + + /* interpret converter (downstream port) caps*/ + get_active_converter_info(ddc, ds_port.byte, sink_cap); + + /* get active dongles (converters) id for specific workarounds*/ + + /* initialize m_dpRecevierInfo to zero */ + dal_memset( + &ddc->dp_receiver_id_info, 0, sizeof(ddc->dp_receiver_id_info)); + + ddc->dp_receiver_id_info.dpcd_rev = sink_cap->dpcd_revision; + + ddc->dp_receiver_id_info.dongle_type = sink_cap->dongle_type; + + /* read IEEE sink device id */ + dal_ddc_service_read_dpcd_data( + ddc, + DPCD_ADDRESS_SINK_DEVICE_ID_START, + (uint8_t *)&dp_id, + sizeof(dp_id)); + + /* cache the sink_id */ + ddc->dp_receiver_id_info.sink_id = + (dp_id.ieee_oui[0] << 16) + + (dp_id.ieee_oui[1] << 8) + + dp_id.ieee_oui[2]; + + /* cache the sink_dev_name */ + dal_memmove( + ddc->dp_receiver_id_info.sink_id_str, + dp_id.ieee_device_id, + sizeof(ddc->dp_receiver_id_info.sink_id_str)); + + /* read IEEE sink hw/fw revision*/ + dal_ddc_service_read_dpcd_data( + ddc, + DPCD_ADDRESS_SINK_REVISION_START, + (uint8_t *)&revision, + sizeof(revision)); + + /* cache the sink hw revision */ + ddc->dp_receiver_id_info.sink_hw_revision = revision.ieee_hw_rev; + + /* cache the sink fw revision */ + dal_memmove( + ddc->dp_receiver_id_info.sink_fw_revision, + revision.ieee_fw_rev, + sizeof(ddc->dp_receiver_id_info.sink_fw_revision)); + + /* read IEEE branch device id */ + dal_ddc_service_read_dpcd_data( + ddc, + DPCD_ADDRESS_BRANCH_DEVICE_ID_START, + (uint8_t *)&dp_id, + sizeof(dp_id)); + + /* extract IEEE id + cache the branchDevId and branchDevName*/ + ddc->dp_receiver_id_info.branch_id = + (dp_id.ieee_oui[0] << 16) + + (dp_id.ieee_oui[1] << 8) + + dp_id.ieee_oui[2]; + + dal_memmove( + ddc->dp_receiver_id_info.branch_name, + dp_id.ieee_device_id, + sizeof(ddc->dp_receiver_id_info.branch_name)); + + switch (ddc->dp_receiver_id_info.branch_id) { + case DP_BRANCH_DEVICE_ID_5: + sink_cap->downstrm_sink_count_valid = true; + break; + default: + break; + } + + /* translate receiver capabilities*/ + if (sink_cap->dp_link_spead != LINK_SPREAD_DISABLED) + sink_cap->ss_supported = true; + + return true; +} + +bool dal_ddc_service_query_ddc_data( + struct ddc_service *ddc, + uint32_t address, + uint8_t *write_buf, + uint32_t write_size, + uint8_t *read_buf, + uint32_t read_size) +{ + bool ret; + uint32_t payload_size = + dal_ddc_service_is_in_aux_transaction_mode(ddc) ? + DEFAULT_AUX_MAX_DATA_SIZE : EDID_SEGMENT_SIZE; + + uint32_t write_payloads = + (write_size + payload_size - 1) / payload_size; + + uint32_t read_payloads = + (read_size + payload_size - 1) / payload_size; + + uint32_t payloads_num = write_payloads + read_payloads; + + if (write_size > EDID_SEGMENT_SIZE || read_size > EDID_SEGMENT_SIZE) + 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_payloads *payloads = + dal_ddc_aux_payloads_create(payloads_num); + + struct aux_command command = { + .payloads = dal_ddc_aux_payloads_get(payloads), + .number_of_payloads = 0, + .defer_delay = get_defer_delay(ddc), + .max_defer_write_retry = 0 }; + + dal_ddc_aux_payloads_add( + payloads, address, write_size, write_buf, true); + + dal_ddc_aux_payloads_add( + payloads, address, read_size, read_buf, false); + + command.number_of_payloads = + dal_ddc_aux_payloads_get_count(payloads); + + ret = dal_i2caux_submit_aux_command( + dal_adapter_service_get_i2caux(ddc->as), + ddc->ddc_pin, + &command); + + dal_ddc_aux_payloads_destroy(&payloads); + + } else { + struct i2c_payloads *payloads = + dal_ddc_i2c_payloads_create(payloads_num); + + struct i2c_command command = { + .payloads = dal_ddc_i2c_payloads_get(payloads), + .number_of_payloads = 0, + .engine = DDC_I2C_COMMAND_ENGINE, + .speed = + dal_adapter_service_get_sw_i2c_speed(ddc->as) }; + + dal_ddc_i2c_payloads_add( + payloads, address, write_size, write_buf, true); + + dal_ddc_i2c_payloads_add( + payloads, address, read_size, read_buf, false); + + command.number_of_payloads = + dal_ddc_i2c_payloads_get_count(payloads); + + ret = dal_i2caux_submit_i2c_command( + dal_adapter_service_get_i2caux(ddc->as), + ddc->ddc_pin, + &command); + + dal_ddc_i2c_payloads_destroy(&payloads); + } + + return ret; +} + +bool dal_ddc_service_get_dp_receiver_id_info( + struct ddc_service *ddc, + struct dp_receiver_id_info *info) +{ + if (!info) + return false; + + *info = ddc->dp_receiver_id_info; + return true; +} + +enum ddc_result dal_ddc_service_read_dpcd_data( + struct ddc_service *ddc, + uint32_t address, + uint8_t *data, + uint32_t len) +{ + struct aux_payload read_payload = { + .i2c_over_aux = false, + .write = false, + .address = address, + .length = len, + .data = data, + }; + struct aux_command command = { + .payloads = &read_payload, + .number_of_payloads = 1, + .defer_delay = 0, + .max_defer_write_retry = 0, + }; + + if (len > DEFAULT_AUX_MAX_DATA_SIZE) { + BREAK_TO_DEBUGGER(); + return DDC_RESULT_FAILED_INVALID_OPERATION; + } + + if (dal_i2caux_submit_aux_command( + dal_adapter_service_get_i2caux(ddc->as), + ddc->ddc_pin, + &command)) + return DDC_RESULT_SUCESSFULL; + + return DDC_RESULT_FAILED_OPERATION; +} + +enum ddc_result dal_ddc_service_write_dpcd_data( + struct ddc_service *ddc, + uint32_t address, + const uint8_t *data, + uint32_t len) +{ + struct aux_payload write_payload = { + .i2c_over_aux = false, + .write = true, + .address = address, + .length = len, + .data = (uint8_t *)data, + }; + struct aux_command command = { + .payloads = &write_payload, + .number_of_payloads = 1, + .defer_delay = 0, + .max_defer_write_retry = 0, + }; + + if (len > DEFAULT_AUX_MAX_DATA_SIZE) { + BREAK_TO_DEBUGGER(); + return DDC_RESULT_FAILED_INVALID_OPERATION; + } + + if (dal_i2caux_submit_aux_command( + dal_adapter_service_get_i2caux(ddc->as), + ddc->ddc_pin, + &command)) + return DDC_RESULT_SUCESSFULL; + + return DDC_RESULT_FAILED_OPERATION; +} + +/*test only function*/ +void dal_ddc_service_set_ddc_pin( + struct ddc_service *ddc_service, + struct ddc *ddc) +{ + ddc_service->ddc_pin = ddc; +} + +struct ddc *dal_ddc_service_get_ddc_pin(struct ddc_service *ddc_service) +{ + return ddc_service->ddc_pin; +} + + +void dal_ddc_service_reset_dp_receiver_id_info(struct ddc_service *ddc_service) +{ + dal_memset(&ddc_service->dp_receiver_id_info, + 0, sizeof(struct dp_receiver_id_info)); +} diff --git a/drivers/gpu/drm/amd/dal/dcs/ddc_service.h b/drivers/gpu/drm/amd/dal/dcs/ddc_service.h new file mode 100644 index 000000000000..e5217b72c241 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/ddc_service.h @@ -0,0 +1,38 @@ +/* + * Copyright 2012-15 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 + * + */ + +#ifndef __DAL_DDC_SERVICE_H__ +#define __DAL_DDC_SERVICE_H__ + +#include "include/ddc_service_types.h" + +void dal_ddc_service_set_ddc_pin( + struct ddc_service *ddc_service, + struct ddc *ddc); + +struct ddc *dal_ddc_service_get_ddc_pin(struct ddc_service *ddc_service); +void dal_ddc_service_reset_dp_receiver_id_info(struct ddc_service *ddc_service); + +#endif /* __DAL_DDC_SERVICE_H__ */ diff --git a/drivers/gpu/drm/amd/dal/dcs/default_modes_dco.c b/drivers/gpu/drm/amd/dal/dcs/default_modes_dco.c new file mode 100644 index 000000000000..dc02debb3e7d --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/default_modes_dco.c @@ -0,0 +1,181 @@ +/* + * Copyright 2012-15 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 + * + */ + +#include "dal_services.h" + +#include "default_modes_dco.h" +#include "include/dcs_interface.h" +#include "include/default_mode_list_interface.h" +#include "include/timing_service_interface.h" +#include "include/timing_service_types.h" + +static struct mode_info digital_default_modes[] = { + { 640, 480, 60, TIMING_STANDARD_DMT, TIMING_SOURCE_DEFAULT, + { 0, 0, 0, 0, 1, 0 } }, +}; + +static struct mode_info wireless_default_modes[] = { + { 640, 480, 60, TIMING_STANDARD_CEA861, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0 } }, + { 640, 480, 60, TIMING_STANDARD_CEA861, TIMING_SOURCE_DEFAULT, + { 0, 0, 0, 0, 1, 0 } }, + { 720, 480, 60, TIMING_STANDARD_CEA861, TIMING_SOURCE_DEFAULT, + { 0, 0, 0, 0, 0, 0 } }, + { 720, 480, 60, TIMING_STANDARD_CEA861, TIMING_SOURCE_DEFAULT, + { 0, 0, 0, 0, 1, 0 } }, + { 720, 576, 50, TIMING_STANDARD_CEA861, TIMING_SOURCE_DEFAULT, + { 0, 0, 0, 0, 0, 0 } }, + { 1280, 720, 50, TIMING_STANDARD_CEA861, TIMING_SOURCE_DEFAULT, + { 0, 0, 0, 0, 0, 0 } }, + { 1280, 720, 60, TIMING_STANDARD_CEA861, TIMING_SOURCE_DEFAULT, + { 0, 0, 0, 0, 0, 0 } }, + { 1280, 720, 60, TIMING_STANDARD_CEA861, TIMING_SOURCE_DEFAULT, + { 0, 0, 0, 0, 1, 0 } }, + { 1920, 1080, 24, TIMING_STANDARD_CEA861, TIMING_SOURCE_DEFAULT, + { 0, 0, 0, 0, 0, 0 } }, + { 1920, 1080, 24, TIMING_STANDARD_CEA861, TIMING_SOURCE_DEFAULT, + { 0, 0, 0, 0, 1, 0 } }, + { 1920, 1080, 50, TIMING_STANDARD_CEA861, TIMING_SOURCE_DEFAULT, + { 0, 0, 0, 0, 0, 0 } }, + { 1920, 1080, 60, TIMING_STANDARD_CEA861, TIMING_SOURCE_DEFAULT, + { 0, 0, 0, 0, 0, 0 } }, + { 1920, 1080, 60, TIMING_STANDARD_CEA861, TIMING_SOURCE_DEFAULT, + { 0, 0, 0, 0, 1, 0 } }, +}; + +static bool add_default_timing( + struct mode_info mode_info[], + uint32_t len, + struct dcs_mode_timing_list *list, + struct timing_service *ts) +{ + bool ret = true; + uint32_t i; + struct mode_timing mode_timing; + + for (i = 0; i < len; ++i) { + + /* Query the timing for a given mode */ + if (!dal_timing_service_get_timing_for_mode( + ts, &mode_info[i], &mode_timing.crtc_timing)) { + + ret = false; + break; + } + + /* copy the mode info to complete the ModeTiming*/ + mode_timing.mode_info = mode_info[i]; + + /* append the mode to the supplied list */ + if (!dal_dcs_mode_timing_list_append(list, &mode_timing)) { + + ret = false; + break; + } + + ret = true; + } + + return ret; +} + + +bool dal_default_modes_dco_add_mode_timing( + struct dcs_mode_timing_list *list, + struct timing_service *ts) +{ + return add_default_timing( + digital_default_modes, + ARRAY_SIZE(digital_default_modes), + list, + ts); +} + +bool dal_default_modes_dco_multi_sync_dco_add_mode_timing( + struct dcs_mode_timing_list *list, + struct timing_service *ts) +{ + bool ret = true; + const struct default_mode_list *mode_list = + dal_timing_service_get_default_mode_list(ts); + uint32_t size = dal_default_mode_list_get_count(mode_list); + uint32_t i; + struct mode_timing mode_timing; + + for (i = 0; i < size; ++i) { + /* copy the mode info to the ModeTiming structure*/ + mode_timing.mode_info = + *dal_default_mode_list_get_mode_info_at_index( + mode_list, i); + mode_timing.mode_info.timing_source = TIMING_SOURCE_DEFAULT; + + /* skip all modes larger than 1600x1200 + * and those which are not 60Hz*/ + if ((mode_timing.mode_info.pixel_width > 1600) || + (mode_timing.mode_info.pixel_height > 1200) || + (mode_timing.mode_info.field_rate != 60) || + (mode_timing.mode_info.flags.INTERLACE)) + continue; + + /* set default timing standard as GTF.*/ + /* default modes getting from "DALNonStandardModesBCD" + runtime parameters should use GTF for Non-EDID monitor.*/ + if (mode_timing.mode_info.timing_standard == + TIMING_STANDARD_UNDEFINED) + mode_timing.mode_info.timing_standard = + TIMING_STANDARD_GTF; + + /* query the timing for the mode*/ + if (!dal_timing_service_get_timing_for_mode( + ts, &mode_timing.mode_info, &mode_timing.crtc_timing)) { + + ret = false; + break; + }; + + /*set preferred mode before inserting to list*/ + /*m_preferredNonDdcMode used for FID#12086 - + * see constructor for more detail*/ + + /* append the mode to the supplied list*/ + if (!dal_dcs_mode_timing_list_append(list, &mode_timing)) { + ret = false; + break; + } + + } + return ret; +} + +bool dal_default_wireless_dco_add_mode_timing( + struct dcs_mode_timing_list *list, + struct timing_service *ts) +{ + return add_default_timing( + wireless_default_modes, + ARRAY_SIZE(wireless_default_modes), + list, + ts); +} diff --git a/drivers/gpu/drm/amd/dal/dcs/default_modes_dco.h b/drivers/gpu/drm/amd/dal/dcs/default_modes_dco.h new file mode 100644 index 000000000000..e895b4c5c7d9 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/default_modes_dco.h @@ -0,0 +1,44 @@ +/* + * Copyright 2012-15 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 + * + */ + +#ifndef __DEFAULT_MODES_DCO_H__ +#define __DEFAULT_MODES_DCO_H__ + +struct dcs_mode_timing_list; +struct timing_service; + +bool dal_default_modes_dco_add_mode_timing( + struct dcs_mode_timing_list *list, + struct timing_service *ts); + +bool dal_default_modes_dco_multi_sync_dco_add_mode_timing( + struct dcs_mode_timing_list *list, + struct timing_service *ts); + +bool dal_default_wireless_dco_add_mode_timing( + struct dcs_mode_timing_list *list, + struct timing_service *ts); + +#endif /* __DEFAULT_MODES_DCO_H__ */ diff --git a/drivers/gpu/drm/amd/dal/dcs/edid/edid.c b/drivers/gpu/drm/amd/dal/dcs/edid/edid.c new file mode 100644 index 000000000000..febe33a23ed7 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/edid/edid.c @@ -0,0 +1,66 @@ +/* + * Copyright 2012-15 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 + * + */ + +#include "dal_services.h" + +#include "edid1x_data.h" +#include "edid20.h" +#include "edid.h" + +static uint8_t edid_1x_header[NUM_OF_BYTE_EDID1X_HEADER] = { + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00}; + +bool dal_edid_get_version_raw( + const uint8_t *buff, + uint32_t len, + uint8_t *major, + uint8_t *minor) +{ + const struct edid_data_v1x *data; + + if (!minor || !major) + return false; + + if (len < sizeof(struct edid_data_v1x)) + return false; + + data = (const struct edid_data_v1x *)buff; + if (dal_memcmp( + data->header, + edid_1x_header, + NUM_OF_BYTE_EDID1X_HEADER) == 0) { + *major = data->version[0]; + *minor = data->version[1]; + return true; + } + + if (len < sizeof(struct edid_data_v20)) + return false;/*Edid 2.0 base block is 256 byte in length*/ + + *major = (buff[0] >> 4) & 0xF; + *minor = buff[0] & 0xF; + + return true; +} diff --git a/drivers/gpu/drm/amd/dal/dcs/edid/edid.h b/drivers/gpu/drm/amd/dal/dcs/edid/edid.h new file mode 100644 index 000000000000..4b2c3ba6dd27 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/edid/edid.h @@ -0,0 +1,35 @@ +/* + * Copyright 2012-15 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 + * + */ + +#ifndef __DAL_EDID_H__ +#define __DAL_EDID_H__ + +bool dal_edid_get_version_raw( + const uint8_t *buff, + uint32_t len, + uint8_t *major, + uint8_t *minor); + +#endif diff --git a/drivers/gpu/drm/amd/dal/dcs/edid/edid13.c b/drivers/gpu/drm/amd/dal/dcs/edid/edid13.c new file mode 100644 index 000000000000..46fcdfa09c3c --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/edid/edid13.c @@ -0,0 +1,858 @@ +/* + * Copyright 2012-15 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 + * + */ + +#include "dal_services.h" +#include "include/dcs_interface.h" +#include "edid_base.h" +#include "edid.h" +#include "edid1x_data.h" +#include "edid13.h" + +#define DET_TIMING_SECTION_ELEMENT_SIZE_IN_BYTES 18 + +struct detailed_timing_section { + union { + /*as a detailed timing descriptor*/ + struct edid_detailed detailed_timing_descriptor; + + /* as a monitor descriptor*/ + struct edid_display_descriptor monitor; + + /* as RAW bytes*/ + uint8_t raw[DET_TIMING_SECTION_ELEMENT_SIZE_IN_BYTES]; + } u; +}; + +#define ESTABLISHED_TIMING_I_II_TABLE_SIZE 17 + +static struct edid_established_modes + established_timings[ESTABLISHED_TIMING_I_II_TABLE_SIZE] = { + /* Established Timing I*/ + { 0, 720, 400, 70 }, + { 0, 720, 400, 88 }, + { 0, 640, 480, 60 }, + { 0, 640, 480, 67 }, + { 0, 640, 480, 72 }, + { 0, 640, 480, 75 }, + { 0, 800, 600, 56 }, + { 0, 800, 600, 60 }, + /* Established Timing II*/ + { 0, 800, 600, 72 }, + { 0, 800, 600, 75 }, + { 0, 832, 624, 75 }, + { 0, 1024, 768, 87 },/* 87Hz Interlaced*/ + { 0, 1024, 768, 60 }, + { 0, 1024, 768, 70 }, + { 0, 1024, 768, 75 }, + { 0, 1280, 1024, 75 }, + /* Manufacturer's Timings*/ + { 0, 1152, 872, 75 }, + }; + +#define FROM_EDID(e) container_of((e), struct edid_13, edid) + +bool dal_edid13_is_v_13(uint32_t len, const uint8_t *buff) +{ + uint8_t major; + uint8_t minor; + + if (!dal_edid_get_version_raw(buff, len, &major, &minor)) + return false; + + if (major == 1 && minor < 4) + return true; + + return false; +} + +bool dal_edid13_add_detailed_timings( + struct edid_13 *edid, + struct dcs_mode_timing_list *list, + bool *preferred_mode_found) +{ + uint32_t i = 0; + struct mode_timing mode_timing; + bool ret = false; + + ASSERT(list != NULL); + ASSERT(preferred_mode_found != NULL); + + for (i = 0; i < NUM_OF_EDID1X_DETAILED_TIMING; ++i) { + + const struct edid_display_descriptor *descr = + (struct edid_display_descriptor *) + &edid->data->edid_detailed_timings[i]; + + if (descr->flag == 0) { + + if ((descr->reserved1 != 0) || + (descr->reserved2 != 0)) + edid->edid.error.BAD_DESCRIPTOR_FIELD = true; + + if ((0x11 <= descr->type_tag) && + (descr->type_tag <= 0xf9)) + edid->edid.error.BAD_DESCRIPTOR_FIELD = true; + + continue; + } + + dal_memset(&mode_timing, 0, sizeof(struct mode_timing)); + + if (!dal_edid_detailed_to_timing( + &edid->edid, + &edid->data->edid_detailed_timings[i], + false, + &mode_timing.crtc_timing)) + continue; + + /* update the mode information*/ + dal_edid_timing_to_mode_info( + &mode_timing.crtc_timing, &mode_timing.mode_info); + + mode_timing.mode_info.flags.NATIVE = 1; + + /* If preferred mode yet not found - + select first detailed mode/timing as preferred*/ + if (!(*preferred_mode_found)) { + mode_timing.mode_info.flags.PREFERRED = 1; + *preferred_mode_found = true; + } + + dal_dcs_mode_timing_list_append(list, &mode_timing); + + ret = true; + } + + return ret; +} + +bool dal_edid13_retrieve_standard_mode( + uint8_t edid_minor_version, + const struct standard_timing *std_timing, + struct mode_info *mode_info) +{ + uint32_t h_active; + uint32_t v_active = 0; + uint32_t freq; + + /* reserve, do not use per spec*/ + if (std_timing->h_addressable == 0x00) + return false; + +/* Unused Standard Timing data fields shall be set to 01h, 01h, as per spec*/ + if ((std_timing->h_addressable == 0x01) && + (std_timing->u.s_uchar == 0x01)) + return false; + + freq = std_timing->u.ratio_and_refresh_rate.REFRESH_RATE + 60; + h_active = (std_timing->h_addressable + 31) * 8; + + switch (std_timing->u.ratio_and_refresh_rate.RATIO) { + case RATIO_16_BY_10: + /* as per spec EDID structures prior to version 1, revision 3 + defined the bit (bits 7 & 6 at address 27h) combination of + 0 0 to indicate a 1 : 1 aspect ratio.*/ + if (edid_minor_version < 3) + v_active = h_active; + else + v_active = (h_active * 10) / 16; + break; + case RATIO_4_BY_3: + v_active = (h_active * 3) / 4; + break; + case RATIO_5_BY_4: + v_active = (h_active * 4) / 5; + break; + case RATIO_16_BY_9: + v_active = (h_active * 9) / 16; + break; + } + mode_info->pixel_width = h_active; + mode_info->pixel_height = v_active; + mode_info->field_rate = freq; + mode_info->timing_standard = TIMING_STANDARD_DMT; + mode_info->timing_source = TIMING_SOURCE_EDID_STANDARD; + + return true; +} + +static bool retrieve_standard_mode( + struct edid_13 *edid, + const struct standard_timing *std_timing, + struct mode_info *mode_info) +{ + uint32_t h_active; + uint32_t v_active = 0; + uint32_t freq; + + /* reserve, do not use per spec*/ + if (std_timing->h_addressable == 0x00) + return false; + +/* Unused Standard Timing data fields shall be set to 01h, 01h, as per spec*/ + if ((std_timing->h_addressable == 0x01) && + (std_timing->u.s_uchar == 0x01)) + return false; + + freq = std_timing->u.ratio_and_refresh_rate.REFRESH_RATE + 60; + h_active = (std_timing->h_addressable + 31) * 8; + + switch (std_timing->u.ratio_and_refresh_rate.RATIO) { + case RATIO_16_BY_10: + /* as per spec EDID structures prior to version 1, + revision 3 defined the bit (bits 7 & 6 at address 27h) + combination of 0 0 to indicate a 1 : 1 aspect ratio.*/ + if (edid->data->version[1] < 3) + v_active = h_active; + else + v_active = (h_active * 10) / 16; + break; + case RATIO_4_BY_3: + v_active = (h_active * 3) / 4; + break; + case RATIO_5_BY_4: + v_active = (h_active * 4) / 5; + break; + case RATIO_16_BY_9: + v_active = (h_active * 9) / 16; + break; + } + mode_info->pixel_width = h_active; + mode_info->pixel_height = v_active; + mode_info->field_rate = freq; + mode_info->timing_standard = TIMING_STANDARD_DMT; + mode_info->timing_source = TIMING_SOURCE_EDID_STANDARD; + + return true; +} + +bool dal_edid13_add_standard_timing( + struct edid_13 *edid, + struct dcs_mode_timing_list *list, + bool *preferred_mode_found) +{ + bool ret = false; + uint32_t i, j; + struct mode_timing mode_timing; + + ASSERT(list != NULL); + ASSERT(preferred_mode_found != NULL); + +/*Parse standard timings in standard timing section*/ + for (i = 0; i < NUM_OF_EDID1X_STANDARD_TIMING; i++) { + + const struct standard_timing *std_timing = + (const struct standard_timing *) + &edid->data->standard_timings[i]; + + dal_memset(&mode_timing, 0, sizeof(struct mode_timing)); + + if (!retrieve_standard_mode( + edid, std_timing, &mode_timing.mode_info)) + continue; + + if (!dal_edid_get_timing_for_vesa_mode( + &edid->edid, + &mode_timing.mode_info, + &mode_timing.crtc_timing)) + continue; + + /* If preferred mode yet not found - + * select first standard mode/timing as preferred*/ + if (!(*preferred_mode_found)) { + mode_timing.mode_info.flags.PREFERRED = 1; + *preferred_mode_found = true; + } + + dal_dcs_mode_timing_list_append(list, &mode_timing); + ret = true; + } + + /* Parse standard timings in 18 byte display descriptor*/ + for (i = 0; i < NUM_OF_EDID1X_DETAILED_TIMING; ++i) { + const struct detailed_timing_section *descr = + (const struct detailed_timing_section *) + &edid->data->edid_detailed_timings[i]; + + if ((descr->u.monitor.flag != 0x0000) || + (descr->u.monitor.type_tag != 0xFA)) + continue; + + /* Get the 18 byte display descriptor for standard timings. */ + if ((descr->u.monitor.reserved1 != 0x00) || + (descr->u.monitor.reserved2 != 0x00)) { + + edid->edid.error.BAD_18BYTE_STANDARD_FIELD = true; + BREAK_TO_DEBUGGER(); + } + + for (j = 0; j < MAX_NUM_OF_STD_TIMING_IDS_IN_DET_TIMING_DESC; + j++) { + const struct standard_timing *std_timing = + (const struct standard_timing *) + &descr->u.monitor.u.std_timings.timing[j]; + + dal_memset(&mode_timing, 0, sizeof(struct mode_timing)); + + if (!retrieve_standard_mode( + edid, std_timing, &mode_timing.mode_info)) + continue; + + if (!dal_edid_get_timing_for_vesa_mode( + &edid->edid, + &mode_timing.mode_info, + &mode_timing.crtc_timing)) + continue; + /* If preferred mode yet not found - select first + standard (in detailed section) mode/timing + as preferred*/ + if (!(*preferred_mode_found)) { + mode_timing.mode_info.flags.PREFERRED = 1; + *preferred_mode_found = true; + } + + dal_dcs_mode_timing_list_append(list, &mode_timing); + ret = true; + } + } + + return ret; +} + +#define GET_BIT(arr, bit) (arr[(bit) / 8] & (1 << (7 - ((bit) & 0x07)))) + +bool dal_edid13_add_established_timings( + struct edid_13 *edid, + struct dcs_mode_timing_list *list, + bool *preferred_mode_found) +{ + bool ret = false; + struct mode_timing timing; + uint32_t i; + + uint32_t bit_size = + (ESTABLISHED_TIMING_I_II_TABLE_SIZE < + NUM_OF_EDID1X_ESTABLISHED_TIMING * 8) ? + ESTABLISHED_TIMING_I_II_TABLE_SIZE : + NUM_OF_EDID1X_ESTABLISHED_TIMING * 8; + + ASSERT(list != NULL); + ASSERT(preferred_mode_found != NULL); + + for (i = 0; i < bit_size; ++i) { + + if (!GET_BIT(edid->data->established_timings, i)) + continue; +/* In Timing II, bit 4 indicates 1024X768 87Hz interlaced*/ +/* We don't want to expose this old mode since it causes DTM failures*/ +/* so skip adding it to the TS list*/ + if (established_timings[i].refresh_rate == 87) + continue; + + dal_memset(&timing, 0, sizeof(struct mode_timing)); + timing.mode_info.pixel_width = established_timings[i].h_res; + timing.mode_info.pixel_height = established_timings[i].v_res; + timing.mode_info.field_rate = + established_timings[i].refresh_rate; + timing.mode_info.timing_standard = TIMING_STANDARD_DMT; + timing.mode_info.timing_source = TIMING_SOURCE_EDID_ESTABLISHED; + + + if (!dal_edid_get_timing_for_vesa_mode( + &edid->edid, + &timing.mode_info, + &timing.crtc_timing)) + continue; + + dal_dcs_mode_timing_list_append( + list, + &timing); + + ret = true; + } + + if (ret && !(*preferred_mode_found)) + for (i = dal_dcs_mode_timing_list_get_count(list); i > 0; --i) { + + struct mode_timing *mt = + dal_dcs_mode_timing_list_at_index(list, i-1); + + if (mt->mode_info.timing_source == + TIMING_SOURCE_EDID_ESTABLISHED) { + + mt->mode_info.flags.PREFERRED = 1; + *preferred_mode_found = true; + break; + } + } + return ret; +} + +void dal_edid13_add_patch_timings( + struct edid_13 *edid, + struct dcs_mode_timing_list *list, + bool *preferred_mode_found) +{ + + /*TODO:add edid patch timing impl*/ +} + +static bool get_supported_mode_timing( + struct edid_base *edid, + struct dcs_mode_timing_list *list, + bool *preferred_mode_found) +{ + struct edid_13 *e = FROM_EDID(edid); + /* Calling sequence/order is important for preferred mode lookup*/ + bool det = dal_edid13_add_detailed_timings( + e, list, preferred_mode_found); + + bool sta = dal_edid13_add_standard_timing( + e, list, preferred_mode_found); + + bool est = dal_edid13_add_established_timings( + e, list, preferred_mode_found); + + dal_edid13_add_patch_timings(e, list, preferred_mode_found); + + return det || sta || est; +} + +bool dal_edid13_get_vendor_product_id_info( + struct edid_base *edid, + struct vendor_product_id_info *info) +{ + struct edid_13 *e = FROM_EDID(edid); + + if (info == NULL) + return false; + + info->manufacturer_id = + (e->data->vendor_id[1] << 8) + (e->data->vendor_id[0]); + info->product_id = + (e->data->vendor_id[3] << 8) + (e->data->vendor_id[2]); + info->serial_id = + (e->data->vendor_id[7] << 24) + (e->data->vendor_id[6] << 16) + + (e->data->vendor_id[5] << 8) + (e->data->vendor_id[4]); + info->manufacture_week = e->data->vendor_id[8]; + info->manufacture_year = e->data->vendor_id[9]; + return true; +} + +static bool retrieve_display_name_from_descr( + struct edid_base *edid, + struct edid_display_descriptor *descr, + uint8_t *name) +{ + bool ret = false; + uint32_t i = 0; + uint8_t *buf; + + ASSERT(descr != NULL); + ASSERT(name != NULL); + + if ((0x0000 != descr->flag) || (0xFC != descr->type_tag)) + return false; + + /* Display Product Name Descriptor*/ + if ((descr->reserved1 != 0x00) || (descr->reserved2 != 0x00)) + if (!edid->error.BAD_DESCRIPTOR_FIELD) { + + edid->error.BAD_DESCRIPTOR_FIELD = true; + BREAK_TO_DEBUGGER(); + } + + buf = descr->u.name.monitor_name; + + for (i = 0; i < EDID_DISPLAY_NAME_SIZE; ++i) { + + if ((i < 13) && (buf[i] != 0x0A)) + name[i] = buf[i]; + else + name[i] = 0; + + } + + if (0 != name[0]) + ret = true; + + return ret; +} + +bool dal_edid13_get_display_name( + struct edid_base *edid, + uint8_t *name, + uint32_t name_size) +{ + uint8_t default_name[EDID_DISPLAY_NAME_SIZE] = {"DDC Display"}; + + uint32_t i; + bool name_supported = false; + struct edid_13 *e = FROM_EDID(edid); + + if (name_size > EDID_DISPLAY_NAME_SIZE) + name_size = EDID_DISPLAY_NAME_SIZE; + + ASSERT(name != NULL); + ASSERT(name_size > 0); + + /*Find and parse display product name descriptor in base block*/ + for (i = 0; i < NUM_OF_EDID1X_DETAILED_TIMING; ++i) { + struct edid_display_descriptor *descr = + (struct edid_display_descriptor *) + &e->data->edid_detailed_timings[i]; + + if (retrieve_display_name_from_descr(edid, descr, name)) { + name_supported = true; + break; + } + } + + if (!name_supported) + dal_memmove(name, default_name, name_size - 1); + + /*string must be NULL terminated. Set last char to '\0'.*/ + name[name_size - 1] = '\0'; + + return name_supported; +} + +static bool retrive_range_limits_from_descr( + struct edid_base *edid, + struct edid_display_descriptor *descr, + struct monitor_range_limits *limts) +{ + bool ret = false; + /*locals to store the info from EDID.*/ + uint32_t min_v_rate_hz; + uint32_t max_v_rate_hz; + uint32_t min_h_rate_khz; + uint32_t max_h_rate_khz; + uint32_t max_pix_clk_10mhz; + + if (((0x0000 != descr->flag) || (0xFD != descr->type_tag))) + return false; + + /* Monitor Range Limite Descriptor*/ + if ((descr->reserved1 != 0x00) || (descr->flag_range_limits != 0x00)) { + edid->error.BAD_RANGE_LIMIT_FIELD = true; + /*Bad RangeLimit Field*/ + BREAK_TO_DEBUGGER(); + } + + /* Monitor Range Limite Descriptor*/ + switch (descr->u.range_limit.secondary_timing_formula[0]) { + case 0x00: + limts->generation_method = EDID_TIMING_GENERATION_GTF; + break; + case 0x02: + limts->generation_method = EDID_TIMING_GENERATION_GTF2ND; + break; + default: + limts->generation_method = EDID_TIMING_GENERATION_UNKNOWN; + break; + } + + min_v_rate_hz = descr->u.range_limit.min_v_hz; + max_v_rate_hz = descr->u.range_limit.max_v_hz; + min_h_rate_khz = descr->u.range_limit.min_h_hz; + max_h_rate_khz = descr->u.range_limit.max_h_hz; + + /* See if the rates make sense.*/ + if ((min_v_rate_hz <= max_v_rate_hz) + && (min_h_rate_khz <= max_h_rate_khz)) { + + limts->min_v_rate_hz = min_v_rate_hz; + limts->max_v_rate_hz = max_v_rate_hz; + limts->min_h_rate_khz = min_h_rate_khz; + limts->max_h_rate_khz = max_h_rate_khz; + + max_pix_clk_10mhz = + descr->u.range_limit.max_support_pix_clk_by_10; + + if (max_pix_clk_10mhz != 0xFF) + limts->max_pix_clk_khz = max_pix_clk_10mhz * 10000; + + ret = true; + + } else { + edid->error.BAD_RANGE_LIMIT = true; + ret = false; + } + return ret; +} + +bool dal_edid13_get_monitor_range_limits( + struct edid_base *edid, + struct monitor_range_limits *limts) +{ + bool range_limits_supported = false; + uint32_t i; + struct edid_13 *e = FROM_EDID(edid); + + if (limts == NULL) + return false; + + for (i = 0; i < NUM_OF_EDID1X_DETAILED_TIMING; ++i) { + + struct edid_display_descriptor *descr = + (struct edid_display_descriptor *) + &e->data->edid_detailed_timings[i]; + + if (retrive_range_limits_from_descr(edid, descr, limts)) { + range_limits_supported = true; + break; + } + + } + return range_limits_supported; +} + +bool dal_edid13_get_display_characteristics( + struct edid_base *edid, + struct display_characteristics *characteristics) +{ + uint16_t rx, ry, gx, gy, bx, by, wx, wy; + uint8_t *arr; + struct edid_13 *e = FROM_EDID(edid); + + ASSERT(characteristics != NULL); + if (NULL == characteristics) + return false; + + dal_memmove( + characteristics->color_characteristics, + e->data->color_characteristics, + NUM_OF_BYTE_EDID_COLOR_CHARACTERISTICS); + + if (dal_edid_validate_display_gamma( + edid, e->data->basic_display_params[3])) + characteristics->gamma = e->data->basic_display_params[3]; + else + characteristics->gamma = 0; + + arr = characteristics->color_characteristics; + rx = (arr[2] << 2) | ((arr[0] >> 6) & 0x03); + ry = (arr[3] << 2) | ((arr[0] >> 4) & 0x03); + gx = (arr[4] << 2) | ((arr[0] >> 2) & 0x03); + gy = (arr[5] << 2) | (arr[0] & 0x3); + bx = (arr[6] << 2) | ((arr[1] >> 6) & 0x03); + by = (arr[7] << 2) | ((arr[1] >> 4) & 0x03); + wx = (arr[8] << 2) | ((arr[1] >> 2) & 0x03); + wy = (arr[9] << 2) | (arr[1] & 0x03); + + if ((rx + ry) > 1024 || (gx + gy) > 1024 || (bx + by) > 1024 + || (wx + wy) > 1024) + return false; + + return true; +} + +static bool get_screen_info( + struct edid_base *edid, + struct edid_screen_info *edid_screen_info) +{ + struct edid_13 *e = FROM_EDID(edid); + + ASSERT(edid_screen_info != NULL); + + /* Projector*/ + if ((e->data->basic_display_params[1] == 0) && + (e->data->basic_display_params[2] == 0)) + edid_screen_info->aspect_ratio = EDID_SCREEN_AR_PROJECTOR; + /* Screen Size*/ + else { + edid_screen_info->width = + 10 * e->data->basic_display_params[1]; + + edid_screen_info->height = + 10 * e->data->basic_display_params[2]; + + edid_screen_info->aspect_ratio = EDID_SCREEN_AR_UNKNOWN; + } + + return true; +} + +static bool get_connector_type( + struct edid_base *edid, + enum dcs_edid_connector_type *type) +{ + struct edid_13 *e = FROM_EDID(edid); + *type = (e->data->basic_display_params[0] & EDID1X_DIGITAL_SIGNAL) ? + EDID_CONNECTOR_DIGITAL : EDID_CONNECTOR_ANALOG; + return true; +} + +static bool get_display_color_depth( + struct edid_base *edid, + struct display_color_depth_support *color_depth) +{ + ASSERT(color_depth != NULL); + + /* Assume 8bpc always supported by Edid 1.3*/ + color_depth->mask = COLOR_DEPTH_INDEX_888; + + if (dal_edid_get_connector_type(edid) == EDID_CONNECTOR_DVI) + color_depth->deep_color_native_res_only = true; + + return true; +} + +bool dal_edid13_get_display_pixel_encoding( + struct edid_base *edid, + struct display_pixel_encoding_support *pixel_encoding) +{ + ASSERT(pixel_encoding != NULL); + + dal_memset( + pixel_encoding, + 0, + sizeof(struct display_pixel_encoding_support)); + + pixel_encoding->mask |= PIXEL_ENCODING_MASK_RGB; + + return true; +} + +uint8_t dal_edid13_num_of_extension(struct edid_base *edid) +{ + struct edid_13 *e = FROM_EDID(edid); + + return e->data->ext_blk_cnt; +} + +uint16_t dal_edid13_get_version(struct edid_base *edid) +{ + struct edid_13 *e = FROM_EDID(edid); + + return (e->data->version[0] << 8) | (e->data->version[1]); +} + +const uint8_t *dal_edid13_get_raw_data(struct edid_base *edid) +{ + struct edid_13 *e = FROM_EDID(edid); + + return (uint8_t *)e->data; +} + +const uint32_t dal_edid13_get_raw_size(struct edid_base *edid) +{ + return sizeof(struct edid_data_v1x); +} + +void dal_edid13_validate(struct edid_base *edid) +{ + struct edid_13 *e = FROM_EDID(edid); + + if (e->data->checksum != dal_edid_compute_checksum(edid)) + edid->error.BAD_CHECKSUM = true; +} + +void dal_edid13_destruct(struct edid_13 *edid) +{ + +} + +static void destroy(struct edid_base **edid) +{ + dal_edid13_destruct(FROM_EDID(*edid)); + dal_free(FROM_EDID(*edid)); + *edid = NULL; +} + +static const struct edid_funcs funcs = { + .destroy = destroy, + .get_display_tile_info = dal_edid_base_get_display_tile_info, + .get_min_drr_fps = dal_edid_base_get_min_drr_fps, + .get_drr_pixel_clk_khz = dal_edid_base_get_drr_pixel_clk_khz, + .is_non_continuous_frequency = + dal_edid_base_is_non_continuous_frequency, + .get_stereo_3d_support = dal_edid_base_get_stereo_3d_support, + .validate = dal_edid13_validate, + .get_version = dal_edid13_get_version, + .num_of_extension = dal_edid13_num_of_extension, + .get_edid_extension_tag = dal_edid_base_get_edid_extension_tag, + .get_cea_audio_modes = dal_edid_base_get_cea_audio_modes, + .get_cea861_support = dal_edid_base_get_cea861_support, + .get_display_pixel_encoding = dal_edid13_get_display_pixel_encoding, + .get_display_color_depth = get_display_color_depth, + .get_connector_type = get_connector_type, + .get_screen_info = get_screen_info, + .get_display_characteristics = dal_edid13_get_display_characteristics, + .get_monitor_range_limits = dal_edid13_get_monitor_range_limits, + .get_display_name = dal_edid13_get_display_name, + .get_vendor_product_id_info = dal_edid13_get_vendor_product_id_info, + .get_supported_mode_timing = get_supported_mode_timing, + .get_cea_video_capability_data_block = + dal_edid_base_get_cea_video_capability_data_block, + .get_cea_colorimetry_data_block = + dal_edid_base_get_cea_colorimetry_data_block, + .get_cea_speaker_allocation_data_block = + dal_edid_base_get_cea_speaker_allocation_data_block, + .get_cea_vendor_specific_data_block = + dal_edid_base_get_cea_vendor_specific_data_block, + .get_raw_size = dal_edid13_get_raw_size, + .get_raw_data = dal_edid13_get_raw_data, +}; + +bool dal_edid13_construct( + struct edid_13 *edid, + struct timing_service *ts, + uint32_t len, + const uint8_t *buf) +{ + /*if data not present, created from child edid v1.4*/ + if (len && buf && !dal_edid13_is_v_13(len, buf)) + return false; + + if (!dal_edid_base_construct(&edid->edid, ts)) + return false; + + edid->data = (struct edid_data_v1x *)buf; + + edid->edid.funcs = &funcs; + return true; +} + +struct edid_base *dal_edid13_create( + struct timing_service *ts, + uint32_t len, + const uint8_t *buf) +{ + struct edid_13 *edid = NULL; + + edid = dal_alloc(sizeof(struct edid_13)); + + if (!edid) + return NULL; + + if (dal_edid13_construct(edid, ts, len, buf)) + return &edid->edid; + + dal_free(edid); + BREAK_TO_DEBUGGER(); + return NULL; +} diff --git a/drivers/gpu/drm/amd/dal/dcs/edid/edid13.h b/drivers/gpu/drm/amd/dal/dcs/edid/edid13.h new file mode 100644 index 000000000000..0ea946f883a7 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/edid/edid13.h @@ -0,0 +1,117 @@ +/* + * Copyright 2012-15 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 + * + */ +#ifndef __DAL_EDID_13_H__ +#define __DAL_EDID_13_H__ + +#include "edid_base.h" + +struct edid_data_v1x; +struct standard_timing; +struct mode_info; + +struct edid_13 { + struct edid_base edid; + struct edid_data_v1x *data; +}; + +struct edid_established_modes { + bool reduced_blanking; + uint32_t h_res; + uint32_t v_res; + uint32_t refresh_rate; +}; + +struct edid_base *dal_edid13_create( + struct timing_service *ts, + uint32_t len, + const uint8_t *buf); + +bool dal_edid13_construct( + struct edid_13 *edid, + struct timing_service *ts, + uint32_t len, + const uint8_t *buf); + +void dal_edid13_destruct(struct edid_13 *edid); + +bool dal_edid13_retrieve_standard_mode( + uint8_t edid_minor_version, + const struct standard_timing *std_timing, + struct mode_info *mode_info); + +bool dal_edid13_get_display_pixel_encoding( + struct edid_base *edid, + struct display_pixel_encoding_support *pixel_encoding); + +bool dal_edid13_add_standard_timing( + struct edid_13 *edid, + struct dcs_mode_timing_list *list, + bool *preferred_mode_found); + +bool dal_edid13_add_detailed_timings( + struct edid_13 *edid, + struct dcs_mode_timing_list *list, + bool *preferred_mode_found); + +bool dal_edid13_add_established_timings( + struct edid_13 *edid, + struct dcs_mode_timing_list *list, + bool *preferred_mode_found); + +void dal_edid13_add_patch_timings( + struct edid_13 *edid, + struct dcs_mode_timing_list *list, + bool *preferred_mode_found); + +bool dal_edid13_is_v_13(uint32_t len, const uint8_t *buff); + +const uint8_t *dal_edid13_get_raw_data(struct edid_base *edid); + +const uint32_t dal_edid13_get_raw_size(struct edid_base *edid); + +uint16_t dal_edid13_get_version(struct edid_base *edid); + +uint8_t dal_edid13_num_of_extension(struct edid_base *edid); + +bool dal_edid13_get_display_characteristics( + struct edid_base *edid, + struct display_characteristics *characteristics); + +bool dal_edid13_get_monitor_range_limits( + struct edid_base *edid, + struct monitor_range_limits *limts); + +bool dal_edid13_get_display_name( + struct edid_base *edid, + uint8_t *name, + uint32_t name_size); + +bool dal_edid13_get_vendor_product_id_info( + struct edid_base *edid, + struct vendor_product_id_info *info); + +void dal_edid13_validate(struct edid_base *edid); + +#endif /* __DAL_EDID_13_H__ */ diff --git a/drivers/gpu/drm/amd/dal/dcs/edid/edid14.c b/drivers/gpu/drm/amd/dal/dcs/edid/edid14.c new file mode 100644 index 000000000000..6b694241688a --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/edid/edid14.c @@ -0,0 +1,842 @@ +/* + * Copyright 2012-15 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 + * + */ +#include "dal_services.h" +#include "include/timing_service_interface.h" +#include "edid_base.h" +#include "edid1x_data.h" +#include "edid.h" +#include "edid14.h" +#include "include/dcs_interface.h" + +enum cvt_3byte_ratio { + CVT_3BYTE_RATIO_4_BY_3, + CVT_3BYTE_RATIO_16_BY_9, + CVT_3BYTE_RATIO_16_BY_10, +}; + +enum cvt_3byte_v_rate { + CVT_3BYTE_V_RATE_60R = 0x01, + CVT_3BYTE_V_RATE_85 = 0x02, + CVT_3BYTE_V_RATE_75 = 0x04, + CVT_3BYTE_V_RATE_60 = 0x08, + CVT_3BYTE_V_RATE_50 = 0x10, + CVT_3BYTE_V_RATES_NUM = 5, +}; + +enum edid14_dvis_supported { + EDID14_DVIS_UNSUPPORTED = 0x00, + EDID14_DVIS_DVI = 0x01, + EDID14_DVIS_HDMIA = 0x02, + EDID14_DVIS_MDDI = 0x04, + EDID14_DVIS_DISPLAYPORT = 0x05 +}; + +#define EDID14_CEF_MASK 0x18 + +enum edid14_cef_supported { + EDID14_CEF_RGB = 0x00, + EDID14_CEF_RGB_YCRCB444 = 0x01, + EDID14_CEF_RGB_YCRCB422 = 0x02, + EDID14_CEF_RGB_YCRCB444_YCRCB422 = 0x03 +}; + +#define EDID14_BITDEPTH_UNDEFINED_MASK 0x80 +#define EDID14_BITDEPTH_6BIT_MASK 0x90 +#define EDID14_BITDEPTH_8BIT_MASK 0xA0 +#define EDID14_BITDEPTH_10BIT_MASK 0xB0 +#define EDID14_BITDEPTH_12BIT_MASK 0xC0 +#define EDID14_BITDEPTH_14BIT_MASK 0xD0 +#define EDID14_BITDEPTH_16BIT_MASK 0xE0 +#define EDID14_BITDEPTH_RSVD_MASK 0xF0 +#define EDID14_BITDEPTH_MASK 0xF0 + +#define EDID14_FEATURE_SUPPORT_OFFSET 0x04 + +union edid14_feature_support { + + struct { + uint8_t IS_CONTINUOUS_FREQUENCY:1; + uint8_t INCLUDE_NATIVE_PIXEL_FORMAT:1; + uint8_t DEFAULT_SRGB:1; + uint8_t BITS_3_4:2; + uint8_t DISPLAY_POWER_MANAGEMENT:3; + } bits; + uint8_t raw; +}; + +/** + * Digital Video Interface Standard (DVIS) Enumeration + * According to VESA EDID Document Version 1, Revision 4, May 5, 2006 + * DVSI - Digital Video Signal Interface + * DVIS - Digital Video Interface Standard + */ +#define EDID14_DVIS_MASK 0x0F + +#define ESTABLISHED_TIMING_III_TABLE_SIZE 44 +#define NUM_OF_EDID14_ESTABLISHED_TIMING_III 6 +#define NUM_OF_EDID14_ESTABLISHED_TIMING_III_RESERVED 6 + +static const struct edid_established_modes + established_timings_iii[ESTABLISHED_TIMING_III_TABLE_SIZE] = { + /* Established Timing III*/ + /* Byte 6 */ + { 0, 640, 350, 85 }, + { 0, 640, 400, 85 }, + { 0, 720, 400, 85 }, + { 0, 640, 480, 85 }, + { 0, 848, 480, 60 }, + { 0, 800, 600, 85 }, + { 0, 1024, 768, 85 }, + { 0, 1152, 864, 75 }, + + /* Byte 7*/ + { 1, 1280, 768, 60 }, /* Reduced Blanking*/ + { 0, 1280, 768, 60 }, + { 0, 1280, 768, 75 }, + { 0, 1280, 768, 85 }, + { 0, 1280, 960, 60 }, + { 0, 1280, 960, 85 }, + { 0, 1280, 1024, 60 }, + { 0, 1280, 1024, 85 }, + + /* Byte 8*/ + { 0, 1360, 768, 60 }, + { 1, 1440, 900, 60 }, /* Reduced Blanking*/ + { 0, 1440, 900, 60 }, + { 0, 1440, 900, 75 }, + { 0, 1440, 900, 85 }, + { 1, 1400, 1050, 60 }, /* Reduced Blanking*/ + { 0, 1400, 1050, 60 }, + { 0, 1400, 1050, 75 }, + + /* Byte 9*/ + { 0, 1400, 1050, 85 }, + { 1, 1680, 1050, 60 }, /* Reduced Blanking*/ + { 0, 1680, 1050, 60 }, + { 0, 1680, 1050, 75 }, + { 0, 1680, 1050, 85 }, + { 0, 1600, 1200, 60 }, + { 0, 1600, 1200, 65 }, + { 0, 1600, 1200, 70 }, + + /* Byte 10*/ + { 0, 1600, 1200, 75 }, + { 0, 1600, 1200, 85 }, + { 0, 1792, 1344, 60 }, + { 0, 1792, 1344, 75 }, + { 0, 1856, 1392, 60 }, + { 0, 1856, 1392, 75 }, + { 1, 1920, 1200, 60 }, /* Reduced Blanking*/ + { 0, 1920, 1200, 60 }, + + /* Byte 11*/ + { 0, 1920, 1200, 75 }, + { 0, 1920, 1200, 85 }, + { 0, 1920, 1440, 60 }, + { 0, 1920, 1440, 75 }, +}; + +#define FROM_EDID(e) container_of((e), struct edid_13, edid) + +bool dal_edid14_is_v_14(uint32_t len, const uint8_t *buff) +{ + uint8_t major; + uint8_t minor; + + if (!dal_edid_get_version_raw(buff, len, &major, &minor)) + return false; + + if (major == 1 && minor == 4) + return true; + + return false; +} + +bool dal_edid14_add_cvt_3byte_timing_from_descr( + struct edid_base *edid, + struct dcs_mode_timing_list *list, + const struct cvt_3byte_timing *descr) +{ + bool ret = false; + uint32_t i; + struct mode_timing mode_timing; + uint32_t aspect_ratio; + uint32_t refresh_rate; + uint32_t h_active = 0; + uint32_t v_active = 0; + + ASSERT(descr != NULL); + + aspect_ratio = (descr->v_active_u4_ratio & 0x0c) >> 2; + refresh_rate = descr->refresh_rate & 0x1f; + + dal_memset(&mode_timing, 0, sizeof(struct mode_timing)); + + if ((descr->v_active_l8 == 0x00) && + (descr->v_active_u4_ratio == 0x00) && + (descr->refresh_rate == 0x00)) + /* Skip current timing block.*/ + return false; + + if ((descr->v_active_u4_ratio & 0x03) != 0) { + edid->error.BAD_CVT_3BYTE_FIELD = true; + return false; + } + + v_active = descr->v_active_u4_ratio & 0xf0; + v_active = (v_active << 4) + descr->v_active_l8; + + switch (aspect_ratio) { + case CVT_3BYTE_RATIO_4_BY_3: + h_active = (v_active * 4) / 3; + break; + + case CVT_3BYTE_RATIO_16_BY_9: + h_active = (v_active * 16) / 9; + break; + + case CVT_3BYTE_RATIO_16_BY_10: + h_active = (v_active * 16) / 10; + break; + + default: + edid->error.BAD_CVT_3BYTE_FIELD = true; + return false; + } + + mode_timing.mode_info.pixel_height = v_active; + mode_timing.mode_info.pixel_width = h_active; + mode_timing.mode_info.timing_source = TIMING_SOURCE_EDID_CVT_3BYTE; + + for (i = 0; i < CVT_3BYTE_V_RATES_NUM; ++i) { + + mode_timing.mode_info.field_rate = 0; + mode_timing.mode_info.flags.REDUCED_BLANKING = false; + mode_timing.mode_info.timing_standard = + TIMING_STANDARD_CVT; + + switch (refresh_rate & (1 << i)) { + case CVT_3BYTE_V_RATE_60R: + mode_timing.mode_info.field_rate = 60; + mode_timing.mode_info.flags.REDUCED_BLANKING = true; + mode_timing.mode_info.timing_standard = + TIMING_STANDARD_CVT_RB; + break; + case CVT_3BYTE_V_RATE_85: + mode_timing.mode_info.field_rate = 85; + mode_timing.mode_info.flags.REDUCED_BLANKING = false; + break; + case CVT_3BYTE_V_RATE_75: + mode_timing.mode_info.field_rate = 75; + mode_timing.mode_info.flags.REDUCED_BLANKING = false; + break; + case CVT_3BYTE_V_RATE_60: + mode_timing.mode_info.field_rate = 60; + mode_timing.mode_info.flags.REDUCED_BLANKING = false; + break; + case CVT_3BYTE_V_RATE_50: + mode_timing.mode_info.field_rate = 50; + mode_timing.mode_info.flags.REDUCED_BLANKING = false; + break; + default: + break; + } + + if (mode_timing.mode_info.field_rate == 0) + continue; + + if (dal_timing_service_get_timing_for_mode( + edid->ts, + &mode_timing.mode_info, + &mode_timing.crtc_timing)) + if (dal_dcs_mode_timing_list_append( + list, + &mode_timing)) + ret = true; + } + return ret; +} + +static bool add_cvt_3byte_timing( + struct edid_13 *edid, + struct dcs_mode_timing_list *list, + bool *preferred_mode_found) +{ + bool ret = false; + uint32_t i; + uint32_t j; + + ASSERT(list != NULL); + + for (i = 0; i < NUM_OF_EDID1X_DETAILED_TIMING; ++i) { + const struct edid_display_descriptor *descr = + (struct edid_display_descriptor *) + &edid->data->edid_detailed_timings[i]; + + if (descr->flag != 0) + continue; + + if ((descr->reserved1 != 0) || (descr->reserved2 != 0)) { + edid->edid.error.BAD_DESCRIPTOR_FIELD = true; + continue; + } + + if (0xf8 != descr->type_tag) + continue; + + if (descr->u.cvt_3byte_timing.version != 0x01) { + edid->edid.error.BAD_DESCRIPTOR_FIELD = true; + continue; + } + + for (j = 0; + j < MAX_NUM_OF_CVT3BYTE_TIMING_IDS_IN_DET_TIMING_DESC; + ++j) { + if (dal_edid14_add_cvt_3byte_timing_from_descr( + &edid->edid, + list, + &descr->u.cvt_3byte_timing.timing[i])) + ret = true; + } + } + return ret; +} + +#define GET_BIT(arr, bit) (arr[(bit) / 8] & (1 << (7 - ((bit) & 0x07)))) + +static bool add_established_timing_from_descr( + struct edid_13 *edid, + struct dcs_mode_timing_list *list, + const struct edid_display_descriptor *descr) +{ + bool ret = false; + struct mode_timing timing; + uint32_t i; + uint32_t bit_size = + (ESTABLISHED_TIMING_III_TABLE_SIZE < + NUM_OF_EDID14_ESTABLISHED_TIMING_III * 8) ? + ESTABLISHED_TIMING_III_TABLE_SIZE : + NUM_OF_EDID14_ESTABLISHED_TIMING_III * 8; + + for (i = 0; i < bit_size; ++i) { + + if (!GET_BIT(descr->u.est_timing_iii.timing_bits, i)) + continue; +/* In Timing II, bit 4 indicates 1024X768 87Hz interlaced*/ +/* We don't want to expose this old mode since it causes DTM failures*/ +/* so skip adding it to the TS list*/ + if (established_timings_iii[i].refresh_rate == 87) + continue; + + dal_memset(&timing, 0, sizeof(struct mode_timing)); + timing.mode_info.pixel_width = + established_timings_iii[i].h_res; + + timing.mode_info.pixel_height = + established_timings_iii[i].v_res; + + timing.mode_info.field_rate = + established_timings_iii[i].refresh_rate; + + timing.mode_info.timing_standard = TIMING_STANDARD_DMT; + timing.mode_info.timing_source = TIMING_SOURCE_EDID_ESTABLISHED; + + if (!dal_edid_get_timing_for_vesa_mode( + &edid->edid, + &timing.mode_info, + &timing.crtc_timing)) + continue; + + dal_dcs_mode_timing_list_append(list, &timing); + + ret = true; + } + return ret; +} + +static bool add_established_timing( + struct edid_13 *edid, + struct dcs_mode_timing_list *list, + bool *preferred_mode_found) +{ + bool ret = false; + int32_t i; + + ASSERT(list != NULL); + ASSERT(preferred_mode_found != NULL); + + { +/*Parse Established Timing I/II & Manufacturer's Timing without selecting + preferred mode. Preferred mode will be selected in the end of this function*/ + bool ignore_preffered = true; + + dal_edid13_add_established_timings( + edid, list, &ignore_preffered); + } + + for (i = 0; i < 4; i++) { + + const struct edid_display_descriptor *descr = + (struct edid_display_descriptor *) + &edid->data->edid_detailed_timings[i]; + + if ((descr->flag != 0x0000) || (descr->type_tag != 0xF7)) + continue; + + if (descr->u.est_timing_iii.version != 0x0A) { + edid->edid.error.BAD_ESTABLISHED_III_FIELD = true; + break; + } + +/* Check the reserved bits in the last byte in Established Timing III*/ + if (descr->u.est_timing_iii.timing_bits[5] & 0x0f) { + edid->edid.error.BAD_ESTABLISHED_III_FIELD = true; + BREAK_TO_DEBUGGER(); + } + + add_established_timing_from_descr(edid, list, descr); + } + + if (ret && !(*preferred_mode_found)) { + i = dal_dcs_mode_timing_list_get_count(list); + for (; i > 0; --i) { + struct mode_timing *mt = + dal_dcs_mode_timing_list_at_index(list, i-1); + if (mt->mode_info.timing_source == + TIMING_SOURCE_EDID_ESTABLISHED) { + mt->mode_info.flags.PREFERRED = 1; + *preferred_mode_found = true; + break; + } + } + } + return ret; +} + +static bool get_supported_mode_timing( + struct edid_base *edid, + struct dcs_mode_timing_list *list, + bool *preferred_mode_found) +{ + struct edid_13 *e = FROM_EDID(edid); + /* Calling sequence/order is important for preferred mode lookup*/ + bool det = dal_edid13_add_detailed_timings( + e, list, preferred_mode_found); + + bool sta = dal_edid13_add_standard_timing( + e, list, preferred_mode_found); + + bool est = add_established_timing( + e, list, preferred_mode_found); + + bool cvt = add_cvt_3byte_timing( + e, list, preferred_mode_found); + + dal_edid13_add_patch_timings(e, list, preferred_mode_found); + + return det || sta || est || cvt; +} + +static bool get_connector_type( + struct edid_base *edid, + enum dcs_edid_connector_type *type) +{ + struct edid_13 *e = FROM_EDID(edid); + + if (e->data->basic_display_params[0] & EDID1X_DIGITAL_SIGNAL) + switch (e->data->basic_display_params[0] & EDID14_DVIS_MASK) { + case EDID14_DVIS_UNSUPPORTED: + *type = EDID_CONNECTOR_DIGITAL; + break; + case EDID14_DVIS_DVI: + *type = EDID_CONNECTOR_DVI; + break; + case EDID14_DVIS_HDMIA: + *type = EDID_CONNECTOR_HDMIA; + break; + case EDID14_DVIS_MDDI: + *type = EDID_CONNECTOR_MDDI; + break; + case EDID14_DVIS_DISPLAYPORT: + *type = EDID_CONNECTOR_DISPLAYPORT; + break; + default: + edid->error.INVALID_CONNECTOR = true; + break; + } + else + *type = EDID_CONNECTOR_ANALOG; + + return true; +} + +static bool is_non_continuous_frequency(struct edid_base *edid) +{ + struct edid_13 *e = FROM_EDID(edid); + union edid14_feature_support feature; + + feature.raw = + e->data->basic_display_params[EDID14_FEATURE_SUPPORT_OFFSET]; + + return !feature.bits.IS_CONTINUOUS_FREQUENCY; +} + +static bool get_screen_info( + struct edid_base *edid, + struct edid_screen_info *info) +{ + struct edid_13 *e = FROM_EDID(edid); + + ASSERT(info != NULL); + + /* Portrait Aspect Ratio*/ + if ((e->data->basic_display_params[1] == 0) && + (e->data->basic_display_params[2] != 0)) + switch (e->data->basic_display_params[2]) { + case 0x4f: + info->aspect_ratio = EDID_SCREEN_AR_9X16; + break; + case 0x3d: + info->aspect_ratio = EDID_SCREEN_AR_10X16; + break; + case 0x22: + info->aspect_ratio = EDID_SCREEN_AR_3X4; + break; + case 0x1a: + info->aspect_ratio = EDID_SCREEN_AR_4X5; + break; + } + /*Landscape Aspect Ratio*/ + else if ((e->data->basic_display_params[1] != 0) && + (e->data->basic_display_params[2] == 0)) + switch (e->data->basic_display_params[1]) { + case 0x4f: + info->aspect_ratio = EDID_SCREEN_AR_16X9; + break; + case 0x3d: + info->aspect_ratio = EDID_SCREEN_AR_16X10; + break; + case 0x22: + info->aspect_ratio = EDID_SCREEN_AR_4X3; + break; + case 0x1a: + info->aspect_ratio = EDID_SCREEN_AR_5X4; + break; + } + /* Projector*/ + else if ((e->data->basic_display_params[1] == 0) && + (e->data->basic_display_params[2] == 0)) + info->aspect_ratio = EDID_SCREEN_AR_PROJECTOR; + /* Screen Size*/ + else { + /* Change ImageSize from centimeter to millimeter*/ + info->width = 10 * e->data->basic_display_params[1]; + info->height = 10 * e->data->basic_display_params[2]; + } + return true; +} + +static void add_lower_color_depth( + struct display_color_depth_support *color_depth) +{ + if (color_depth->mask & COLOR_DEPTH_INDEX_888) { + color_depth->mask |= COLOR_DEPTH_INDEX_666; + } else if (color_depth->mask & COLOR_DEPTH_INDEX_101010) { + color_depth->mask |= COLOR_DEPTH_INDEX_888; + color_depth->mask |= COLOR_DEPTH_INDEX_666; + } else if (color_depth->mask & COLOR_DEPTH_INDEX_121212) { + color_depth->mask |= COLOR_DEPTH_INDEX_101010; + color_depth->mask |= COLOR_DEPTH_INDEX_888; + color_depth->mask |= COLOR_DEPTH_INDEX_666; + } else if (color_depth->mask & COLOR_DEPTH_INDEX_141414) { + color_depth->mask |= COLOR_DEPTH_INDEX_121212; + color_depth->mask |= COLOR_DEPTH_INDEX_101010; + color_depth->mask |= COLOR_DEPTH_INDEX_888; + color_depth->mask |= COLOR_DEPTH_INDEX_666; + } else if (color_depth->mask & COLOR_DEPTH_INDEX_161616) { + color_depth->mask |= COLOR_DEPTH_INDEX_141414; + color_depth->mask |= COLOR_DEPTH_INDEX_121212; + color_depth->mask |= COLOR_DEPTH_INDEX_101010; + color_depth->mask |= COLOR_DEPTH_INDEX_888; + color_depth->mask |= COLOR_DEPTH_INDEX_666; + } +} + +static bool get_display_color_depth( + struct edid_base *edid, + struct display_color_depth_support *color_depth) +{ + + struct edid_13 *e = FROM_EDID(edid); + uint8_t vsi = e->data->basic_display_params[0]; + uint8_t bit_depth = vsi & EDID14_BITDEPTH_MASK; + enum dcs_edid_connector_type connector; + + color_depth->mask = 0; + + ASSERT(color_depth != NULL); + /* Interpret panel format according to depth bit only in + case of DVI/DISPLAYPORT device*/ + if (!(bit_depth & EDID1X_DIGITAL_SIGNAL)) + return false; + + switch (bit_depth) { + case EDID14_BITDEPTH_6BIT_MASK: + color_depth->mask |= COLOR_DEPTH_INDEX_666; + break; + case EDID14_BITDEPTH_8BIT_MASK: + color_depth->mask |= COLOR_DEPTH_INDEX_888; + break; + case EDID14_BITDEPTH_10BIT_MASK: + color_depth->mask |= COLOR_DEPTH_INDEX_101010; + break; + case EDID14_BITDEPTH_12BIT_MASK: + color_depth->mask |= COLOR_DEPTH_INDEX_121212; + break; + case EDID14_BITDEPTH_14BIT_MASK: + color_depth->mask |= COLOR_DEPTH_INDEX_141414; + break; + case EDID14_BITDEPTH_16BIT_MASK: + color_depth->mask |= COLOR_DEPTH_INDEX_161616; + break; + default: + color_depth->mask |= COLOR_DEPTH_INDEX_888; + break; + } + + /* Display Port should support all lower depths*/ + if ((vsi & EDID14_DVIS_MASK) == EDID14_DVIS_DISPLAYPORT) + add_lower_color_depth(color_depth); + + get_connector_type(edid, &connector); + + if (connector == EDID_CONNECTOR_DVI) + color_depth->deep_color_native_res_only = true; + + return true; + +} + +static bool get_display_pixel_encoding( + struct edid_base *edid, + struct display_pixel_encoding_support *encoding) +{ + struct edid_13 *e = FROM_EDID(edid); + bool ret = true; + + ASSERT(encoding != NULL); + + dal_memset(encoding, 0, sizeof(struct display_pixel_encoding_support)); + + if (e->data->basic_display_params[0] & EDID1X_DIGITAL_SIGNAL) { + + uint8_t pix_encoding = (e->data->basic_display_params[4] & + EDID14_CEF_MASK) >> 3; + + switch (pix_encoding) { + case EDID14_CEF_RGB: + encoding->mask |= PIXEL_ENCODING_MASK_RGB; + break; + case EDID14_CEF_RGB_YCRCB444: + encoding->mask |= PIXEL_ENCODING_MASK_RGB; + encoding->mask |= PIXEL_ENCODING_MASK_YCBCR444; + break; + case EDID14_CEF_RGB_YCRCB422: + encoding->mask |= PIXEL_ENCODING_MASK_RGB; + encoding->mask |= PIXEL_ENCODING_MASK_YCBCR422; + break; + case EDID14_CEF_RGB_YCRCB444_YCRCB422: + encoding->mask |= PIXEL_ENCODING_MASK_RGB; + encoding->mask |= PIXEL_ENCODING_MASK_RGB; + encoding->mask |= PIXEL_ENCODING_MASK_YCBCR422; + break; + default: + edid->error.BAD_COLOR_ENCODING_FORMAT_FIELD = 1; + ret = false; + break; + } + + } else + ret = dal_edid13_get_display_pixel_encoding(edid, encoding); + + return ret; +} + +static bool panel_supports_drr( + struct edid_base *edid, + uint32_t *pix_clk, + uint32_t *min_fps) +{ + /*DRR support indicated by a) panel pixel clock within + rangelimit max pixel clock*/ + /*b) supports nonContinuous freq*/ + + /*The 1st "detailed timing" is the preferred timing in EDID1.4.*/ + /*Expect valid pixel clock since preferred timing is a requirement + of 1.4*/ + struct edid_13 *e = FROM_EDID(edid); + uint32_t pix_clk_khz = e->data->edid_detailed_timings[0].pix_clk * 10; + + struct monitor_range_limits range_limits = { 0 }; + + if (0 == pix_clk_khz) + return false; /*shouldn't get here if EDID is correct*/ + + /* One condition is removed here. + * In old logic, we require panel EDID to report Non-Continuous freq. + * In new logic, we require panel EDID to report Continuous freq. + * New logic is being proposed in new DP VESA spec. + * So to support both old and new methods, we remove the check. */ + + /* Second condition is removed here. + * There used to be a check condition here to compare RangeLimit Max + * Clock >= Detailed Timing required Pixel Clock. From Syed, DRR should + * not need this restriction so it is removed from here. */ + + /* We must have RangeLimits to support DRR. Otherwise return false. */ + if (!dal_edid_get_monitor_range_limits(edid, &range_limits)) + return false; + + /*check rangeLimits horz timing must match*/ + if (range_limits.max_h_rate_khz != range_limits.min_h_rate_khz) + return false; + + /*return parameters if requested*/ + if (pix_clk) + *pix_clk = pix_clk_khz; + + /*Note: EDID1.4 Display Range Limit Btye[4]bits[1:0] indicates if offset + required if minVerticalRateInHz > 255Hz. Since DRR wants lowest refresh + rate we do not expect to use offset. If required in future override + Edid13::retrieveRangeLimitFromDescriptor.*/ + if (min_fps) + *min_fps = range_limits.min_v_rate_hz; + + /*Panel EDID indicates support for DRR*/ + return true; +} + +static uint32_t get_drr_pixel_clk_khz(struct edid_base *edid) +{ + uint32_t pix_clk_khz = 0; + + /*Check panel support for DRR; returns valid PixelClock*/ + if (!panel_supports_drr(edid, &pix_clk_khz, NULL)) + pix_clk_khz = 0; + + return pix_clk_khz; +} + +static uint32_t get_min_drr_fps(struct edid_base *edid) +{ + uint32_t fps = 0; + + if (!panel_supports_drr(edid, NULL, &fps)) + fps = 0; + + return fps; +} + +static void destroy(struct edid_base **edid) +{ + dal_edid13_destruct(FROM_EDID(*edid)); + dal_free(FROM_EDID(*edid)); + *edid = NULL; +} + +static const struct edid_funcs funcs = { + .destroy = destroy, + .get_display_tile_info = dal_edid_base_get_display_tile_info, + .get_min_drr_fps = get_min_drr_fps, + .get_drr_pixel_clk_khz = get_drr_pixel_clk_khz, + .is_non_continuous_frequency = is_non_continuous_frequency, + .get_stereo_3d_support = dal_edid_base_get_stereo_3d_support, + .validate = dal_edid13_validate, + .get_version = dal_edid13_get_version, + .num_of_extension = dal_edid13_num_of_extension, + .get_edid_extension_tag = dal_edid_base_get_edid_extension_tag, + .get_cea_audio_modes = dal_edid_base_get_cea_audio_modes, + .get_cea861_support = dal_edid_base_get_cea861_support, + .get_display_pixel_encoding = get_display_pixel_encoding, + .get_display_color_depth = get_display_color_depth, + .get_connector_type = get_connector_type, + .get_screen_info = get_screen_info, + .get_display_characteristics = dal_edid13_get_display_characteristics, + .get_monitor_range_limits = dal_edid13_get_monitor_range_limits, + .get_display_name = dal_edid13_get_display_name, + .get_vendor_product_id_info = dal_edid13_get_vendor_product_id_info, + .get_supported_mode_timing = get_supported_mode_timing, + .get_cea_video_capability_data_block = + dal_edid_base_get_cea_video_capability_data_block, + .get_cea_colorimetry_data_block = + dal_edid_base_get_cea_colorimetry_data_block, + .get_cea_speaker_allocation_data_block = + dal_edid_base_get_cea_speaker_allocation_data_block, + .get_cea_vendor_specific_data_block = + dal_edid_base_get_cea_vendor_specific_data_block, + .get_raw_size = dal_edid13_get_raw_size, + .get_raw_data = dal_edid13_get_raw_data, +}; + +static bool construct( + struct edid_13 *edid, + struct timing_service *ts, + uint32_t len, + const uint8_t *buf) +{ + if (len == 0 || buf == NULL) + return false; + + if (!dal_edid14_is_v_14(len, buf)) + return false; + + if (!dal_edid13_construct(edid, ts, 0, NULL)) + return false; + + edid->data = (struct edid_data_v1x *)buf; + + edid->edid.funcs = &funcs; + return true; +} + +struct edid_base *dal_edid14_create( + struct timing_service *ts, + uint32_t len, + const uint8_t *buf) +{ + struct edid_13 *edid = NULL; + + edid = dal_alloc(sizeof(struct edid_13)); + + if (!edid) + return NULL; + + if (construct(edid, ts, len, buf)) + return &edid->edid; + + dal_free(edid); + BREAK_TO_DEBUGGER(); + return NULL; +} diff --git a/drivers/gpu/drm/amd/dal/dcs/edid/edid14.h b/drivers/gpu/drm/amd/dal/dcs/edid/edid14.h new file mode 100644 index 000000000000..3c7596ff5f8e --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/edid/edid14.h @@ -0,0 +1,45 @@ +/* + * Copyright 2012-15 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 + * + */ +#ifndef __DAL_EDID_14_H__ +#define __DAL_EDID_14_H__ + +#include "edid13.h" + +struct edid_base *dal_edid14_create( + struct timing_service *ts, + uint32_t len, + const uint8_t *buf); + +bool dal_edid14_is_v_14(uint32_t len, const uint8_t *buff); + +struct cvt_3byte_timing; +struct dcs_override_mode_timing_list; + +bool dal_edid14_add_cvt_3byte_timing_from_descr( + struct edid_base *edid, + struct dcs_mode_timing_list *list, + const struct cvt_3byte_timing *descr); + +#endif /* __DAL_EDID_14_H__ */ diff --git a/drivers/gpu/drm/amd/dal/dcs/edid/edid1x_data.h b/drivers/gpu/drm/amd/dal/dcs/edid/edid1x_data.h new file mode 100644 index 000000000000..1ea432364b97 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/edid/edid1x_data.h @@ -0,0 +1,167 @@ +/* + * Copyright 2012-15 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 + * + */ + +#ifndef __DAL_EDID_1X_DATA_H__ +#define __DAL_EDID_1X_DATA_H__ + +#include "edid_base.h" + +#define NUM_OF_BYTE_EDID1X_HEADER 8 +#define NUM_OF_BYTE_EDID1X_VENDOR_ID 10 +#define NUM_OF_BYTE_EDID1X_VERSION 2 +#define NUM_OF_BYTE_EDID1X_BASIC_INFO 5 +#define NUM_OF_EDID1X_ESTABLISHED_TIMING 3 +#define NUM_OF_EDID1X_STANDARD_TIMING 8 +#define NUM_OF_EDID1X_DETAILED_TIMING 4 +#define NUM_OF_BYTE_EDID_COLOR_CHARACTERISTICS 10 +#define EDID1X_SERIAL_NUMBER_OFFSET 0x0C +#define EDID1X_MANUFACTURE_DATE_OFFSET 0x10 +#define EDID1X_DETAILED_TIMINGS_OFFSET 0x36 +#define EDID1X_CHECKSUM_OFFSET 0x7F + +#define EDID1X_SERIAL_NUMBER_DATASIZE 4 +#define EDID1X_MANUFACTURE_DATE_DATASIZE 2 +#define EDID1X_DETAILED_TIMINGS_DATASIZE 0x48 + +#define EDID1X_DIGITAL_SIGNAL 0x80 + +#define MAX_NUM_OF_STD_TIMING_IDS_IN_DET_TIMING_DESC 6 +#define MAX_NUM_OF_CVT3BYTE_TIMING_IDS_IN_DET_TIMING_DESC 4 + +enum edid_ratio { + RATIO_16_BY_10, + RATIO_4_BY_3, + RATIO_5_BY_4, + RATIO_16_BY_9 +}; + +struct edid_data_v1x { + uint8_t header[NUM_OF_BYTE_EDID1X_HEADER]; + uint8_t vendor_id[NUM_OF_BYTE_EDID1X_VENDOR_ID]; + uint8_t version[NUM_OF_BYTE_EDID1X_VERSION]; + uint8_t basic_display_params[NUM_OF_BYTE_EDID1X_BASIC_INFO]; + uint8_t color_characteristics[NUM_OF_BYTE_EDID_COLOR_CHARACTERISTICS]; + uint8_t established_timings[NUM_OF_EDID1X_ESTABLISHED_TIMING]; + uint16_t standard_timings[NUM_OF_EDID1X_STANDARD_TIMING]; + + struct edid_detailed + edid_detailed_timings[NUM_OF_EDID1X_DETAILED_TIMING]; + + uint8_t ext_blk_cnt; + uint8_t checksum; +}; + +struct standard_timing { + uint8_t h_addressable; + union { + struct { + uint8_t REFRESH_RATE:6; + uint8_t RATIO:2; + } ratio_and_refresh_rate; + uint8_t s_uchar; + } u; +}; + +struct cvt_3byte_timing { + uint8_t v_active_l8; + uint8_t v_active_u4_ratio; + uint8_t refresh_rate; +}; + +struct edid_display_descriptor { + uint16_t flag; + uint8_t reserved1; + uint8_t type_tag; + union { + uint8_t reserved2; + uint8_t flag_range_limits;/*Only use in range limits*/ + }; + + union { + /* as ASCI string, when ucTypeTag == 0xff*/ + struct { + uint8_t sn[13]; + } serial; + + /* as ASCI string, when ucTypeTag == 0xfe*/ + struct { + uint8_t string[13]; + } asci_string; + + /* as monitor range limit, when ucTypeTag = 0xfd*/ + struct { + uint8_t min_v_hz; + uint8_t max_v_hz; + uint8_t min_h_hz; + uint8_t max_h_hz; + uint8_t max_support_pix_clk_by_10; + uint8_t secondary_timing_formula[8]; + } range_limit; + + /*as monitor name, when ucTypeTag == 0xfc*/ + struct { + uint8_t monitor_name[13]; + } name; + + /* as color point data, when ucTypeTag == 0xfb*/ + struct { + uint8_t color_point[13]; + } point; + + /* as standard timing id, when ucTypeTag == 0xfa*/ + struct { + struct standard_timing timing[MAX_NUM_OF_STD_TIMING_IDS_IN_DET_TIMING_DESC]; + uint8_t magic; + } std_timings; + + /* as display color management (DCM), when ucTypeTag = 0xf9*/ + struct { + uint8_t dcm[13]; + } dcm; + + /* as CVT 3byte timings, when ucTypeTag == 0xf8*/ + struct { + uint8_t version; + struct cvt_3byte_timing timing[MAX_NUM_OF_CVT3BYTE_TIMING_IDS_IN_DET_TIMING_DESC]; + } cvt_3byte_timing; + + /* as established timings III, when ucTypeTag = 0xf7*/ + struct { + uint8_t version; + uint8_t timing_bits[12]; + } est_timing_iii; + + /* as CEA-861 manufacturer defined desc, + when ucTypeTag = 0x0-0xf*/ + struct { + uint8_t data[13]; + } manufacture_defined_blk; + + /* raw char arrary*/ + uint8_t monitor_raw_data[13]; + } u; +}; + +#endif /* __DAL_EDID_1X_DATA_H__ */ diff --git a/drivers/gpu/drm/amd/dal/dcs/edid/edid20.c b/drivers/gpu/drm/amd/dal/dcs/edid/edid20.c new file mode 100644 index 000000000000..e73ecdb3b74b --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/edid/edid20.c @@ -0,0 +1,637 @@ +/* + * Copyright 2012-15 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 + * + */ + +#include "dal_services.h" +#include "include/dcs_interface.h" +#include "edid_base.h" +#include "edid1x_data.h" +#include "edid.h" +#include "edid20.h" + +#define EDID20_SERIAL_NUMBER_OFFSET 40 +#define EDID20_CHECKSUM_OFFSET 255 + +struct edid20_4byte_mode { + uint8_t h_active; + uint8_t flag; + uint8_t active_ratio; + uint8_t refresh_rate; +}; + +struct edid20_range_limit { + uint8_t min_frame_rate;/* In Hz*/ + uint8_t max_frame_rate;/* In Hz*/ + uint8_t min_line_rate;/* In KHz*/ + uint8_t max_line_rate;/* In KHz*/ + struct { + uint8_t MAX_LINE:2; + uint8_t MIN_LINE:2; + uint8_t MAX_FRAME:2; + uint8_t MIN_FRAME:2; + } low_bits_v_h_rate; + uint8_t min_pixel_rate;/*In KHz*/ + uint8_t max_pixel_rate;/* In KHz*/ + struct { + uint8_t MAX_PIXEL_RATE:4; + uint8_t MIN_PIXEL_RATE:4; + } up_bits_pixel_rate; +}; + +#define EDID20_4BYTE_RATIO_4_BY_3 0x85 +#define EDID20_4BYTE_RATIO_16_BY_9 0xB2 +#define EDID20_4BYTE_RATIO_1152_BY_870 0x84 + +#define FROM_EDID(e) (container_of((e), struct edid_20, edid)) + +struct edid_20 { + struct edid_base edid; + struct edid_data_v20 *data; + +}; + +bool dal_edid20_is_v_20(uint32_t len, const uint8_t *buf) +{ + uint8_t major; + uint8_t minor; + + if (!dal_edid_get_version_raw(buf, len, &major, &minor)) + return false; + + if (major == 2 && minor == 0) + return true; + + return false; +} + +static bool add_detailed_timings( + struct edid_20 *e, + struct dcs_mode_timing_list *list, + bool *preferred_mode_found) +{ + bool ret = false; + uint32_t i; + struct mode_timing mode_timing; + + uint8_t offs = 0; + + uint8_t luminance_table = (e->data->timing_info_map[0] >> 5) & 0x1; + uint8_t fre_ranges_num = (e->data->timing_info_map[0] >> 2) & 0x7; + uint8_t detailed_ranges_num = e->data->timing_info_map[0] & 0x3; + uint8_t timing_codes_num = (e->data->timing_info_map[1] >> 3) & 0x1f; + uint8_t detailed_timings_num = e->data->timing_info_map[1] & 0x7; + + ASSERT(list != NULL); + ASSERT(preferred_mode_found != NULL); + + if (luminance_table) { + /*if separate sub channels*/ + if ((e->data->timing_descr[offs] >> 7) & 0x1) + offs += ((e->data->timing_descr[offs] & 0x1f) * 3) + 1; + else + offs += (e->data->timing_descr[offs] & 0x1f) + 1; + } + + offs += fre_ranges_num * 8 + detailed_ranges_num * 27 + + timing_codes_num * 4; + + /* since we are retrieving these values from the EDID we need to verify + that we do not exceed the array*/ + for (i = 0; i < NUM_OF_EDID20_DETAILED_TIMING && + i < detailed_timings_num && + (i * sizeof(struct edid_detailed)) + offs <= + sizeof(e->data->timing_descr) - + sizeof(struct edid_detailed); ++i) { + + uint32_t pos = i * sizeof(struct edid_detailed) + offs; + const struct edid_detailed *edid_detailed = + (const struct edid_detailed *) + &e->data->timing_descr[pos]; + + dal_memset(&mode_timing, 0, sizeof(struct mode_timing)); + if (!dal_edid_detailed_to_timing( + &e->edid, + edid_detailed, + true, + &mode_timing.crtc_timing)) + continue; + + dal_edid_timing_to_mode_info( + &mode_timing.crtc_timing, + &mode_timing.mode_info); + + mode_timing.mode_info.flags.NATIVE = 1; + + /* If preferred mode yet not found - + select first detailed mode/timing as preferred*/ + if (!(*preferred_mode_found)) { + mode_timing.mode_info.flags.PREFERRED = 1; + *preferred_mode_found = true; + } + + dal_dcs_mode_timing_list_append(list, &mode_timing); + + ret = true; + } + + return ret; +} + +static bool retrieve_4byte_mode_from_descr( + const struct edid20_4byte_mode *descr, + struct mode_info *mode_info) +{ + uint32_t h_active; + uint32_t v_active; + + if ((descr->h_active == 0) || + (descr->active_ratio == 0) || + (descr->refresh_rate == 0)) + return false; + + dal_memset(mode_info, 0, sizeof(struct mode_info)); + + h_active = descr->h_active * 16 + 256; + + switch (descr->active_ratio) { + case EDID20_4BYTE_RATIO_4_BY_3: + v_active = (h_active * 3) / 4; + break; + + case EDID20_4BYTE_RATIO_16_BY_9: + v_active = (h_active * 9) / 16; + break; + + case EDID20_4BYTE_RATIO_1152_BY_870: + v_active = (h_active * 870) / 1152; + break; + + default: + v_active = (h_active * 100) / descr->active_ratio; + break; + } + + mode_info->timing_standard = TIMING_STANDARD_GTF; + mode_info->timing_source = TIMING_SOURCE_EDID_4BYTE; + + if (descr->flag & 0x40) + mode_info->flags.INTERLACE = 1; + + mode_info->pixel_width = h_active; + mode_info->pixel_height = v_active; + if (descr->refresh_rate == 59) + mode_info->field_rate = 60; + else + mode_info->field_rate = descr->refresh_rate; + + return true; +} + +static bool add_4byte_timings( + struct edid_20 *e, + struct dcs_mode_timing_list *list, + bool *preferred_mode_found) +{ + + bool ret = false; + uint32_t i; + struct mode_timing mode_timing; + uint8_t offs = 0; + uint8_t luminance_table = (e->data->timing_info_map[0] >> 5) & 0x1; + uint8_t fre_ranges_num = (e->data->timing_info_map[0] >> 2) & 0x7; + uint8_t detailed_ranges_num = e->data->timing_info_map[0] & 0x3; + uint8_t timing_codes_num = (e->data->timing_info_map[1] >> 3) & 0x1f; + + ASSERT(list != NULL); + ASSERT(preferred_mode_found != NULL); + + + /* find end of luminance*/ + if (luminance_table) { + /* if separate sub channels*/ + if ((e->data->timing_descr[offs] >> 7) & 0x1) + offs += ((e->data->timing_descr[offs] & 0x1f) * 3) + 1; + else + offs += (e->data->timing_descr[offs] & 0x1f) + 1; + } + + offs += fre_ranges_num * 8 + detailed_ranges_num * 27; + + for (i = 0; i < NUM_OF_EDID20_4BYTE_TIMING && i < timing_codes_num; + ++i) { + uint32_t pos = i * sizeof(struct edid20_4byte_mode) + offs; + const struct edid20_4byte_mode *edid20_4byte_mode = + (const struct edid20_4byte_mode *)&e->data->timing_descr[pos]; + + dal_memset(&mode_timing, 0, sizeof(struct mode_timing)); + + if (!retrieve_4byte_mode_from_descr( + edid20_4byte_mode, + &mode_timing.mode_info)) + continue; + + if (!dal_edid_get_timing_for_vesa_mode( + &e->edid, + &mode_timing.mode_info, + &mode_timing.crtc_timing)) + continue; + + dal_dcs_mode_timing_list_append(list, &mode_timing); + ret = true; + } + + return ret; +} + +static bool get_supported_mode_timing( + struct edid_base *edid, + struct dcs_mode_timing_list *list, + bool *preferred_mode_found) +{ + struct edid_20 *e = FROM_EDID(edid); + /*Calling sequence/order is important for preferred mode lookup*/ + bool ret_det = add_detailed_timings(e, list, preferred_mode_found); + bool ret_4byte = add_4byte_timings(e, list, preferred_mode_found); + + return ret_det || ret_4byte; +} + +static bool get_vendor_product_id_info( + struct edid_base *edid, + struct vendor_product_id_info *info) +{ + struct edid_20 *e = FROM_EDID(edid); + + ASSERT(info != NULL); + + info->manufacturer_id = + (e->data->vendor_id[1] << 8) + e->data->vendor_id[0]; + + info->product_id = + (e->data->vendor_id[3] << 8) + e->data->vendor_id[2]; + + info->serial_id = 0;/* for V2, serial is a 16 byte ascii string.*/ + + info->manufacture_week = e->data->vendor_id[4]; + + info->manufacture_year = + (e->data->vendor_id[6] << 8) + e->data->vendor_id[5]; + + return true; +} + +static bool get_display_name( + struct edid_base *edid, + uint8_t *name, + uint32_t name_size) +{ + uint8_t default_name[EDID_DISPLAY_NAME_SIZE] = { "DDC Display" }; + uint8_t *edid_name = default_name; + struct edid_20 *e = FROM_EDID(edid); + + ASSERT(name != NULL); + ASSERT(name_size > 0); + + if (name_size > EDID_DISPLAY_NAME_SIZE) + name_size = EDID_DISPLAY_NAME_SIZE; + + if (e->data->manufacturer_id[0]) + edid_name = e->data->manufacturer_id; + + dal_memmove(name, edid_name, name_size); + return true; +} + +static bool retrieve_range_limit_from_descr( + const struct edid20_range_limit *descr, + struct monitor_range_limits *limts) +{ + if ((descr->max_frame_rate == 0) || + (descr->max_line_rate == 0) || + (descr->max_pixel_rate == 0)) + return false; + + limts->min_v_rate_hz = + ((uint16_t)descr->min_frame_rate << 2) + + descr->low_bits_v_h_rate.MIN_FRAME; + + limts->max_v_rate_hz = + ((uint16_t)descr->max_frame_rate << 2) + + descr->low_bits_v_h_rate.MAX_FRAME; + + limts->min_h_rate_khz = + ((uint16_t)descr->min_line_rate << 2) + + (descr->low_bits_v_h_rate.MIN_LINE); + + limts->max_h_rate_khz = + ((uint16_t)descr->max_line_rate << 2) + + (descr->low_bits_v_h_rate.MAX_LINE); + + /* Convert to KHz*/ + limts->max_pix_clk_khz = + (((uint16_t)descr->up_bits_pixel_rate.MAX_PIXEL_RATE << 4) + + descr->max_pixel_rate) * 1000; + + return true; +} + +static bool get_monitor_range_limits( + struct edid_base *edid, + struct monitor_range_limits *limts) +{ + bool ret = false; + uint32_t i; + /* find start of detailed timing*/ + struct edid_20 *e = FROM_EDID(edid); + uint8_t luminance_table = (e->data->timing_info_map[0] >> 5) & 0x01; + uint8_t fre_ranges_num = (e->data->timing_info_map[0] >> 2) & 0x07; + uint8_t offs = 0; + + ASSERT(limts != NULL); + + /* find end of luminance*/ + if (luminance_table) { + /* if separate sub channels*/ + if ((e->data->timing_descr[offs] >> 7) & 0x1) + offs += ((e->data->timing_descr[offs] & 0x1f) * 3) + 1; + else + offs += (e->data->timing_descr[offs] & 0x1f) + 1; + } + + for (i = 0; i < fre_ranges_num; ++i) { + + uint32_t pos = i * sizeof(struct edid20_range_limit) + offs; + + const struct edid20_range_limit *range_limit = + (const struct edid20_range_limit *) + (&e->data->timing_descr[pos]); + + if (retrieve_range_limit_from_descr( + range_limit, + limts)) { + /* At most one monitor range limit descriptor + exists in EDID base block*/ + ret = true; + break; + } + } + + return ret; +} + +static bool get_display_characteristics( + struct edid_base *edid, + struct display_characteristics *characteristics) +{ + struct edid_20 *e = FROM_EDID(edid); + uint16_t rx, ry, gx, gy, bx, by, wx, wy; + uint8_t *arr; + + ASSERT(characteristics != NULL); + + dal_memmove(characteristics->color_characteristics, + &e->data->color_descr[8], + NUM_OF_BYTE_EDID_COLOR_CHARACTERISTICS); + + if (dal_edid_validate_display_gamma(edid, e->data->color_descr[0])) + characteristics->gamma = e->data->color_descr[0]; + else + characteristics->gamma = 0; + + arr = characteristics->color_characteristics; + rx = ((uint16_t)arr[2] << 2) + ((arr[0] & 0xC0) >> 6); + ry = ((uint16_t)arr[3] << 2) + ((arr[0] & 0x30) >> 4); + gx = ((uint16_t)arr[4] << 2) + ((arr[0] & 0x0C) >> 2); + gy = ((uint16_t)arr[5] << 2) + (arr[0] & 0x03); + bx = ((uint16_t)arr[6] << 2) + ((arr[1] & 0xC0) >> 6); + by = ((uint16_t)arr[7] << 2) + ((arr[1] & 0x30) >> 4); + wx = ((uint16_t)arr[8] << 2) + ((arr[1] & 0x0C) >> 2); + wy = ((uint16_t)arr[9] << 2) + (arr[1] & 0x03); + + if ((rx + ry) > 1024 || (gx + gy) > 1024 || (bx + by) > 1024 + || (wx + wy) > 1024) + return false; + + return true; + +} + +static bool get_screen_info( + struct edid_base *edid, + struct edid_screen_info *info) +{ + struct edid_20 *e = FROM_EDID(edid); + + ASSERT(info != NULL); + + info->width = + (e->data->display_spatial_descr[1] << 8) + + e->data->display_spatial_descr[0]; + info->height = + (e->data->display_spatial_descr[3] << 8) + + e->data->display_spatial_descr[2]; + + info->aspect_ratio = EDID_SCREEN_AR_UNKNOWN; + + return true; +} + +static bool get_connector_type( + struct edid_base *edid, + enum dcs_edid_connector_type *type) +{ + struct edid_20 *e = FROM_EDID(edid); + uint8_t v_if_type = e->data->display_interface_params[1]; + + if ((((v_if_type & 0x1F) == v_if_type) || /*default interface*/ + ((v_if_type & 0x2F) == v_if_type)) && + (((v_if_type & 0xF0) == v_if_type) || /*secondary interface*/ + ((v_if_type & 0xF1) == v_if_type) || + ((v_if_type & 0xF2) == v_if_type))) + return EDID_CONNECTOR_ANALOG; + else + return EDID_CONNECTOR_DIGITAL; + + return true; +} + +static bool get_display_color_depth( + struct edid_base *edid, + struct display_color_depth_support *color_depth) +{ + struct edid_20 *e = FROM_EDID(edid); + uint8_t gr_data = e->data->display_interface_params[11]; + uint8_t g_bit_depth = gr_data & 0x0F; + uint8_t r_bit_depth = gr_data >> 4; + uint8_t b_data = e->data->display_interface_params[12]; + uint8_t b_bit_depth = b_data >> 4; + + ASSERT(color_depth != NULL); + + if (g_bit_depth == 6 && r_bit_depth == 6 && b_bit_depth == 6) + color_depth->mask |= COLOR_DEPTH_INDEX_666; + else + color_depth->mask |= COLOR_DEPTH_INDEX_888; + + return true; +} + +static bool get_display_pixel_encoding( + struct edid_base *edid, + struct display_pixel_encoding_support *pixel_encoding) +{ + struct edid_20 *e = FROM_EDID(edid); + uint8_t color_encoding = + (e->data->display_interface_params[10] & 0xF0) >> 4; + + ASSERT(pixel_encoding != NULL); + + dal_memset( + pixel_encoding, + 0, + sizeof(struct display_pixel_encoding_support)); + + if (color_encoding == 0x1) + pixel_encoding->mask |= PIXEL_ENCODING_MASK_RGB; + else if (color_encoding == 0xA) + pixel_encoding->mask |= PIXEL_ENCODING_MASK_YCBCR422; + + return true; +} + +static uint16_t get_version(struct edid_base *edid) +{ + return EDID_VER_20; +} + +static const uint8_t *get_raw_data(struct edid_base *edid) +{ + struct edid_20 *e = FROM_EDID(edid); + + return (uint8_t *)e->data; +} + +static const uint32_t get_raw_size(struct edid_base *edid) +{ + return sizeof(struct edid_data_v20); +} + +static void validate(struct edid_base *edid) +{ + struct edid_20 *e = FROM_EDID(edid); + + if (e->data->checksum != dal_edid_compute_checksum(edid)) + edid->error.BAD_CHECKSUM = true; +} + +static void destruct(struct edid_20 *edid) +{ + +} + +static void destroy(struct edid_base **edid) +{ + destruct(FROM_EDID(*edid)); + dal_free(FROM_EDID(*edid)); + *edid = NULL; +} + +static const struct edid_funcs funcs = { + .destroy = destroy, + .get_display_tile_info = dal_edid_base_get_display_tile_info, + .get_min_drr_fps = dal_edid_base_get_min_drr_fps, + .get_drr_pixel_clk_khz = dal_edid_base_get_drr_pixel_clk_khz, + .is_non_continuous_frequency = + dal_edid_base_is_non_continuous_frequency, + .get_stereo_3d_support = dal_edid_base_get_stereo_3d_support, + .validate = validate, + .get_version = get_version, + .num_of_extension = dal_edid_base_num_of_extension, + .get_edid_extension_tag = dal_edid_base_get_edid_extension_tag, + .get_cea_audio_modes = dal_edid_base_get_cea_audio_modes, + .get_cea861_support = dal_edid_base_get_cea861_support, + .get_display_pixel_encoding = get_display_pixel_encoding, + .get_display_color_depth = get_display_color_depth, + .get_connector_type = get_connector_type, + .get_screen_info = get_screen_info, + .get_display_characteristics = get_display_characteristics, + .get_monitor_range_limits = get_monitor_range_limits, + .get_display_name = get_display_name, + .get_vendor_product_id_info = get_vendor_product_id_info, + .get_supported_mode_timing = get_supported_mode_timing, + .get_cea_video_capability_data_block = + dal_edid_base_get_cea_video_capability_data_block, + .get_cea_colorimetry_data_block = + dal_edid_base_get_cea_colorimetry_data_block, + .get_cea_speaker_allocation_data_block = + dal_edid_base_get_cea_speaker_allocation_data_block, + .get_cea_vendor_specific_data_block = + dal_edid_base_get_cea_vendor_specific_data_block, + .get_raw_size = get_raw_size, + .get_raw_data = get_raw_data, +}; + +static bool construct( + struct edid_20 *edid, + struct timing_service *ts, + uint32_t len, + const uint8_t *buf) +{ + if (len == 0 || buf == NULL) + return false; + + if (!dal_edid_base_construct(&edid->edid, ts)) + return false; + + if (!dal_edid20_is_v_20(len, buf)) { + dal_edid_base_destruct(&edid->edid); + return false; + } + + edid->data = (struct edid_data_v20 *)buf; + + edid->edid.funcs = &funcs; + return true; + +} + +struct edid_base *dal_edid20_create( + struct timing_service *ts, + uint32_t len, + const uint8_t *buf) +{ + struct edid_20 *edid = NULL; + + edid = dal_alloc(sizeof(struct edid_20)); + + if (!edid) + return NULL; + + if (construct(edid, ts, len, buf)) + return &edid->edid; + + dal_free(edid); + BREAK_TO_DEBUGGER(); + return NULL; +} diff --git a/drivers/gpu/drm/amd/dal/dcs/edid/edid20.h b/drivers/gpu/drm/amd/dal/dcs/edid/edid20.h new file mode 100644 index 000000000000..b20f932752b6 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/edid/edid20.h @@ -0,0 +1,69 @@ +/* + * Copyright 2012-15 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 + * + */ +#ifndef __DAL_EDID_20_H__ +#define __DAL_EDID_20_H__ + +#define NUM_OF_EDID20_DETAILED_TIMING 7 +#define NUM_OF_EDID20_4BYTE_TIMING 31 +#define NUM_OF_BYTE_EDID20_VENDOR_ID 7 +#define NUM_OF_BYTE_EDID20_MANUFACTURER 32 +#define NUM_OF_BYTE_EDID20_SERIALNUM 16 +#define NUM_OF_BYTE_EDID20_RESERVED 8 +#define NUM_OF_BYTE_EDID20_INTERFACE_PARA 15 +#define NUM_OF_BYTE_EDID20_DEVICE_DESCRIPTION 5 +#define NUM_OF_BYTE_EDID20_RESPONSETIME 2 +#define NUM_OF_BYTE_EDID20_COLOR_DESCRIPTION 28 +#define NUM_OF_BYTE_EDID20_SPATIAL_DESCRIPTION 10 +#define NUM_OF_BYTE_EDID20_MAP_TIMING 2 +#define NUM_OF_BYTE_EDID20_TIMING_DESCRIPTION 127 + +struct edid_data_v20 { + uint8_t version; + uint8_t vendor_id[NUM_OF_BYTE_EDID20_VENDOR_ID]; + uint8_t manufacturer_id[NUM_OF_BYTE_EDID20_MANUFACTURER]; + uint8_t serial_number[NUM_OF_BYTE_EDID20_SERIALNUM]; + uint8_t reserved[NUM_OF_BYTE_EDID20_RESERVED]; + uint8_t display_interface_params[NUM_OF_BYTE_EDID20_INTERFACE_PARA]; + uint8_t display_device_descr[NUM_OF_BYTE_EDID20_DEVICE_DESCRIPTION]; + uint8_t display_response_time[NUM_OF_BYTE_EDID20_RESPONSETIME]; + uint8_t color_descr[NUM_OF_BYTE_EDID20_COLOR_DESCRIPTION]; + uint8_t display_spatial_descr[NUM_OF_BYTE_EDID20_SPATIAL_DESCRIPTION]; + uint8_t reserved2; + uint8_t gtf_info; + uint8_t timing_info_map[NUM_OF_BYTE_EDID20_MAP_TIMING]; + uint8_t timing_descr[NUM_OF_BYTE_EDID20_TIMING_DESCRIPTION]; + uint8_t checksum; +}; + +struct timing_service; + +struct edid_base *dal_edid20_create( + struct timing_service *ts, + uint32_t len, + const uint8_t *buf); + +bool dal_edid20_is_v_20(uint32_t len, const uint8_t *buff); + +#endif /* __DAL_EDID_20_H__ */ diff --git a/drivers/gpu/drm/amd/dal/dcs/edid/edid_base.c b/drivers/gpu/drm/amd/dal/dcs/edid/edid_base.c new file mode 100644 index 000000000000..f5ffdc1a35ed --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/edid/edid_base.c @@ -0,0 +1,817 @@ +/* + * Copyright 2012-15 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 + * + */ + +#include "dal_services.h" +#include "include/timing_service_interface.h" +#include "include/dcs_interface.h" +#include "edid_base.h" + +#define FOR_EACH_EDID_FIRST(list, expr, result)\ +{\ + for (; list != NULL; list = list->next)\ + if (list->funcs->expr) {\ + result = true;\ + break;\ + } \ +} + +#define FOR_EACH_EDID_ALL(list, expr, result)\ +{\ + for (; list != NULL; list = list->next) {\ + if (list->funcs->expr) \ + result = true;\ + } \ +} + + + +bool dal_edid_base_get_supported_mode_timing( + struct edid_base *edid, + struct dcs_mode_timing_list *list, + bool *preferred_mode_found) +{ + return false; +} + +bool dal_edid_base_get_vendor_product_id_info( + struct edid_base *edid, + struct vendor_product_id_info *info) +{ + return false; +} + +bool dal_edid_base_get_display_name( + struct edid_base *edid, + uint8_t *name, + uint32_t name_size) +{ + return false; +} + +bool dal_edid_base_get_monitor_range_limits( + struct edid_base *edid, + struct monitor_range_limits *limts) +{ + return false; +} + +bool dal_edid_base_get_display_characteristics( + struct edid_base *edid, + struct display_characteristics *characteristics) +{ + return false; +} + +bool dal_edid_base_get_screen_info( + struct edid_base *edid, + struct edid_screen_info *edid_screen_info) +{ + return false; +} + +bool dal_edid_base_get_connector_type( + struct edid_base *edid, + enum dcs_edid_connector_type *type) +{ + return false; +} + +bool dal_edid_base_get_display_color_depth( + struct edid_base *edid, + struct display_color_depth_support *color_depth) +{ + return false; +} + +bool dal_edid_base_get_display_pixel_encoding( + struct edid_base *edid, + struct display_pixel_encoding_support *pixel_encoding) +{ + return false; +} + +bool dal_edid_base_get_cea861_support( + struct edid_base *edid, + struct cea861_support *cea861_support) +{ + return false; +} + +bool dal_edid_base_get_cea_vendor_specific_data_block( + struct edid_base *edid, + struct cea_vendor_specific_data_block *vendor_block) +{ + return false; +} + +bool dal_edid_base_get_cea_speaker_allocation_data_block( + struct edid_base *edid, + union cea_speaker_allocation_data_block *spkr_data) +{ + return false; +} + +bool dal_edid_base_get_cea_colorimetry_data_block( + struct edid_base *edid, + struct cea_colorimetry_data_block *colorimetry_data_block) +{ + return false; +} + +bool dal_edid_base_get_cea_video_capability_data_block( + struct edid_base *edid, + union cea_video_capability_data_block + *video_capability_data_block) +{ + return false; +} + +bool dal_edid_base_get_cea_audio_modes( + struct edid_base *edid, + struct dcs_cea_audio_mode_list *audio_list) +{ + return false; +} + +uint8_t dal_edid_base_get_edid_extension_tag(struct edid_base *edid) +{ + return 0; +} + +uint8_t dal_edid_base_num_of_extension(struct edid_base *edid) +{ + return 0; +} + +uint16_t dal_edid_base_get_version(struct edid_base *edid) +{ + return 0; +} + +void dal_edid_base_validate(struct edid_base *edid) +{ + +} + +bool dal_edid_base_get_stereo_3d_support( + struct edid_base *edid, + struct edid_stereo_3d_capability *stereo_capability) +{ + return false; +} +bool dal_edid_base_is_non_continuous_frequency(struct edid_base *edid) +{ + return false; +} + +uint32_t dal_edid_base_get_drr_pixel_clk_khz(struct edid_base *edid) +{ + return false; +} + +uint32_t dal_edid_base_get_min_drr_fps(struct edid_base *edid) +{ + return false; +} +bool dal_edid_base_get_display_tile_info( + struct edid_base *edid, + struct dcs_display_tile *display_tile) +{ + return false; +} + +void dal_edid_base_destruct(struct edid_base *edid) +{ + +} + +bool dal_edid_base_construct(struct edid_base *edid, struct timing_service *ts) +{ + if (NULL == ts) + return false; + + edid->ts = ts; + return true; +} + +void dal_edid_list_destroy(struct edid_base *edid) +{ + if (!edid) + return; + + do { + struct edid_base *edid_base = edid->next; + + dal_edid_destroy(&edid); + edid = edid_base; + } while (edid); +} + +void dal_edid_destroy(struct edid_base **edid) +{ + if (edid == NULL || *edid == NULL) + return; + + (*edid)->funcs->destroy(edid); + *edid = NULL; + +} + +bool dal_edid_get_supported_mode_timing( + struct edid_base *edid, + struct dcs_mode_timing_list *list, + bool *preferred_mode_found) +{ + bool ret = false; + + FOR_EACH_EDID_ALL( + edid, + get_supported_mode_timing(edid, list, preferred_mode_found), + ret); + return ret; +} + +bool dal_edid_get_vendor_product_id_info( + struct edid_base *edid, + struct vendor_product_id_info *info) +{ + bool ret = false; + + FOR_EACH_EDID_FIRST( + edid, + get_vendor_product_id_info(edid, info), + ret); + return ret; +} + +bool dal_edid_get_display_name( + struct edid_base *edid, + uint8_t *name, + uint32_t name_size) +{ + bool ret = false; + + FOR_EACH_EDID_FIRST( + edid, + get_display_name(edid, name, name_size), + ret); + return ret; +} + +bool dal_edid_get_monitor_range_limits( + struct edid_base *edid, + struct monitor_range_limits *limits) +{ + bool ret = false; + + FOR_EACH_EDID_FIRST( + edid, + get_monitor_range_limits(edid, limits), + ret); + return ret; +} + +bool dal_edid_get_display_characteristics( + struct edid_base *edid, + struct display_characteristics *characteristics) +{ + bool ret = false; + + FOR_EACH_EDID_FIRST( + edid, + get_display_characteristics(edid, characteristics), + ret); + return ret; +} + +bool dal_edid_get_screen_info( + struct edid_base *edid, + struct edid_screen_info *info) +{ + bool ret = false; + + FOR_EACH_EDID_FIRST( + edid, + get_screen_info(edid, info), + ret); + return ret; +} + +enum dcs_edid_connector_type dal_edid_get_connector_type(struct edid_base *edid) +{ + enum dcs_edid_connector_type type = EDID_CONNECTOR_UNKNOWN; + bool res = false; + + FOR_EACH_EDID_ALL( + edid, get_connector_type(edid, &type), res); + return type; +} +bool dal_edid_get_display_color_depth( + struct edid_base *edid, + struct display_color_depth_support *color_depth) +{ + bool res = false; + + FOR_EACH_EDID_ALL( + edid, get_display_color_depth(edid, color_depth), res); + return res; +} + +bool dal_edid_get_display_pixel_encoding( + struct edid_base *edid, + struct display_pixel_encoding_support *pixel_encoding) +{ + bool res = false; + + FOR_EACH_EDID_ALL( + edid, + get_display_pixel_encoding(edid, pixel_encoding), + res); + + return res; +} + +bool dal_edid_get_cea861_support( + struct edid_base *edid, + struct cea861_support *cea861_support) +{ + bool ret = false; + + FOR_EACH_EDID_FIRST( + edid, + get_cea861_support(edid, cea861_support), + ret); + return ret; +} + +bool dal_edid_get_cea_vendor_specific_data_block( + struct edid_base *edid, + struct cea_vendor_specific_data_block *vendor_block) +{ + bool ret = false; + + FOR_EACH_EDID_FIRST( + edid, + get_cea_vendor_specific_data_block(edid, vendor_block), + ret); + return ret; +} + +bool dal_edid_get_cea_speaker_allocation_data_block( + struct edid_base *edid, + union cea_speaker_allocation_data_block *spkr_data) +{ + bool ret = false; + + FOR_EACH_EDID_FIRST( + edid, + get_cea_speaker_allocation_data_block(edid, spkr_data), + ret); + return ret; +} +bool dal_edid_get_cea_colorimetry_data_block( + struct edid_base *edid, + struct cea_colorimetry_data_block *colorimetry_data_block) +{ + bool ret = false; + + FOR_EACH_EDID_FIRST( + edid, + get_cea_colorimetry_data_block(edid, colorimetry_data_block), + ret); + return ret; +} + +bool dal_edid_get_cea_video_capability_data_block( + struct edid_base *edid, + union cea_video_capability_data_block *video_capability_data_block) +{ + bool ret = false; + + FOR_EACH_EDID_FIRST( + edid, + get_cea_video_capability_data_block( + edid, video_capability_data_block), + ret); + return ret; +} + +bool dal_edid_get_cea_audio_modes( + struct edid_base *edid, + struct dcs_cea_audio_mode_list *audio_list) +{ + bool res = false; + + FOR_EACH_EDID_ALL( + edid, + get_cea_audio_modes(edid, audio_list), + res); + + return res; +} + +uint8_t dal_edid_get_edid_extension_tag(struct edid_base *edid) +{ + return edid->funcs->get_edid_extension_tag(edid); +} + +uint8_t dal_edid_get_num_of_extension(struct edid_base *edid) +{ + return edid->funcs->num_of_extension(edid); +} + +uint16_t dal_edid_get_version(struct edid_base *edid) +{ + return edid->funcs->get_version(edid); +} + +void dal_edid_validate(struct edid_base *edid) +{ + edid->funcs->validate(edid); +} + +bool dal_edid_get_stereo_3d_support( + struct edid_base *edid, + struct edid_stereo_3d_capability *stereo_capability) +{ + bool ret = false; + + FOR_EACH_EDID_FIRST( + edid, + get_stereo_3d_support(edid, stereo_capability), + ret); + return ret; +} + +bool dal_edid_is_non_continous_frequency(struct edid_base *edid) +{ + bool ret = false; + + FOR_EACH_EDID_FIRST( + edid, + is_non_continuous_frequency(edid), + ret); + return ret; +} + +uint32_t dal_edid_get_drr_pixel_clk_khz(struct edid_base *edid) +{ + bool ret = false; + + FOR_EACH_EDID_FIRST( + edid, + get_drr_pixel_clk_khz(edid), + ret); + return ret; +} + +uint32_t dal_edid_get_min_drr_fps(struct edid_base *edid) +{ + bool ret = false; + + FOR_EACH_EDID_FIRST( + edid, + get_min_drr_fps(edid), + ret); + return ret; +} + +bool dal_edid_get_display_tile_info( + struct edid_base *edid, + struct dcs_display_tile *display_tile) +{ + bool ret = false; + + FOR_EACH_EDID_FIRST( + edid, + get_display_tile_info(edid, display_tile), + ret); + return ret; +} + +struct edid_base *dal_edid_get_next_block(struct edid_base *edid) +{ + return edid->next; +} + +void dal_edid_set_next_block(struct edid_base *edid, struct edid_base *nxt_edid) +{ + edid->next = nxt_edid; +} + +struct edid_error *dal_edid_get_errors(struct edid_base *edid) +{ + return &edid->error; +} + +bool dal_edid_validate_display_gamma(struct edid_base *edid, uint8_t gamma) +{ + /*TODO: looks useless, input uint8_t + 100 in range 100-355*/ + uint32_t min_gamma = 100; /*min acc.to vesa spec*/ + uint32_t max_gamma = 355; /*max acc.to vesa spec*/ + uint32_t i_gamma = gamma; + + /*gamma should be in range 1 - 3.55 + edid has the gamma in the following form= 120(x78), + the usage is (120+100)/100=2.2*/ + i_gamma += 100; + return (i_gamma >= min_gamma) && (i_gamma <= max_gamma); +} + +static uint8_t one_byte_checksum(uint32_t len, const uint8_t *buf) +{ + uint8_t checksum = 0; + uint32_t i; + + for (i = 0; i < len; ++i) + checksum += buf[i]; + + return 0x100 - checksum; +} + +uint8_t dal_edid_compute_checksum(struct edid_base *edid) +{ + return one_byte_checksum( + dal_edid_get_size(edid) - 1, + dal_edid_get_raw_data(edid)); +} + +uint32_t dal_edid_get_size(struct edid_base *edid) +{ + return edid->funcs->get_raw_size(edid); +} + +const uint8_t *dal_edid_get_raw_data(struct edid_base *edid) +{ + return edid->funcs->get_raw_data(edid); +} + +void dal_edid_timing_to_mode_info( + const struct crtc_timing *timing, + struct mode_info *mode_info) +{ + dal_timing_service_create_mode_info_from_timing(timing, mode_info); + mode_info->timing_source = TIMING_SOURCE_EDID_DETAILED; +} + +/* + * patch_porch_values_for_4k + * + * @brief + * path front and back porch values for AOC 4K timing + * + * @param + * struct crtc_timing *crtc_timing - [in/out]CRTC timing for patching. + * + * @return void + */ +void patch_porch_values_for_4k(struct crtc_timing *crtc_timing) +{ + if (3840 == crtc_timing->h_addressable && + 2160 == crtc_timing->v_addressable && + 4000 == crtc_timing->h_total && + 2222 == crtc_timing->v_total && + 4 == crtc_timing->h_front_porch && + 54 == crtc_timing->v_front_porch && + 144 == crtc_timing->h_sync_width && + 5 == crtc_timing->v_sync_width && + 0 == crtc_timing->h_border_left && + 0 == crtc_timing->v_border_top && + 0 == crtc_timing->h_border_right && + 0 == crtc_timing->v_border_bottom && + 533280 == crtc_timing->pix_clk_khz) { + + crtc_timing->h_front_porch = 48; + crtc_timing->h_sync_width = 109; + crtc_timing->v_front_porch = 8; + crtc_timing->v_sync_width = 10; + } +} + +bool dal_edid_detailed_to_timing( + struct edid_base *edid, + const struct edid_detailed *edid_detailed, + bool border_active, + struct crtc_timing *timing) +{ + uint32_t h_blank = 0; + uint32_t v_blank = 0; + uint32_t h_border = 0; + uint32_t v_border = 0; + enum edid_detailed_stereo stereo; + + + /*Make sure that this is not a monitor descriptor block as per + EDID 1.1 and above*/ + if (edid_detailed->pix_clk == 0) + return false; + + { + const uint8_t *data = (uint8_t *)edid_detailed; + uint32_t i = 0; + /*We are going to look at the raw bytes for this timing and + make sure that the timing does not consist of all + the same byte.*/ + for (i = 1; data[i] == data[0] && + i < sizeof(struct edid_detailed); ++i) + ; + + if (i == sizeof(struct edid_detailed)) + return false; + } + timing->pix_clk_khz = edid_detailed->pix_clk * 10UL; + timing->h_addressable = edid_detailed->pix_width_8_low + + ((uint32_t)edid_detailed->byte4.PIX_WIDTH_4_HIGH << 8); + timing->v_addressable = edid_detailed->pix_height_8_low + + ((uint32_t)edid_detailed->byte7.PIX_HEIGHT_4_HIGH << 8); + timing->h_front_porch = edid_detailed->h_sync_offs_8_low + + ((uint32_t)edid_detailed->byte11.H_SYNC_OFFSET_2_HIGH << 8); + timing->h_sync_width = edid_detailed->h_sync_width_8_low + + ((uint32_t)edid_detailed->byte11.H_SYNC_WIDTH_2_HIGH << 8); + timing->v_front_porch = edid_detailed->byte10.V_SYNC_OFFS_4_LOW + + ((uint32_t)edid_detailed->byte11.V_SYNC_OFFSET_2_HIGH << 4); + timing->v_sync_width = edid_detailed->byte10.V_SYNC_WIDTH_4_LOW + + ((uint32_t)edid_detailed->byte11.V_SYNC_WIDTH_2_HIGH << 4); + h_blank = edid_detailed->h_blank_8_low + + ((uint32_t)edid_detailed->byte4.H_BLANK_4_HIGH << 8); + v_blank = edid_detailed->v_blank_8_low + + ((uint32_t)edid_detailed->byte7.V_BLANK_4_HIGH << 8); + + /* We apply borders only to analog signal*/ + if (dal_edid_get_connector_type(edid) == EDID_CONNECTOR_ANALOG) { + + h_border = edid_detailed->h_border; + v_border = edid_detailed->v_border; + + /* EDID 1.3 Border is part of blank (implicit, need to compensate)*/ + /* EDID 1.4 Border is part of active (explicit, noneed to compensate)*/ + if (!border_active) { + timing->h_front_porch -= h_border; + timing->v_front_porch -= v_border; + h_blank -= (h_border * 2); + v_blank -= (v_border * 2); + } + } + + timing->h_border_left = h_border; + timing->h_border_right = h_border; + timing->v_border_top = v_border; + timing->v_border_bottom = v_border; + timing->h_total = timing->h_addressable + (2 * h_border) + h_blank; + timing->v_total = timing->v_addressable + (2 * v_border) + v_blank; + + timing->flags.INTERLACE = edid_detailed->flags.INTERLACED; + timing->flags.HSYNC_POSITIVE_POLARITY = + edid_detailed->flags.SYNC_3_LINES_OR_H_SYNC_P; + timing->flags.VSYNC_POSITIVE_POLARITY = + edid_detailed->flags.SERRATION_OR_V_SYNC_P; + + /* Double vectical sizes for interlaced mode + * (in Detailed Timing, interlaced vertical sizes are half)*/ + if (timing->flags.INTERLACE) { + timing->v_addressable *= 2; + timing->v_border_bottom *= 2; + timing->v_border_top *= 2; + timing->v_sync_width *= 2; + timing->v_front_porch = (timing->v_front_porch * 2) + 1; + timing->v_total = (timing->v_total * 2) + 1; + } + + /* Setup stereo 3D parameters*/ + stereo = edid_detailed->flags.STEREO_1_LOW + + (edid_detailed->flags.STEREO_2_HIGH << 1); + switch (stereo) { + case EDID_DETAILED_STEREO_FS_RIGHT_EYE: + timing->timing_3d_format = TIMING_3D_FORMAT_INBAND_FA; + timing->flags.EXCLUSIVE_3D = false; + timing->flags.RIGHT_EYE_3D_POLARITY = true; + break; + + case EDID_DETAILED_STEREO_FS_LEFT_EYE: + timing->timing_3d_format = TIMING_3D_FORMAT_INBAND_FA; + timing->flags.EXCLUSIVE_3D = false; + timing->flags.RIGHT_EYE_3D_POLARITY = false; + break; + + case EDID_DETAILED_STEREO_2WAYI_RIGHT_EYE: + timing->timing_3d_format = TIMING_3D_FORMAT_ROW_INTERLEAVE; + timing->flags.EXCLUSIVE_3D = false; + timing->flags.RIGHT_EYE_3D_POLARITY = true; + timing->flags.SUB_SAMPLE_3D = true; + break; + + case EDID_DETAILED_STEREO_2WAYI_LEFT_EYE: + timing->timing_3d_format = TIMING_3D_FORMAT_ROW_INTERLEAVE; + timing->flags.EXCLUSIVE_3D = false; + timing->flags.RIGHT_EYE_3D_POLARITY = false; + timing->flags.SUB_SAMPLE_3D = true; + break; + + case EDID_DETAILED_STEREO_4WAYI: + timing->timing_3d_format = TIMING_3D_FORMAT_PIXEL_INTERLEAVE; + timing->flags.EXCLUSIVE_3D = false; + /* A guess, since not really specified in EDID*/ + timing->flags.RIGHT_EYE_3D_POLARITY = true; + timing->flags.SUB_SAMPLE_3D = true; + break; + + default: + break; + } + +/* Check for CEA861 detailed timings in EDID base blocks for HDMI compliance*/ + if (dal_timing_service_is_timing_in_standard( + edid->ts, + timing, + TIMING_STANDARD_CEA861)) { + timing->timing_standard = TIMING_STANDARD_CEA861; + timing->vic = dal_timing_service_get_video_code_for_timing( + edid->ts, timing); + } else { + timing->timing_standard = TIMING_STANDARD_EXPLICIT; + } + + /* Check if the timing parsed has realistically valid values*/ + if (!dal_timing_service_are_timing_parameters_valid(timing)) { + BREAK_TO_DEBUGGER(); + return false; + } + + patch_porch_values_for_4k(timing); + return true; +} + +bool dal_edid_get_timing_for_vesa_mode( + struct edid_base *edid, + struct mode_info *mode_info, + struct crtc_timing *timing) +{ + bool res = false; + enum dcs_edid_connector_type connector_type = + dal_edid_get_connector_type(edid); + uint32_t edid_ver = dal_edid_get_version(edid); + enum timing_standard fallback_std; + + ASSERT(mode_info != NULL); + ASSERT(timing != NULL); + +/*By default use reduced blanking, unless we are using an anaogue display*/ + mode_info->flags.REDUCED_BLANKING = true; + + if (connector_type == EDID_CONNECTOR_UNKNOWN || + connector_type == EDID_CONNECTOR_ANALOG) + mode_info->flags.REDUCED_BLANKING = false; + + fallback_std = mode_info->flags.REDUCED_BLANKING ? + TIMING_STANDARD_CVT_RB : TIMING_STANDARD_CVT; + + /* + * EDID 1.3 or below: DMT (not-RB) and then GTF + * EDID 2.0: same as EDID1.3 + * EDID 1.4: DMT (not-RB) and then CVT (not-Rb, progressive, 0-borders) + */ + + if (edid_ver < EDID_VER_14 || edid_ver == EDID_VER_20) + if (connector_type == EDID_CONNECTOR_UNKNOWN || + connector_type == EDID_CONNECTOR_ANALOG) + fallback_std = TIMING_STANDARD_GTF; + + mode_info->timing_standard = TIMING_STANDARD_DMT; + res = dal_timing_service_get_timing_for_mode( + edid->ts, mode_info, timing); + + if (!res) { + mode_info->timing_standard = fallback_std; + res = dal_timing_service_get_timing_for_mode( + edid->ts, mode_info, timing); + + } + + if (res) + mode_info->timing_standard = timing->timing_standard; + + return res; + +} diff --git a/drivers/gpu/drm/amd/dal/dcs/edid/edid_base.h b/drivers/gpu/drm/amd/dal/dcs/edid/edid_base.h new file mode 100644 index 000000000000..5e7bd49ae60a --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/edid/edid_base.h @@ -0,0 +1,480 @@ +/* + * Copyright 2012-15 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 + * + */ +#ifndef __DAL_EDID_BASE_H__ +#define __DAL_EDID_BASE_H__ + +#include "include/timing_service_types.h" +#include "include/dcs_types.h" + +#define EDID_VER_1x_MASK 0x00000100 +#define EDID_VER_10 0x00000100 +#define EDID_VER_11 0x00000101 +#define EDID_VER_12 0x00000102 +#define EDID_VER_13 0x00000103 +#define EDID_VER_14 0x00000104 +#define EDID_VER_20 0x00000200 + +#define EDID_VER_1_STDLEN 128 +#define EDID_VER_2_STDLEN 256 + +#define MAX_EDID_BUFFER_SIZE 512 + +#define EDID_MIN_X_RES 320 +#define EDID_MIN_Y_RES 200 + +#define EDID_EXTENSION_TAG_LCD_TIMING 0x01 +#define EDID_EXTENSION_TAG_CEA861_EXT 0x02 +#define EDID_EXTENSION_TAG_VTB_EXT 0x10 +#define EDID_EXTENSION_TAG_EDID20_EXT 0x20 +#define EDID_EXTENSION_TAG_COLORINFO_TYPE 0x30 +/*Defined as EDID_EXTENSION_TAG_DVI_FEATURE_DATA in +Release A,Revision 1 February 9, 2000*/ +#define EDID_EXTENSION_TAG_DI_EXT 0x40 +/*Defined as EDID_EXTENSION_TAG_TOUCH_SCREENE_MAP in +Release A,Revision 1 February 9, 2000*/ +#define EDID_EXTENSION_TAG_LS_EXT 0x50 +#define EDID_EXTENSION_TAG_DPVL_EXT 0x60 +#define EDID_EXTENSION_TAG_DISPLAYID 0x70 +#define EDID_EXTENSION_TAG_BLOCK_MAP 0xF0 +#define EDID_EXTENSION_TAG_EXT_BY_MANUFACTURER 0xFF + +#define EDID_DISPLAY_NAME_SIZE 20 + + +enum edid_timing_generation_type { + EDID_TIMING_GENERATION_UNKNOWN, + EDID_TIMING_GENERATION_GTF, + EDID_TIMING_GENERATION_GTF2ND, + EDID_TIMING_GENERATION_CVT +}; + +struct edid_cvt_aspect_ratio { + uint32_t CVT_ASPECT_RATIO_4X3:1; + uint32_t CVT_ASPECT_RATIO_16X9:1; + uint32_t CVT_ASPECT_RATIO_16X10:1; + uint32_t CVT_ASPECT_RATIO_5X4:1; + uint32_t CVT_ASPECT_RATIO_15X9:1; +}; + +struct monitor_range_limits { + uint32_t min_v_rate_hz; + uint32_t max_v_rate_hz; + uint32_t min_h_rate_khz; + uint32_t max_h_rate_khz; + uint32_t max_pix_clk_khz; + enum edid_timing_generation_type generation_method; + +/*The following members are available when CVT iS supported*/ +/*CVT defines "active" as H_PIXELS_RND (addressable) plus the number of + pixels in left/right margins (borders)*/ + uint32_t max_h_active; + bool normal_blanking; + bool reduced_blanking; + struct edid_cvt_aspect_ratio cvt_aspect_ratio; +}; + +struct edid_detailed { + uint16_t pix_clk; + uint8_t pix_width_8_low; + uint8_t h_blank_8_low; + struct byte4 { + uint8_t H_BLANK_4_HIGH:4; + uint8_t PIX_WIDTH_4_HIGH:4; + } byte4; + uint8_t pix_height_8_low; + uint8_t v_blank_8_low; + struct byte7 { + uint8_t V_BLANK_4_HIGH:4; + uint8_t PIX_HEIGHT_4_HIGH:4; + } byte7; + uint8_t h_sync_offs_8_low; + uint8_t h_sync_width_8_low; + struct byte10 { + uint8_t V_SYNC_WIDTH_4_LOW:4; + uint8_t V_SYNC_OFFS_4_LOW:4; + } byte10; + struct byte11 { + uint8_t V_SYNC_WIDTH_2_HIGH:2; + uint8_t V_SYNC_OFFSET_2_HIGH:2; + uint8_t H_SYNC_WIDTH_2_HIGH:2; + uint8_t H_SYNC_OFFSET_2_HIGH:2; + } byte11; + uint8_t h_img_size_8_low; + uint8_t v_image_size_8_low; + struct byte14 { + uint8_t V_IMG_SIZE_4_HIGH:4; + uint8_t H_IMG_SIZE_4_HIGH:4; + } byte14; + uint8_t h_border; + uint8_t v_border; + struct flags { + uint8_t STEREO_1_LOW:1; + uint8_t SYNC_3_LINES_OR_H_SYNC_P:1; + uint8_t SERRATION_OR_V_SYNC_P:1; + uint8_t SYNC_TYPE:2; + uint8_t STEREO_2_HIGH:2; + uint8_t INTERLACED:1; + } flags; +}; + +/* Combined from Detailed Timing flag fields: (stereo2High << 1) + stereo1Low*/ +enum edid_detailed_stereo { + EDID_DETAILED_STEREO_2D = 0, + EDID_DETAILED_STEREO_2DX = 1,/* both 0 and 1 are 2D*/ + EDID_DETAILED_STEREO_FS_RIGHT_EYE = 2, + EDID_DETAILED_STEREO_2WAYI_RIGHT_EYE = 3, + EDID_DETAILED_STEREO_FS_LEFT_EYE = 4, + EDID_DETAILED_STEREO_2WAYI_LEFT_EYE = 5, + EDID_DETAILED_STEREO_4WAYI = 6, + EDID_DETAILED_STEREO_SIDE_BY_SIDE_I = 7 +}; + +struct edid_error { + bool BAD_CHECKSUM:1; + bool BAD_VERSION_NUM:1; + bool BAD_RANGE_LIMIT:1; + bool INVALID_CONNECTOR:1; + bool BAD_DESCRIPTOR_FIELD:1; + bool BAD_ESTABLISHED_III_FIELD:1; + bool BAD_18BYTE_STANDARD_FIELD:1; + bool BAD_RANGE_LIMIT_FIELD:1; + bool BAD_CVT_3BYTE_FIELD:1; + bool BAD_COLOR_ENCODING_FORMAT_FIELD:1; +}; + +/* Edid stereo 3D capabilities for Edid blocks*/ +/* Currently assumed Edid base block and DisplayId can have only + single 3D format supported*/ +/* Commented fields for future use*/ +struct edid_stereo_3d_capability { + enum timing_3d_format timing_3d_format; + /* relevant only if applyToAllTimings set */ + bool override_per_timing_format; + union { + struct frame_alternate_data { + /* polarity of inband/sideband signal */ + bool right_eye_polarity; + } frame_alternate_data; + struct interleaved_data { + /* i.e. right image pixel/row/column comes first */ + bool right_eye_polarity; + } interleaved_data; + + }; +}; + +struct dcs_mode_timing_list; +struct dcs_cea_audio_mode_list; +struct edid_base; + +struct edid_funcs { + void (*destroy)(struct edid_base **edid_base); + bool (*get_supported_mode_timing)( + struct edid_base *edid_base, + struct dcs_mode_timing_list *list, + bool *preferred_mode_found); + bool (*get_vendor_product_id_info)( + struct edid_base *edid_base, + struct vendor_product_id_info *info); + bool (*get_display_name)( + struct edid_base *edid_base, + uint8_t *name, + uint32_t name_size); + bool (*get_monitor_range_limits)( + struct edid_base *edid_base, + struct monitor_range_limits *limts); + bool (*get_display_characteristics)( + struct edid_base *edid_base, + struct display_characteristics *characteristics); + bool (*get_screen_info)( + struct edid_base *edid_base, + struct edid_screen_info *edid_screen_info); + bool (*get_connector_type)( + struct edid_base *edid_base, + enum dcs_edid_connector_type *type); + bool (*get_display_color_depth)( + struct edid_base *edid_base, + struct display_color_depth_support *color_depth); + bool (*get_display_pixel_encoding)( + struct edid_base *edid_base, + struct display_pixel_encoding_support *pixel_encoding); + bool (*get_cea861_support)( + struct edid_base *edid_base, + struct cea861_support *cea861_support); + bool (*get_cea_vendor_specific_data_block)( + struct edid_base *edid_base, + struct cea_vendor_specific_data_block *vendor_block); + bool (*get_cea_speaker_allocation_data_block)( + struct edid_base *edid_base, + union cea_speaker_allocation_data_block *spkr_data); + bool (*get_cea_colorimetry_data_block)( + struct edid_base *edid_base, + struct cea_colorimetry_data_block *colorimetry_data_block); + bool (*get_cea_video_capability_data_block)( + struct edid_base *edid_base, + union cea_video_capability_data_block + *video_capability_data_block); + bool (*get_cea_audio_modes)( + struct edid_base *edid_base, + struct dcs_cea_audio_mode_list *audio_list); + uint8_t (*get_edid_extension_tag)(struct edid_base *edid_base); + uint8_t (*num_of_extension)(struct edid_base *edid_base); + uint16_t (*get_version)(struct edid_base *edid_base); + const uint8_t* (*get_raw_data)(struct edid_base *edid_base); + const uint32_t (*get_raw_size)(struct edid_base *edid_base); + void (*validate)(struct edid_base *edid_base); + bool (*get_stereo_3d_support)( + struct edid_base *edid_base, + struct edid_stereo_3d_capability *stereo_capability); + bool (*is_non_continuous_frequency)(struct edid_base *edid_base); + uint32_t (*get_drr_pixel_clk_khz)(struct edid_base *edid_base); + uint32_t (*get_min_drr_fps)(struct edid_base *edid_base); + bool (*get_display_tile_info)( + struct edid_base *edid_base, + struct dcs_display_tile *display_tile); + +}; + +struct edid_base { + const struct edid_funcs *funcs; + struct edid_error error; + struct edid_base *next; + struct timing_service *ts; +}; + +void dal_edid_destroy(struct edid_base **edid); +void dal_edid_list_destroy(struct edid_base *edid); + +bool dal_edid_base_construct(struct edid_base *edid, struct timing_service *ts); +void dal_edid_base_destruct(struct edid_base *edid); + +bool dal_edid_base_get_supported_mode_timing( + struct edid_base *edid, + struct dcs_mode_timing_list *list, + bool *preferred_mode_found); + +bool dal_edid_get_supported_mode_timing( + struct edid_base *edid, + struct dcs_mode_timing_list *list, + bool *preferred_mode_found); + +bool dal_edid_base_get_vendor_product_id_info( + struct edid_base *edid, + struct vendor_product_id_info *info); + +bool dal_edid_base_get_display_name( + struct edid_base *edid, + uint8_t *name, + uint32_t name_size); + +bool dal_edid_base_get_monitor_range_limits( + struct edid_base *edid, + struct monitor_range_limits *limts); + +bool dal_edid_base_get_display_characteristics( + struct edid_base *edid, + struct display_characteristics *characteristics); + +bool dal_edid_get_screen_info( + struct edid_base *edid, + struct edid_screen_info *edid_screen_info); + +enum dcs_edid_connector_type dal_edid_get_connector_type( + struct edid_base *edid_list); + +bool dal_edid_base_get_display_color_depth( + struct edid_base *edid_list, + struct display_color_depth_support *color_depth); + +bool dal_edid_get_display_pixel_encoding( + struct edid_base *edid_list, + struct display_pixel_encoding_support *pixel_encoding); + +bool dal_edid_base_get_cea861_support( + struct edid_base *edid, + struct cea861_support *cea861_support); + +bool dal_edid_get_cea_vendor_specific_data_block( + struct edid_base *edid, + struct cea_vendor_specific_data_block *vendor_block); + +bool dal_edid_get_cea_speaker_allocation_data_block( + struct edid_base *edid, + union cea_speaker_allocation_data_block *spkr_data); + +bool dal_edid_get_cea_colorimetry_data_block( + struct edid_base *edid, + struct cea_colorimetry_data_block *colorimetry_data_block); + +bool dal_edid_base_get_cea_video_capability_data_block( + struct edid_base *edid, + union cea_video_capability_data_block *video_capability_data_block); + +bool dal_edid_base_get_cea_audio_modes( + struct edid_base *edid, + struct dcs_cea_audio_mode_list *audio_list); + +bool dal_edid_get_cea_audio_modes( + struct edid_base *edid, + struct dcs_cea_audio_mode_list *audio_list); + +uint8_t dal_edid_base_get_edid_extension_tag(struct edid_base *edid); + +uint8_t dal_edid_get_num_of_extension(struct edid_base *edid); + +uint16_t dal_edid_base_get_version(struct edid_base *edid); + +uint32_t dal_edid_get_size(struct edid_base *edid); + +const uint8_t *dal_edid_get_raw_data(struct edid_base *edid); + +void dal_edid_base_validate(struct edid_base *edid); + +bool dal_edid_get_stereo_3d_support( + struct edid_base *edid, + struct edid_stereo_3d_capability *stereo_capability); + +bool dal_edid_is_non_continous_frequency(struct edid_base *edid); + +uint32_t dal_edid_get_drr_pixel_clk_khz(struct edid_base *edid); + +uint32_t dal_edid_base_get_min_drr_fps(struct edid_base *edid); + +bool dal_edid_base_get_display_tile_info( + struct edid_base *edid, + struct dcs_display_tile *display_tile); + +struct edid_base *dal_edid_get_next_block(struct edid_base *edid); + +void dal_edid_set_next_block( + struct edid_base *edid, + struct edid_base *nxt_edid); + +struct edid_error *dal_edid_get_errors(struct edid_base *edid); + +bool dal_edid_validate_display_gamma(struct edid_base *edid, uint8_t gamma); + +uint8_t dal_edid_compute_checksum(struct edid_base *edid); + +void dal_edid_timing_to_mode_info( + const struct crtc_timing *timing, + struct mode_info *mode_info); + +bool dal_edid_detailed_to_timing( + struct edid_base *edid, + const struct edid_detailed *edid_detailed, + bool border_active, + struct crtc_timing *timing); + +bool dal_edid_get_timing_for_vesa_mode( + struct edid_base *edid, + struct mode_info *mode_info, + struct crtc_timing *timing); + +bool dal_edid_get_display_tile_info( + struct edid_base *edid, + struct dcs_display_tile *display_tile); + +uint32_t dal_edid_get_min_drr_fps(struct edid_base *edid); + +uint32_t dal_edid_base_get_drr_pixel_clk_khz(struct edid_base *edid); + +bool dal_edid_base_is_non_continuous_frequency(struct edid_base *edid); + +bool dal_edid_base_get_stereo_3d_support( + struct edid_base *edid, + struct edid_stereo_3d_capability *stereo_capability); + +void dal_edid_validate(struct edid_base *edid); + +uint16_t dal_edid_get_version(struct edid_base *edid); + +uint8_t dal_edid_base_num_of_extension(struct edid_base *edid); + +bool dal_edid_base_get_screen_info( + struct edid_base *edid, + struct edid_screen_info *edid_screen_info); + +bool dal_edid_get_display_characteristics( + struct edid_base *edid, + struct display_characteristics *characteristics); + +bool dal_edid_get_monitor_range_limits( + struct edid_base *edid, + struct monitor_range_limits *limts); + +bool dal_edid_base_get_vendor_product_id_info( + struct edid_base *edid, + struct vendor_product_id_info *info); + +bool dal_edid_get_cea861_support( + struct edid_base *edid, + struct cea861_support *cea861_support); + +uint8_t dal_edid_get_edid_extension_tag(struct edid_base *edid); + +bool dal_edid_get_cea_audio_modes( + struct edid_base *edid, + struct dcs_cea_audio_mode_list *audio_list); + +bool dal_edid_base_get_display_pixel_encoding( + struct edid_base *edid, + struct display_pixel_encoding_support *pixel_encoding); + +bool dal_edid_get_cea_video_capability_data_block( + struct edid_base *edid, + union cea_video_capability_data_block + *video_capability_data_block); + +bool dal_edid_base_get_cea_colorimetry_data_block( + struct edid_base *edid, + struct cea_colorimetry_data_block *colorimetry_data_block); + +bool dal_edid_base_get_cea_speaker_allocation_data_block( + struct edid_base *edid, + union cea_speaker_allocation_data_block *spkr_data); + +bool dal_edid_base_get_cea_vendor_specific_data_block( + struct edid_base *edid, + struct cea_vendor_specific_data_block *vendor_block); + +bool dal_edid_get_display_color_depth( + struct edid_base *edid, + struct display_color_depth_support *color_depth); + +bool dal_edid_base_get_connector_type( + struct edid_base *edid, + enum dcs_edid_connector_type *type); + +bool dal_edid_get_vendor_product_id_info( + struct edid_base *edid, + struct vendor_product_id_info *info); + +bool dal_edid_get_display_name( + struct edid_base *edid, + uint8_t *name, + uint32_t name_size); + +#endif /* __DAL_EDID_BASE_H__ */ diff --git a/drivers/gpu/drm/amd/dal/dcs/edid/edid_ext_cea.c b/drivers/gpu/drm/amd/dal/dcs/edid/edid_ext_cea.c new file mode 100644 index 000000000000..3cd5877d59cd --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/edid/edid_ext_cea.c @@ -0,0 +1,1758 @@ +/* + * Copyright 2012-15 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 + * + */ + +#include "dal_services.h" +#include "include/dcs_interface.h" +#include "include/timing_service_interface.h" +#include "edid_base.h" +#include "edid_ext_cea.h" +#include "../edid_patch.h" + +#define MAX_NUM_OF_DETAILED_TIMING_DESC_IN_CEA_861 6 +#define CEA861_MAX_DATA_BLOKS_SIZE 123 + +#define CEA861_VERSION_1 0x01 +#define CEA861_VERSION_A 0x02 +#define CEA861_VERSION_B 0x03 + +/** + * cea861 feature (CEA861B or later) + */ +#define CEA_861B_EXT_BYTE3_YCRCB422_SUPPORTED 0x10 +#define CEA_861B_EXT_BYTE3_YCRCB444_SUPPORTED 0x20 +#define CEA_861B_EXT_BYTE3_BASICAUDIO_SUPPORTED 0x40 +#define CEA_861B_EXT_BYTE3_UNDERSCAN_SUPPORTED 0x80 +#define CEA_861B_EXT_BYTE3_NATIVE_COUNT_MASK 0x0F + +/** + * CEA 861B Data block collection General Tag Format + */ +#define CEA_861B_GENERAL_TAG_FORMAT_TAG_CODE_MASK 0xE0 +#define CEA_861B_GENERAL_TAG_FORMAT_TAG_CODE_SHIFT 0x5 +#define CEA_861B_GENERAL_TAG_FORMAT_DATA_BLOCK_COUNT_MASK 0x1F +/** + * CEA 861B Data Block Tag Codes + */ +#define CEA_861B_DATA_BLOCK_TAGCODE_SHORT_AUDIO_DESCRIPTOR 0x1 +#define CEA_861B_DATA_BLOCK_TAGCODE_SHORT_VIDEO_DESCRIPTOR 0x2 +#define CEA_861B_DATA_BLOCK_TAGCODE_VENDOR_SPECIFIC_DATA_BLOCK 0x3 +#define CEA_861B_DATA_BLOCK_TAGCODE_SPKR_ALLOCATION_DATA_BLOCK 0x4 +#define CEA_861B_DATA_BLOCK_TAGCODE_VESA_DTC_DATA_BLOCK 0x5 +#define CEA_861B_DATA_BLOCK_TAGCODE_RESERVED 0x6 +/*redirect to extended tag block*/ +#define CEA_861B_DATA_BLOCK_TAGCODE_USE_EXTENDED_TAG 0x7 + +/** + * CEA 861B short video descriptor related + */ +#define CEA_SHORT_VIDEO_DESCRIPTION_NATIVE_MASK 0x80 +#define CEA_SHORT_VIDEO_DESCRIPTION_VIDEO_ID_CODE_MASK 0x7F + +/** + * CEA 861B Extended Data Block Tag Codes + */ +/*when CEA_861B_DATA_BLOCK_TAGCODE_USE_EXTEDNED_TAG not set.*/ +#define CEA_861B_DATA_BLOCK_EXT_TAGCODE_NONE 0x0 +#define CEA_861B_DATA_BLOCK_EXT_TAGCODE_VIDEO_CAPABILITY_DATA_BLOCK 0x0 +#define CEA_861B_DATA_BLOCK_EXT_TAGCODE_VENDOR_SPECIFIC_VIDEO_DATA_BLOCK 0x1 +/*reserved for now*/ +#define CEA_861B_DATA_BLOCK_EXT_TAGCODE_VESA_VIDEO_DISPLAY_DEVICE_INFO_BLOCK 0x2 +/* reserved for now */ +#define CEA_861B_DATA_BLOCK_EXT_TAGCODE_VESA_VIDEO_DATA_BLOCK 0x3 +/* reserved for now */ +#define CEA_861B_DATA_BLOCK_EXT_TAGCODE_VESA_VIDEO_DATA_BLOCK1 0x4 +#define CEA_861B_DATA_BLOCK_EXT_TAGCODE_COLORIMETRY_DATA_BLOCK 0x5 + +#define CEA_861B_DATA_BLOCK_EXT_TAGCODE_CEA_MISC_AUDIO_FIELDS 0x16 +#define CEA_861B_DATA_BLOCK_EXT_TAGCODE_VENDOR_SPECIFIC_AUDIO_DATA_BLOCK 0x17 +/* reserved for now */ +#define CEA_861B_DATA_BLOCK_EXT_TAGCODE_HDMI_AUDIO_DATA_BLOCK 0x18 +#define CEA_861B_DATA_BLOCK_EXT_TAGCODE_MASK 0xFF + +/** + * cea861 extension structure + */ +struct edid_data_cea861_ext { + uint8_t extension_tag; + uint8_t revision; + uint8_t timing_data_start; + uint8_t cea861b_byte3; + uint8_t data_block[CEA861_MAX_DATA_BLOKS_SIZE]; + uint8_t checksum; +}; + +#define DAL_PROGRESSIVE 0x0 +#define DAL_INTERLACED 0x1 + +#define ASPECTRATIO_16_9 0x1 +#define ASPECTRATIO_4_3 0x2 +/* nunmber of entries in CEA-861-E spec */ +#define MAX_NUM_SHORT_VIDEO_DESCRIPTOR_FORMAT 63 + +/** + * CEA_AUDIO_MODE ucFormatCode defines + */ +#define CEA_861B_SHORT_AUDIO_FORMAT_CODE_RESERVED0 0x0 +#define CEA_861B_SHORT_AUDIO_FORMAT_CODE_LINEAR_PCM 0x1 +#define CEA_861B_SHORT_AUDIO_FORMAT_CODE_AC_3 0x2 +#define CEA_861B_SHORT_AUDIO_FORMAT_CODE_MPEG1 0x3 +#define CEA_861B_SHORT_AUDIO_FORMAT_CODE_MP3 0x4 +#define CEA_861B_SHORT_AUDIO_FORMAT_CODE_MPEG2 0x5 +#define CEA_861B_SHORT_AUDIO_FORMAT_CODE_AAC 0x6 +#define CEA_861B_SHORT_AUDIO_FORMAT_CODE_DTS 0x7 +#define CEA_861B_SHORT_AUDIO_FORMAT_CODE_ATRAC 0x8 +#define CEA_861B_SHORT_AUDIO_FORMAT_CODE_ONE_BIT_AUDIO 0x9 +#define CEA_861B_SHORT_AUDIO_FORMAT_CODE_DOLBY_DIGITAL_PLUS 0xA +#define CEA_861B_SHORT_AUDIO_FORMAT_CODE_DTS_HD 0xB +#define CEA_861B_SHORT_AUDIO_FORMAT_CODE_MAT_MLP 0xC +#define CEA_861B_SHORT_AUDIO_FORMAT_CODE_DST 0xD +#define CEA_861B_SHORT_AUDIO_FORMAT_CODE_WMA_PRO 0xE +#define CEA_861B_SHORT_AUDIO_FORMAT_CODE_RESERVEDF 0xF + +#define CEA_861B_SHORT_AUDIO_FORMAT_CODE_MASK 0x78 +#define CEA_861B_SHORT_AUDIO_FORMAT_CODE_SHIFT 0x3 + +#define CEA_861B_SHORT_AUDIO_FORMAT_CODE_MAX_CHANNEL_MINUS_ONE_MASK 0x7 +#define CEA_861B_SHORT_AUDIO_FORMAT_CODE_MAX_CHANNEL_MINUS_ONE_SHIFT 0x0 +#define CEA_861B_SHORT_AUDIO_SAMPLE_RATE_MASK 0x7F + +/** + * CEA_AUDIO_MODE bv8SampleRate defines + */ +#define CEA_861B_SHORT_AUDIO_SUPPORTED_FREQUENCY_32 0x01 +#define CEA_861B_SHORT_AUDIO_SUPPORTED_FREQUENCY_44_1 0x02 +#define CEA_861B_SHORT_AUDIO_SUPPORTED_FREQUENCY_48 0x04 +#define CEA_861B_SHORT_AUDIO_SUPPORTED_FREQUENCY_88_2 0x08 +#define CEA_861B_SHORT_AUDIO_SUPPORTED_FREQUENCY_96 0x10 +#define CEA_861B_SHORT_AUDIO_SUPPORTED_FREQUENCY_176_4 0x20 +#define CEA_861B_SHORT_AUDIO_SUPPORTED_FREQUENCY_192 0x40 + +/** + * CEA_SPEAKER_ALLOCATION ucData[0] defines + */ +#define CEA_SPEAKER_ALLOCATION_SPEAKER_PRESENT_FL_FR 0x01 +#define CEA_SPEAKER_ALLOCATION_SPEAKER_PRESENT_LFE 0x02 +#define CEA_SPEAKER_ALLOCATION_SPEAKER_PRESENT_FC 0x04 +#define CEA_SPEAKER_ALLOCATION_SPEAKER_PRESENT_RL_RR 0x08 +#define CEA_SPEAKER_ALLOCATION_SPEAKER_PRESENT_RC 0x10 +#define CEA_SPEAKER_ALLOCATION_SPEAKER_PRESENT_FLC_FRC 0x20 +#define CEA_SPEAKER_ALLOCATION_SPEAKER_PRESENT_RLC_RRC 0x40 + +#define CEA_861C_COLORIMETRY_SUPPORT_FLAG_NONE 0x00 +#define CEA_861C_COLORIMETRY_SUPPORT_FLAG_XVYCC_601 0x01 +#define CEA_861C_COLORIMETRY_SUPPORT_FLAG_XVYCC_709 0x02 +#define CEA_861C_COLORIMETRY_SUPPORT_FLAG_XVYCC_MASK 0x03 +#define CEA_861E_COLORIMETRY_SUPPORT_FLAG_SYCC_601 0x04 +#define CEA_861E_COLORIMETRY_SUPPORT_FLAG_ADOBE_YCC601 0x08 +#define CEA_861E_COLORIMETRY_SUPPORT_FLAG_ADOBE_RGB 0x10 + +#define CEA_861C_COLORIMETRY_SUPPORT_FLAG_GAMUT_NONE 0x0 +#define CEA_861C_COLORIMETRY_SUPPORT_FLAG_GAMUT_M0 0x1 +#define CEA_861C_COLORIMETRY_SUPPORT_FLAG_GAMUT_M1 0x2 +#define CEA_861C_COLORIMETRY_SUPPORT_FLAG_GAMUT_M2 0x4 +#define CEA_861C_COLORIMETRY_SUPPORT_FLAG_GAMUT_M3 0x8 +#define CEA_861C_COLORIMETRY_SUPPORT_FLAG_GAMUT_MASK 0xf + +/** + * CEA VIDEO CAPABILITY DATA BLCOK (VCDB) + */ +/* CE VIDEO format */ +#define CEA_861C_DATA_BLOCK__VCDB__S_CE0 0x01 +#define CEA_861C_DATA_BLOCK__VCDB__S_CE1 0x02 +#define CEA_861C_DATA_BLOCK__VCDB__S_CE_MASK 0x03 + +/* IT video format */ +#define CEA_861C_DATA_BLOCK__VCDB__S_IT0 0x04 +#define CEA_861C_DATA_BLOCK__VCDB__S_IT1 0x08 +#define CEA_861C_DATA_BLOCK__VCDB__S_IT_MASK 0x0C +#define CEA_861C_DATA_BLOCK__VCDB__S_IT_SHIFT 0x02 + +/* Prefered video formam */ +#define CEA_861C_DATA_BLOCK__VCDB__S_PT0 0x10 +#define CEA_861C_DATA_BLOCK__VCDB__S_PT1 0x20 +#define CEA_861C_DATA_BLOCK__VCDB__S_PT_MASK 0x30 +#define CEA_861C_DATA_BLOCK__VCDB__S_PT_SHIFT 0x04 + +/* Quantization */ +#define CEA_861C_DATA_BLOCK__VCDB__QS 0x40 +#define CEA_861C_DATA_BLOCK__EXTENDED_TAGCODE_MASK 0xFF + +/** + * HDMI Vendor specific data block + */ +#define HDMI_VENDOR_SPECIFIC_DATA_BLOCK_IEEE_REGISTRATION_ID 0x000C03 + +/* Byte 6 */ +#define HDMI_VENDOR_SPECIFIC_DATA_BLOCK_SUPPORTED_AI 0x80 +#define HDMI_VENDOR_SPECIFIC_DATA_BLOCK_SUPPORTED_DC_48BIT 0x40 +#define HDMI_VENDOR_SPECIFIC_DATA_BLOCK_SUPPORTED_DC_36BIT 0x20 +#define HDMI_VENDOR_SPECIFIC_DATA_BLOCK_SUPPORTED_DC_30BIT 0x10 +#define HDMI_VENDOR_SPECIFIC_DATA_BLOCK_SUPPORTED_DC_Y444 0x08 +#define HDMI_VENDOR_SPECIFIC_DATA_BLOCK_SUPPORTED_DUALDVI 0x01 + +/* Byte 8 */ +#define HDMI_VENDOR_SPECIFIC_DATA_BLOCK_LATENCY_PRESENT 0x80 +#define HDMI_VENDOR_SPECIFIC_DATA_BLOCK_I_LATENCY_PRESENT 0x40 +#define HDMI_VENDOR_SPECIFIC_DATA_BLOCK_HDMI_VIDEO_PRESENT 0x20 +#define HDMI_VENDOR_SPECIFIC_DATA_BLOCK_CNC3_GAME 0x08 +#define HDMI_VENDOR_SPECIFIC_DATA_BLOCK_CNC2_CINEMA 0x04 +#define HDMI_VENDOR_SPECIFIC_DATA_BLOCK_CNC1_PHOTO 0x02 +#define HDMI_VENDOR_SPECIFIC_DATA_BLOCK_CNC0_GRAPHICS 0x01 + +/* Byte 9~13 */ +/* exact location of this byte depends in preceeding optional fields +like latency */ +#define HDMI_VENDOR_SPECIFIC_DATA_BLOCK_3D_PRESENT 0x80 +#define HDMI_VENDOR_SPECIFIC_DATA_BLOCK_3D_PRESENT_SHIFT 0x7 +#define HDMI_VENDOR_SPECIFIC_DATA_BLOCK_3D_MULTI_PRESENT_MASK 0x60 /*2 bits*/ +#define HDMI_VENDOR_SPECIFIC_DATA_BLOCK_3D_MULTI_PRESENT_SHIFT 0x5 +#define HDMI_VENDOR_SPECIFIC_DATA_BLOCK_IMAGE_SIZE_MASK 0x18 /* 2 bits*/ +#define HDMI_VENDOR_SPECIFIC_DATA_BLOCK_IMAGE_SIZE_SHIFT 0x3 + +/* Byte that in the VSDB follows "flags byte" which defined right above +in this source file*/ +#define HDMI_VENDOR_SPECIFIC_DATA_BLOCK_VIC_LEN_MASK 0xE0 /* 3 bits */ +#define HDMI_VENDOR_SPECIFIC_DATA_BLOCK_VIC_LEN_SHIFT 0x5 +#define HDMI_VENDOR_SPECIFIC_DATA_BLOCK_3D_LEN_MASK 0x1F /* 5 bits */ + +/* Byte 4 and 5 */ +#define HDMI_VENDOR_SPECIFIC_DATA_BLOCK_COMPONENT_PHY_ADDR_MASK_HIGH 0xF0 +#define HDMI_VENDOR_SPECIFIC_DATA_BLOCK_COMPONENT_PHY_ADDR_MASK_LOW 0x0F +#define HDMI_VENDOR_SPECIFIC_DATA_BLOCK_COMPONENT_PHY_ADDR_SHIFT 0x4 + +#define HDMI_VENDOR_SPECIFIC_DATA_BLOCK_3D_STRUCTURE_ALL_SHIFT 0x8 +#define HDMI_VENDOR_SPECIFIC_DATA_BLOCK_3D_MASK_SHIFT 0x8 + +#define HDMI_VENDOR_SPECIFIC_DATA_BLOCK_2D_VIC_ORDER_SHIFT 0x4 +#define HDMI_VENDOR_SPECIFIC_DATA_BLOCK_3D_DETAIL_SHIFT 0x4 +#define HDMI_VENDOR_SPECIFIC_DATA_BLOCK_2D_VIC_ORDER_MASK 0xF0 +#define HDMI_VENDOR_SPECIFIC_DATA_BLOCK_3D_STRUCTURE_MASK 0xF +#define HDMI_VENDOR_SPECIFIC_DATA_BLOCK_3D_DETAIL_MASK 0xF0 +#define HDMI_VENDOR_SPECIFIC_DATA_BLOCK_RESERVED_MASK 0xF + +struct speaker_allocation_data_block { + uint8_t spkr_data[3]; +}; + +struct colorimetry_data_block { + uint8_t byte3; /* bit 0~1 contains the setting for xvYCCC*/ + uint8_t byte4; /* bit ~4 contains the setting for MD0~MD3*/ +}; + +struct video_capability_data_block { + uint8_t byte3; +}; + +struct short_descr_info { + uint32_t offset; + uint32_t len; +}; + +struct latency_field { + bool valid; + uint8_t video_latency; + uint8_t audio_latency; +}; + +struct latency_fields { + struct latency_field latency; + struct latency_field i_latency; +}; + +enum stereo_3d_all_support { + STEREO_3D_ALL_FRAME_PACKING = 0, + STEREO_3D_ALL_TOP_AND_BOTTOM = 6, + STEREO_3D_ALL_SIDE_BY_SIDE_HALF = 8 +}; + + +struct cea_3d_format_support { + bool FRAME_PACKING:1; + bool SIDE_BY_SIDE_HALF:1; + bool TOP_AND_BOTTOM:1; +}; + +static const struct cea_3d_format_support + cea_mandatory_3d_format_supported[] = { + + { false, false, false },/* VIC 1*/ + { false, false, false },/* VIC 2*/ + { false, false, false },/* VIC 3*/ + /*VIC 4 (Frame Packing, Top-and-Bottom) 1280x720@60 (59.94)*/ + { true, false, true },/*VIC 4*/ + /* VIC 5 (Side-by-Side) 1920x1080i@60 (59.94) */ + { false, true, false },/* VIC 5*/ + { false, false, false },/* VIC 6*/ + { false, false, false },/* VIC 7*/ + { false, false, false },/* VIC 8*/ + { false, false, false },/* VIC 9*/ + + { false, false, false },/* VIC 10*/ + { false, false, false },/* VIC 11*/ + { false, false, false },/* VIC 12*/ + { false, false, false },/* VIC 13*/ + { false, false, false },/* VIC 14*/ + { false, false, false },/* VIC 15*/ + { false, false, false },/* VIC 16*/ + { false, false, false },/* VIC 17*/ + { false, false, false },/* VIC 18*/ + /* VIC 19 (Frame Packing, Top-and-Bottom) 1280x720p@50*/ + { true, false, true},/* VIC 19*/ + /* VIC 20 (Side-by-Side) 1920x1080i@50 */ + { false, true, false },/* VIC 20*/ + { false, false, false },/* VIC 21*/ + { false, false, false },/* VIC 22*/ + { false, false, false },/* VIC 23*/ + { false, false, false },/* VIC 24*/ + { false, false, false },/* VIC 25*/ + { false, false, false },/* VIC 26*/ + { false, false, false },/* VIC 27*/ + { false, false, false },/* VIC 28*/ + { false, false, false },/* VIC 29*/ + + { false, false, false },/* VIC 30*/ + { false, false, false },/* VIC 31*/ + /*VIC 32 (Frame Packing, Top-and-Bottom) 1920x1080@24 (23.98)*/ + { true, false, true },/*VIC 32*/ + { false, false, false },/* VIC 33*/ + { false, false, false },/* VIC 34*/ + { false, false, false },/* VIC 35*/ + { false, false, false },/* VIC 36*/ + { false, false, false },/* VIC 37*/ + { false, false, false },/* VIC 38*/ + { false, false, false },/* VIC 39*/ + + { false, false, false },/* VIC 40*/ + { false, false, false },/* VIC 41*/ + { false, false, false },/* VIC 42*/ + { false, false, false },/* VIC 43*/ + { false, false, false },/* VIC 44*/ + { false, false, false },/* VIC 45*/ + { false, false, false },/* VIC 46*/ + { false, false, false },/* VIC 47*/ + { false, false, false },/* VIC 48*/ + { false, false, false },/* VIC 49*/ + + { false, false, false },/* VIC 50*/ + { false, false, false },/* VIC 51*/ + { false, false, false },/* VIC 52*/ + { false, false, false },/* VIC 53*/ + { false, false, false },/* VIC 54*/ + { false, false, false },/* VIC 55*/ + { false, false, false },/* VIC 56*/ + { false, false, false },/* VIC 57*/ + { false, false, false },/* VIC 58*/ + { false, false, false },/* VIC 59*/ + + { false, false, false },/* VIC 60*/ + { false, false, false },/* VIC 61*/ + { false, false, false }, /* VIC 62*/ + + /*63~127 Reserved for future.*/ +}; + +static const struct cea_3d_format_support cea_all_3d_format_supported[] = { + + { false, false, false },/* VIC 1 */ + { false, false, false },/* VIC 2 */ + { false, false, false },/* VIC 3 */ + /* VIC 4 (Frame Packing(M), Side-by-Side(P), + Top-and-Bottom(M)) 1280x720@60 (59.94)*/ + { true, true, true },/* VIC 4*/ + /* VIC 5 (Frame Packing(P), Side-by-Side(M)) 1920x1080i@60 (59.94)*/ + { true, true, false },/* VIC 5*/ + { false, false, false },/* VIC 6*/ + { false, false, false },/* VIC 7*/ + { false, false, false },/* VIC 8*/ + { false, false, false },/* VIC 9*/ + + { false, false, false },/* VIC 10*/ + { false, false, false },/* VIC 11*/ + { false, false, false },/* VIC 12*/ + { false, false, false },/* VIC 13*/ + { false, false, false },/* VIC 14*/ + { false, false, false },/* VIC 15*/ + /* VIC 16 (Frame Packing, Side-by-Side, + Top-and-Bottom(P)) 1920x1080p@60 (59.94)*/ + { true, true, true },/* VIC 16*/ + { false, false, false },/* VIC 17*/ + { false, false, false },/* VIC 18*/ + /* VIC 19 (Frame Packing(M), Side-by-Side(P), + Top-and-Bottom(M)) 1280x720p@50*/ + { true, true, true },/* VIC 19*/ + + /* VIC 20 (Frame Packing(P), Side-by-Side(M)) 1920x1080i@50*/ + { true, true, false },/* VIC 20*/ + { false, false, false },/* VIC 21*/ + { false, false, false },/* VIC 22*/ + { false, false, false },/* VIC 23*/ + { false, false, false },/* VIC 24*/ + { false, false, false },/* VIC 25*/ + { false, false, false },/* VIC 26*/ + { false, false, false },/* VIC 27*/ + { false, false, false },/* VIC 28*/ + { false, false, false },/* VIC 29*/ + + { false, false, false },/* VIC 30*/ + /* VIC 31 (Frame Packing, Top-and-Bottom(P)) 1920x1080p@50*/ + { true, false, true },/* VIC 31*/ + /* VIC 32 (Frame Packing(M), Side-by-Side(P), + Top-and-Bottom(M)) 1920x1080@24 (23.98)*/ + { true, true, true },/* VIC 32*/ + { false, false, false },/* VIC 33*/ + /* VIC 34 (Frame Packing(P), Top-and-Bottom(P)) 1920x1080p@30 (29.97)*/ + { true, false, true },/* VIC 34 */ + { false, false, false },/* VIC 35*/ + { false, false, false },/* VIC 36*/ + { false, false, false },/* VIC 37*/ + { false, false, false },/* VIC 38*/ + { false, false, false },/* VIC 39*/ + + { false, false, false },/* VIC 40*/ + { false, false, false },/* VIC 41*/ + { false, false, false },/* VIC 42*/ + { false, false, false },/* VIC 43*/ + { false, false, false },/* VIC 44*/ + { false, false, false },/* VIC 45*/ + { false, false, false },/* VIC 46*/ + /* VIC 47 (Frame Packing) 1280x720p@120 (119.88)*/ + { true, false, false },/* VIC 47*/ + { false, false, false },/* VIC 48*/ + { false, false, false },/* VIC 49*/ + + { false, false, false },/* VIC 50*/ + { false, false, false },/* VIC 51*/ + { false, false, false },/* VIC 52*/ + { false, false, false },/* VIC 53*/ + { false, false, false },/* VIC 54*/ + { false, false, false },/* VIC 55*/ + { false, false, false },/* VIC 56*/ + { false, false, false },/* VIC 57*/ + { false, false, false },/* VIC 58*/ + { false, false, false },/* VIC 59*/ + + /* VIC 60 (Frame Packing(P)) 1280x720p@24 (23.98)*/ + { true, false, false },/* VIC 60*/ + { false, false, false },/* VIC 61 */ + /* VIC 62 (Frame Packing(P)) 1280x720p@30 (29.97)*/ + { true, false, false },/* VIC 62*/ + /*63~127 Reserved for future.*/ +}; + +struct cea_extended_3d_support { + uint8_t CODE:4; + uint8_t VIC_INDEX:4; + uint8_t RESERVED:4; + uint8_t DETAIL:4; +}; + +struct additional_video_fields { + bool valid; + struct cea_hdmi_vsdb_extended_caps cea_hdmi_vsdb_ext_cap; + struct cea_3d_format_support stereo_3D_all_support; + uint16_t stereo_3d_mask; + uint32_t hdmi_3d_offset; + uint32_t hdmi_3d_ext_len; + struct stereo_3d_extended_support + stereo_3d_ext_support[MAX_NUM_OF_HDMI_VSDB_3D_EXTENDED_SUPPORT]; + enum cea_hdmi_vic hdmi_vic[MAX_NUM_OF_HDMI_VSDB_VICS]; +}; + +struct vendor_specific_data_block { + uint8_t ieee_id[3]; + /*4*4 bits physical address.*/ + uint8_t commonent_phy_addr[2]; + + uint8_t byte6; +/*bit 7 contains the SUPPORTS_AI, 1 - the sink accepts ACP,ISRC1, ISRC2*/ +/*bits 3-6 declare supports of different pixel format*/ +/*bit 0 declare support of DVI dual-link*/ + uint8_t max_tmds_clk; + uint8_t byte8; +/*if bit 7 set - Video/Audio latency fields present*/ +/* if bit 6 set - two pairs of Video/Audio latency fields present - +progressive & interlaced*/ +/*if bit 5 set - additional video format capabilities are present, +which are described by the fields following the latency ones*/ +/*bits 0-3 indicate which content type is supported*/ + uint32_t video_latency; + uint32_t audio_latency; + uint32_t i_video_latency; + uint32_t i_audio_latency; + + struct additional_video_fields video_fields; +}; + +struct edid_ext_cea { + struct edid_base edid; + struct edid_data_cea861_ext *data; + bool mandatory_3d_support; + enum stereo_3d_multi_presence multi_3d_support; + struct cea_3d_format_support + cached_multi_3d_support[MAX_NUM_OF_HDMI_VSDB_3D_MULTI_SUPPORT]; + struct stereo_3d_extended_support + cached_ext_3d_support[MAX_NUM_OF_HDMI_VSDB_3D_EXTENDED_SUPPORT]; + struct edid_patch *edid_patch; +}; + +#define FROM_EDID(e) container_of((e), struct edid_ext_cea, edid) + +#define CEA_861B_DATA_BLOCK_START 0X04 +#define CEA_861B_GET_DATA_BLOCK_TAG(data) (data >> 5) +#define CEA_861B_GET_DATA_BLOCK_LEN(data) (data & 0x1F) + +bool dal_edid_ext_cea_is_cea_ext(uint32_t len, const uint8_t *buf) +{ + const struct edid_data_cea861_ext *ext; + + if (len < sizeof(struct edid_data_cea861_ext)) + return false; /* CEA extension is 128 byte in length*/ + + ext = (const struct edid_data_cea861_ext *)buf; + + if (!(EDID_EXTENSION_TAG_CEA861_EXT == ext->extension_tag)) + return false; /* Tag says it's not CEA ext*/ + + return true; +} + +static bool find_short_descr( + const struct edid_data_cea861_ext *data, + uint8_t start_offs, + uint8_t tag, + uint8_t ext_tag, + struct short_descr_info *descr) +{ +/* initial setup end of descriptor data at end of the extension block*/ + uint8_t descr_end = CEA861_MAX_DATA_BLOKS_SIZE - 1; + uint8_t i = start_offs; + + ASSERT(data != NULL); + ASSERT(descr != NULL); + ASSERT( + data->timing_data_start >= CEA_861B_DATA_BLOCK_START || + data->timing_data_start == 0); + + if (data->timing_data_start >= CEA_861B_DATA_BLOCK_START) + descr_end = data->timing_data_start - CEA_861B_DATA_BLOCK_START; + + while (i < descr_end) { + uint8_t data_block_tag = CEA_861B_GET_DATA_BLOCK_TAG( + data->data_block[i]); + uint8_t data_block_len = CEA_861B_GET_DATA_BLOCK_LEN( + data->data_block[i]); + + if (tag == CEA_861B_DATA_BLOCK_TAGCODE_USE_EXTENDED_TAG) { + + uint8_t data_block_ext_tag = data->data_block[i+1]; + + if (data_block_ext_tag == ext_tag) { + /* block length includes extended tag byte*/ + descr->len = data_block_len - 1; + /* i point to the block tag, + i+1 is extended tag byte, + so i+2 is extended data block*/ + descr->offset = i + 2; + return true; + } + } else { + if (data_block_tag == tag) { + descr->len = data_block_len; + /*i point to the block tag so i+1 is the 1st + short descriptor*/ + descr->offset = i + 1; + return true; + } + } + + /*next descriptor block starts at block length + 1 + to account for first tag byte*/ + i += data_block_len + 1; + } + return false; +} + +static bool add_detailed_timings( + struct edid_ext_cea *edid, + struct dcs_mode_timing_list *list, + bool *preferred_mode_found) +{ + bool ret = false; + uint32_t i; + struct mode_timing mode_timing; + const struct edid_detailed *detailed; + uint32_t num_of_detailed; + + ASSERT(list != NULL); + ASSERT(preferred_mode_found != NULL); + + /* Get detailed timing from CEA861 extension*/ + if (edid->data->timing_data_start < 4) + return false; + + /* Some HDMI displays report the wrong total number of DTDs. + To avoid missing some included DTDs we are using check that we are not + accessing the EDID block out of range.The last byte of the i-th + detailed timing should be within the CEA block*/ + + num_of_detailed = (sizeof(struct edid_data_cea861_ext) - + edid->data->timing_data_start) / sizeof(struct edid_detailed); + + detailed = (const struct edid_detailed *) + ((uint8_t *)edid->data + edid->data->timing_data_start); + + for (i = 0; i < num_of_detailed; ++i) { + + dal_memset(&mode_timing, 0, sizeof(struct mode_timing)); + + if (!dal_edid_detailed_to_timing( + &edid->edid, + &detailed[i], + true, + &mode_timing.crtc_timing)) + continue; + + dal_edid_timing_to_mode_info(&mode_timing.crtc_timing, + &mode_timing.mode_info); + + if (mode_timing.mode_info.flags.INTERLACE + && mode_timing.mode_info.pixel_width == 1440 + && mode_timing.mode_info.pixel_height == 480) { + /* + * Detailed Timing has no way of specifying + * pixelRepetition here we check if the Timing just + * parsed is 480i pixelRepetion. if so we adjust the + * ModeTiming accordingly*/ + mode_timing.mode_info.pixel_width /= 2; + mode_timing.crtc_timing.flags.PIXEL_REPETITION = 2; + } + + /* default to RGB 8bit */ + mode_timing.crtc_timing.display_color_depth = + DISPLAY_COLOR_DEPTH_888; + mode_timing.crtc_timing.pixel_encoding = PIXEL_ENCODING_RGB; + + /*If preferred mode yet not found - + * select first detailed mode/timing as preferred*/ + if (!(*preferred_mode_found)) { + mode_timing.mode_info.flags.PREFERRED = 1; + *preferred_mode_found = true; + } + + dal_dcs_mode_timing_list_append(list, &mode_timing); + ret = true; + } + return ret; +} + +static uint32_t get_supported_3d_formats( + struct edid_ext_cea *ext, + uint32_t vic, + uint32_t vic_idx) +{ + uint32_t formats = 0; + const struct cea_3d_format_support *support; + + if (vic == 0 || vic > MAX_NUM_SHORT_VIDEO_DESCRIPTOR_FORMAT) + return formats; + + /*Get 3D support from driver table (these are 2D timings which has + 3D mandatory support when sink reports stereo flag)*/ + if (ext->mandatory_3d_support) { + const struct cea_3d_format_support *support = + &cea_mandatory_3d_format_supported[vic-1]; + + if (support->FRAME_PACKING) + formats |= (1 << TIMING_3D_FORMAT_HW_FRAME_PACKING); + + if (support->SIDE_BY_SIDE_HALF) + formats |= (1 << TIMING_3D_FORMAT_SIDE_BY_SIDE); + + if (support->TOP_AND_BOTTOM) + formats |= (1 << TIMING_3D_FORMAT_TOP_AND_BOTTOM); + } + + /*for multi and extended 3D support, we only care about the first + 16 entries in the EDID*/ + if (vic_idx >= 16) + return formats; + + support = &cea_all_3d_format_supported[vic - 1]; + + if (ext->multi_3d_support == STEREO_3D_MULTI_ALL_FORMATS || + ext->multi_3d_support == STEREO_3D_MULTI_MASKED_FORMATS) { + + if (support->FRAME_PACKING && + ext->cached_multi_3d_support[vic_idx].FRAME_PACKING) + formats |= (1 << TIMING_3D_FORMAT_HW_FRAME_PACKING); + + if (support->SIDE_BY_SIDE_HALF && + ext->cached_multi_3d_support[vic_idx].SIDE_BY_SIDE_HALF) + formats |= (1 << TIMING_3D_FORMAT_SIDE_BY_SIDE); + + if (support->TOP_AND_BOTTOM && + ext->cached_multi_3d_support[vic_idx].TOP_AND_BOTTOM) + formats |= (1 << TIMING_3D_FORMAT_TOP_AND_BOTTOM); + } + + /* check for extended 3D support */ + if (support->FRAME_PACKING && + ext->cached_ext_3d_support[vic_idx].format.FRAME_PACKING) + formats |= (1 << TIMING_3D_FORMAT_HW_FRAME_PACKING); + + if (support->SIDE_BY_SIDE_HALF && + ext->cached_ext_3d_support[vic_idx].format.SIDE_BY_SIDE_HALF) + formats |= (1 << TIMING_3D_FORMAT_SIDE_BY_SIDE); + + if (support->TOP_AND_BOTTOM && + ext->cached_ext_3d_support[vic_idx].format.TOP_AND_BOTTOM) + formats |= (1 << TIMING_3D_FORMAT_TOP_AND_BOTTOM); + + return formats; +} + +static bool retrieve_cea861b_svd_mode_timing( + struct edid_ext_cea *ext, + uint8_t svd_code, + bool video_optimized_rate, + enum timing_3d_format timing_3D_format, + bool multi_stereo_mode, + struct mode_timing *mode_timing) +{ + if (!dal_timing_service_get_mode_timing_for_video_code( + ext->edid.ts, + svd_code & CEA_SHORT_VIDEO_DESCRIPTION_VIDEO_ID_CODE_MASK, + video_optimized_rate, + mode_timing)) + return false; + + if (svd_code & CEA_SHORT_VIDEO_DESCRIPTION_NATIVE_MASK) + mode_timing->mode_info.flags.NATIVE = 1; + + /* default to RGB 8bit */ + mode_timing->crtc_timing.display_color_depth = DISPLAY_COLOR_DEPTH_888; + mode_timing->crtc_timing.pixel_encoding = PIXEL_ENCODING_RGB; + + /* Setup timing source - if this mode/timing is 3D or belongs to + 3D group - make it CEA_SVD_3D*/ + if (multi_stereo_mode || timing_3D_format != TIMING_3D_FORMAT_NONE) + mode_timing->mode_info.timing_source = + TIMING_SOURCE_EDID_CEA_SVD_3D; + else + mode_timing->mode_info.timing_source = + TIMING_SOURCE_EDID_CEA_SVD; + + /* If this mode/timing belongs to 3D group - set these two flags*/ + mode_timing->crtc_timing.flags.USE_IN_3D_VIEW_ONLY = + (multi_stereo_mode && + timing_3D_format != TIMING_3D_FORMAT_NONE); + + mode_timing->crtc_timing.flags.STEREO_3D_PREFERENCE = + (multi_stereo_mode && + timing_3D_format == TIMING_3D_FORMAT_NONE); + + mode_timing->crtc_timing.timing_3d_format = timing_3D_format; + + return true; +} + +static bool add_svd_mode_timing_from_svd_code( + struct edid_ext_cea *ext, + struct dcs_mode_timing_list *list, + uint8_t svd_code, + uint8_t vic_idx) +{ + bool ret = false; + struct mode_timing mode_timing; + uint32_t format; + uint32_t stereo_3d_formats = get_supported_3d_formats( + ext, + svd_code & + CEA_SHORT_VIDEO_DESCRIPTION_VIDEO_ID_CODE_MASK, + vic_idx); + + /* We add separate 2D timing if this is multi-stereo mode + (support 2 or more 3D formats) or we do not have 3D formats + at all - need to add 2D timign only*/ + bool multi_stereo_mode = + ((stereo_3d_formats & (stereo_3d_formats - 1)) != 0); + + if (stereo_3d_formats == 0 || multi_stereo_mode) + stereo_3d_formats = + stereo_3d_formats | (1 << TIMING_3D_FORMAT_NONE); + + for (format = 0; format < TIMING_3D_FORMAT_MAX; ++format) { + + if (!(stereo_3d_formats & (1 << format))) + continue; + + /* get non video optimized version*/ + if (!retrieve_cea861b_svd_mode_timing( + ext, + svd_code, + false, + format, + multi_stereo_mode, + &mode_timing)) + continue; + + dal_dcs_mode_timing_list_append(list, &mode_timing); + ret = true; + + /* try video optimized version*/ + if (!retrieve_cea861b_svd_mode_timing( + ext, + svd_code, + true, + format, + multi_stereo_mode, + &mode_timing)) + continue; + + dal_dcs_mode_timing_list_append(list, &mode_timing); + ret = true; + } + return ret; +} + +static bool add_svd_mode_timings( + struct edid_ext_cea *ext, + struct dcs_mode_timing_list *list, + bool *preferred_mode_found) +{ + bool ret = false; + uint8_t offset = 0; + struct short_descr_info descr = { 0 }; + uint32_t vic_idx = 0; + + ASSERT(list != NULL); + + /* loop through all short video descriptors*/ + while (find_short_descr( + ext->data, + offset, + CEA_861B_DATA_BLOCK_TAGCODE_SHORT_VIDEO_DESCRIPTOR, + CEA_861B_DATA_BLOCK_EXT_TAGCODE_NONE, + &descr)) { + /*get the SVDs*/ + const uint8_t *svd = &ext->data->data_block[descr.offset]; + uint32_t i; + + for (i = 0; i < descr.len; ++i) { + + if (add_svd_mode_timing_from_svd_code( + ext, list, svd[i], vic_idx)) + ret = true; + + vic_idx++; + } + + /* start next search at the end of this descriptor block*/ + offset = (uint8_t)(descr.offset + descr.len); + + } + + return ret; +} + +#define GET_BIT(byte, bit) ((byte >> bit) & 0x01) +#define GET_BITS(byte, bit, count) ((byte >> bit) & ((1<<count)-1)) + +static void get_additional_video_fields( + struct edid_ext_cea *ext, + const struct short_descr_info *descr, + struct additional_video_fields *video_fields) +{ + uint8_t byte8; + uint32_t offset; + uint32_t bytes_to_parse; + uint32_t i; + uint32_t ext_3d_to_parse = 0; + uint32_t ext_3d_parsed = 0; + + ASSERT(descr != NULL); + ASSERT(video_fields != NULL); + + dal_memset(video_fields, 0, sizeof(struct additional_video_fields)); + + /*Check if additional video parameters present*/ + if (descr->len < 8) + return; + + /*In byte 8 VSDB defines which of the optional video fields present + (latency, interlaced latency and HDMI Video)*/ + byte8 = ext->data->data_block[descr->offset + 7]; + /* We start at byte 9.*/ + offset = descr->offset + 8; + + if (!(byte8 & HDMI_VENDOR_SPECIFIC_DATA_BLOCK_HDMI_VIDEO_PRESENT)) + return; + + video_fields->valid = false; + + /* Increase offset if latency field present*/ + if (byte8 & HDMI_VENDOR_SPECIFIC_DATA_BLOCK_LATENCY_PRESENT) + offset += 2; + + /* Increase offset if interlaced latency field present*/ + if (byte8 & HDMI_VENDOR_SPECIFIC_DATA_BLOCK_I_LATENCY_PRESENT) + offset += 2; + + bytes_to_parse = descr->len + descr->offset - offset; + + /* Finally get additional video parameters*/ + if (bytes_to_parse >= 2) { + video_fields->valid = true; + video_fields->cea_hdmi_vsdb_ext_cap.stereo_3d_present = + GET_BIT(ext->data->data_block[offset], 7); + video_fields->cea_hdmi_vsdb_ext_cap.stereo_3d_multi_present = + GET_BITS(ext->data->data_block[offset], 5, 2); + video_fields->cea_hdmi_vsdb_ext_cap.image_size = + GET_BITS(ext->data->data_block[offset], 3, 2); + offset += 1; + + video_fields->cea_hdmi_vsdb_ext_cap.hdmi_vic_len = + GET_BITS(ext->data->data_block[offset], 5, 3); + video_fields->cea_hdmi_vsdb_ext_cap.hdmi_3d_len = + GET_BITS(ext->data->data_block[offset], 0, 5); + + offset += 1; + bytes_to_parse -= 2; + } + + for (i = 0; i < video_fields->cea_hdmi_vsdb_ext_cap.hdmi_vic_len; ++i) + video_fields->hdmi_vic[i] = ext->data->data_block[offset+i]; + + offset += video_fields->cea_hdmi_vsdb_ext_cap.hdmi_vic_len; + bytes_to_parse -= video_fields->cea_hdmi_vsdb_ext_cap.hdmi_vic_len; + video_fields->hdmi_3d_offset = 0; + + if (bytes_to_parse >= 2 && + (video_fields->cea_hdmi_vsdb_ext_cap.stereo_3d_multi_present == + STEREO_3D_MULTI_ALL_FORMATS || + video_fields->cea_hdmi_vsdb_ext_cap.stereo_3d_multi_present == + STEREO_3D_MULTI_MASKED_FORMATS)) { + + uint16_t s3d_support = (ext->data->data_block[offset] << 8) + + ext->data->data_block[offset+1]; + /* From Table 8-19 - 3D_Structure_ALL of HDMI 1.4a spec: + Bit 0 specifies whether frame packing is supported + Bit 6 specifies whether top-and-bottom is supported + Bit 8 specifies whether side-by-side (half) is supported*/ + + video_fields->stereo_3D_all_support.FRAME_PACKING = + GET_BIT(s3d_support, STEREO_3D_ALL_FRAME_PACKING); + + video_fields->stereo_3D_all_support.TOP_AND_BOTTOM = + GET_BIT(s3d_support, STEREO_3D_ALL_TOP_AND_BOTTOM); + + video_fields->stereo_3D_all_support.SIDE_BY_SIDE_HALF = + GET_BIT(s3d_support, STEREO_3D_ALL_SIDE_BY_SIDE_HALF); + + video_fields->hdmi_3d_offset += 2; + offset += 2; + bytes_to_parse -= 2; + } + + if (bytes_to_parse >= 2 && + video_fields->cea_hdmi_vsdb_ext_cap.stereo_3d_multi_present == + STEREO_3D_MULTI_MASKED_FORMATS) { + + video_fields->stereo_3d_mask = + (ext->data->data_block[offset] << 8) + + ext->data->data_block[offset+1]; + + video_fields->hdmi_3d_offset += 2; + offset += 2; + bytes_to_parse -= 2; + } + + ASSERT(video_fields->cea_hdmi_vsdb_ext_cap.hdmi_3d_len >= + video_fields->hdmi_3d_offset); + + /*HDMI_3D_LEN indicates the total length of following 3D video format + capabilities including 3D_STRUCTURE_ALL_15...0, + 3D_MASK_15...0, 2D_VIC_order_X, 3D_STRUCTURE_X, and 3D_Detail_X fields + 3D_STRUCTURE_ALL_15...0 and 3D_MASK_15...0 take 2 bytes each, so + if numBytesToParse > 0 and pVideoFields->hdmi3DLength > 2 or 4 + (depending on the presence of 3D_MASK_15...0), then there are fields + for 2D_VIC_order_X, 3D_STRUCTURE_X, and 3D_Detail_X*/ + if (video_fields->cea_hdmi_vsdb_ext_cap.hdmi_3d_len > + video_fields->hdmi_3d_offset && bytes_to_parse > 0) + ext_3d_to_parse = + video_fields->cea_hdmi_vsdb_ext_cap.hdmi_3d_len - + video_fields->hdmi_3d_offset; + + while (ext_3d_to_parse > 0) { + struct cea_extended_3d_support ext_3d_support = { 0 }; + struct stereo_3d_extended_support *video_ext_3d; + uint32_t parsed = 0; + + ext_3d_support.VIC_INDEX = + GET_BITS(ext->data->data_block[offset], 4, 4); + + ext_3d_support.CODE = + GET_BITS(ext->data->data_block[offset], 0, 4); + + offset += 1; + + /* if 3D_Structure_X is in the range of 0000 to 0111, + there are no 3D detail and reserved fields*/ + if (ext_3d_support.CODE >= STEREO_3D_ALL_SIDE_BY_SIDE_HALF) { + ext_3d_support.DETAIL = + GET_BITS(ext->data->data_block[offset], 4, 4); + ext_3d_support.RESERVED = + GET_BITS(ext->data->data_block[offset], 0, 4); + offset += 1; + } + + video_ext_3d = + &video_fields->stereo_3d_ext_support[ext_3d_parsed]; + switch (ext_3d_support.CODE) { + case STEREO_3D_ALL_FRAME_PACKING: + video_ext_3d->format.FRAME_PACKING = true; + video_ext_3d->vic_index = ext_3d_support.VIC_INDEX; + video_ext_3d->size = 1; + ext_3d_parsed++; + parsed = 1; + break; + case STEREO_3D_ALL_TOP_AND_BOTTOM: + video_ext_3d->format.TOP_AND_BOTTOM = true; + video_ext_3d->vic_index = ext_3d_support.VIC_INDEX; + video_ext_3d->size = 1; + ext_3d_parsed++; + parsed = 1; + break; + case STEREO_3D_ALL_SIDE_BY_SIDE_HALF: + video_ext_3d->format.SIDE_BY_SIDE_HALF = true; + video_ext_3d->vic_index = ext_3d_support.VIC_INDEX; + video_ext_3d->value = ext_3d_support.DETAIL; + video_ext_3d->size = 2; + ext_3d_parsed++; + parsed = 2; + break; + default: + parsed = (ext_3d_support.CODE >= + STEREO_3D_ALL_SIDE_BY_SIDE_HALF ? 2 : 1); + break; + } + + ASSERT(ext_3d_to_parse >= parsed); + + if (ext_3d_to_parse >= parsed) + ext_3d_to_parse -= parsed; + else + ext_3d_to_parse = 0; + } + + video_fields->hdmi_3d_ext_len = ext_3d_parsed; +} + +static bool get_timing_for_hdmi_vic( + struct edid_ext_cea *ext, + enum cea_hdmi_vic vic, + bool video_optimized_rate, + struct mode_timing *mode_timing) +{ + if (vic == CEA_HDMI_VIC_RESERVED || + (vic == CEA_HDMI_VIC_4KX2K_25 && video_optimized_rate) || + (vic == CEA_HDMI_VIC_4KX2K_24_SMPTE && video_optimized_rate)) + return false; + + if (dal_timing_service_get_mode_timing_for_hdmi_video_code( + ext->edid.ts, vic, video_optimized_rate, mode_timing)) { + /* default to RGB - 8bit*/ + mode_timing->crtc_timing.display_color_depth = + DISPLAY_COLOR_DEPTH_888; + mode_timing->crtc_timing.pixel_encoding = + PIXEL_ENCODING_RGB; + + return true; + } + + return false; +} + +static bool add_hdmi_vic_timings( + struct edid_ext_cea *edid, + struct dcs_mode_timing_list *list, + bool *preferred_mode_found) +{ + bool ret = false; + uint32_t i; + struct short_descr_info descr = { 0 }; + struct additional_video_fields video_fields; + + ASSERT(list != NULL); + + if (!find_short_descr( + edid->data, + 0, + CEA_861B_DATA_BLOCK_TAGCODE_VENDOR_SPECIFIC_DATA_BLOCK, + CEA_861B_DATA_BLOCK_EXT_TAGCODE_NONE, + &descr)) + return false; + + get_additional_video_fields(edid, &descr, &video_fields); + + for (i = 0; + i < video_fields.cea_hdmi_vsdb_ext_cap.hdmi_vic_len; + ++i) { + /* get non video optimized version */ + struct mode_timing mode_timing; + + if (get_timing_for_hdmi_vic( + edid, + video_fields.hdmi_vic[i], + false, + &mode_timing)) { + + dal_dcs_mode_timing_list_append( + list, &mode_timing); + ret = true; + } + + /* try video optimized version*/ + if (get_timing_for_hdmi_vic( + edid, + video_fields.hdmi_vic[i], + true, + &mode_timing)) { + dal_dcs_mode_timing_list_append( + list, &mode_timing); + ret = true; + } + } + + return ret; +} + +static void get_latency_fields( + struct edid_ext_cea *ext, + const struct short_descr_info *descr, + struct latency_fields *latency_fields) +{ + uint8_t byte8; + uint32_t offset; + + ASSERT(descr != NULL); + ASSERT(latency_fields != NULL); + + dal_memset(latency_fields, 0, sizeof(struct latency_fields)); + + if (descr->len < 8) + return; + + byte8 = ext->data->data_block[descr->offset + 7]; + offset = descr->offset + 8; + + if (byte8 & HDMI_VENDOR_SPECIFIC_DATA_BLOCK_LATENCY_PRESENT) { + + ASSERT(offset + 1 < descr->offset + descr->len); + + latency_fields->latency.valid = true; + + latency_fields->latency.video_latency = + ext->data->data_block[offset]; + + latency_fields->latency.audio_latency = + ext->data->data_block[offset + 1]; + + offset += 2; + } + + if (byte8 & HDMI_VENDOR_SPECIFIC_DATA_BLOCK_I_LATENCY_PRESENT) { + + ASSERT(offset + 1 < descr->offset + descr->len); + + latency_fields->i_latency.valid = true; + + latency_fields->i_latency.video_latency = + ext->data->data_block[offset]; + + latency_fields->i_latency.audio_latency = + ext->data->data_block[offset + 1]; + + offset += 2; + } +} + +static bool get_cea_vendor_specific_data_block( + struct edid_base *edid, + struct cea_vendor_specific_data_block *vendor_block) +{ + /* here we assume it is only one block and just take the first one*/ + struct short_descr_info descr = { 0 }; + const struct vendor_specific_data_block *vsdb; + struct latency_fields latency_fields = { { 0 } }; + struct additional_video_fields video_fields = { 0 }; + struct edid_ext_cea *ext = FROM_EDID(edid); + uint32_t i; + + if (vendor_block == NULL) + return false; + + dal_memset( + vendor_block, 0, sizeof(struct cea_vendor_specific_data_block)); + + if (!find_short_descr( + ext->data, + 0, + CEA_861B_DATA_BLOCK_TAGCODE_VENDOR_SPECIFIC_DATA_BLOCK, + CEA_861B_DATA_BLOCK_EXT_TAGCODE_NONE, + &descr)) + return false; + + vsdb = (const struct vendor_specific_data_block *) + (&ext->data->data_block[descr.offset]); + + /* Translate */ + vendor_block->ieee_id = (vsdb->ieee_id[2] << 16) + + (vsdb->ieee_id[1] << 8) + (vsdb->ieee_id[0]); + vendor_block->commonent_phy_addr.PHY_ADDR_A = + GET_BITS(vsdb->commonent_phy_addr[0], 4, 4); + vendor_block->commonent_phy_addr.PHY_ADDR_B = + GET_BITS(vsdb->commonent_phy_addr[0], 0, 4); + vendor_block->commonent_phy_addr.PHY_ADDR_C = + GET_BITS(vsdb->commonent_phy_addr[1], 4, 4); + vendor_block->commonent_phy_addr.PHY_ADDR_D = + GET_BITS(vsdb->commonent_phy_addr[0], 0, 4); + + /* optional 6th byte */ + if (descr.len >= 6) { + vendor_block->byte6.SUPPORTS_AI = GET_BIT(vsdb->byte6, 7); + vendor_block->byte6.DC_48BIT = GET_BIT(vsdb->byte6, 6); + vendor_block->byte6.DC_36BIT = GET_BIT(vsdb->byte6, 5); + vendor_block->byte6.DC_30BIT = GET_BIT(vsdb->byte6, 4); + vendor_block->byte6.DC_Y444 = GET_BIT(vsdb->byte6, 3); + vendor_block->byte6.DVI_DUAL = GET_BIT(vsdb->byte6, 0); + vendor_block->byte6_valid = true; + } + + /* optional 7th byte */ + if (descr.len >= 7) { + const struct monitor_patch_info *patch_info; + + vendor_block->max_tmds_clk_mhz = vsdb->max_tmds_clk * 5; + + patch_info = + dal_edid_patch_get_monitor_patch_info( + ext->edid_patch, + MONITOR_PATCH_TYPE_DO_NOT_USE_EDID_MAX_PIX_CLK); + + if (patch_info) + vendor_block->max_tmds_clk_mhz = patch_info->param; + } + + /* optional 8th byte*/ + if (descr.len >= 8) { + /* bits 0 - 3 indicate which content type is supported*/ + vendor_block->byte8.CNC0_GRAPHICS = GET_BIT(vsdb->byte8, 0); + vendor_block->byte8.CNC1_PHOTO = GET_BIT(vsdb->byte8, 1); + vendor_block->byte8.CNC2_CINEMA = GET_BIT(vsdb->byte8, 2); + vendor_block->byte8.CNC3_GAME = GET_BIT(vsdb->byte8, 3); + vendor_block->byte8.HDMI_VIDEO_PRESENT = + GET_BIT(vsdb->byte8, 5); + } + + /* optional latency fields (byte 9-12)*/ + get_latency_fields(ext, &descr, &latency_fields); + + if (latency_fields.latency.valid) { + + vendor_block->byte8.LATENCY_FIELDS_PRESENT = 1; + vendor_block->video_latency = + latency_fields.latency.video_latency; + vendor_block->audio_latency = + latency_fields.latency.audio_latency; + } + + if (latency_fields.i_latency.valid) { + vendor_block->byte8.ILATENCY_FIELDS_PRESENT = 1; + vendor_block->i_video_latency = + latency_fields.i_latency.video_latency; + vendor_block->i_audio_latency = + latency_fields.i_latency.audio_latency; + + } + + /*Here could come additional optional fields in VSDB. + Not needed for now*/ + + get_additional_video_fields(ext, &descr, &video_fields); + + if (video_fields.valid) + vendor_block->hdmi_vsdb_extended_caps = + video_fields.cea_hdmi_vsdb_ext_cap; + + for (i = 0; i < video_fields.cea_hdmi_vsdb_ext_cap.hdmi_vic_len; ++i) + vendor_block->hdmi_vic[i] = video_fields.hdmi_vic[i]; + + if (video_fields.cea_hdmi_vsdb_ext_cap.stereo_3d_multi_present == + STEREO_3D_MULTI_ALL_FORMATS || + video_fields.cea_hdmi_vsdb_ext_cap.stereo_3d_multi_present == + STEREO_3D_MULTI_MASKED_FORMATS) { + + vendor_block->stereo_3d_all_support.FRAME_PACKING = + video_fields.stereo_3D_all_support.FRAME_PACKING; + + vendor_block->stereo_3d_all_support.SIDE_BY_SIDE_HALF = + video_fields.stereo_3D_all_support.SIDE_BY_SIDE_HALF; + + vendor_block->stereo_3d_all_support.TOP_AND_BOTTOM = + video_fields.stereo_3D_all_support.TOP_AND_BOTTOM; + } + + if (video_fields.cea_hdmi_vsdb_ext_cap.stereo_3d_multi_present == + STEREO_3D_MULTI_MASKED_FORMATS) + vendor_block->stereo_3d_mask = video_fields.stereo_3d_mask; + + for (i = 0; i < video_fields.hdmi_3d_ext_len; ++i) + vendor_block->stereo_3d_extended_support[i] = + video_fields.stereo_3d_ext_support[i]; + + return true; +} + +static bool get_supported_mode_timing( + struct edid_base *edid, + struct dcs_mode_timing_list *list, + bool *preferred_mode_found) +{ + struct edid_ext_cea *ext = FROM_EDID(edid); + + /* Calling sequence/order is important for preferred mode lookup*/ + bool det = add_detailed_timings( + ext, list, preferred_mode_found); + bool svd = add_svd_mode_timings( + ext, list, preferred_mode_found); + bool hdmi_vic = add_hdmi_vic_timings( + ext, list, preferred_mode_found); + + return det || svd || hdmi_vic; + +} + +static bool get_connector_type( + struct edid_base *edid, + enum dcs_edid_connector_type *type) +{ + struct cea_vendor_specific_data_block vendor_block; + + if (!get_cea_vendor_specific_data_block(edid, &vendor_block)) + return false; + + if (vendor_block.ieee_id != + HDMI_VENDOR_SPECIFIC_DATA_BLOCK_IEEE_REGISTRATION_ID) + return false; + + *type = EDID_CONNECTOR_HDMIA; + return true; +} + +static bool get_display_color_depth( + struct edid_base *edid, + struct display_color_depth_support *color_depth) +{ + struct cea_vendor_specific_data_block vendor_block; + + if (!get_cea_vendor_specific_data_block(edid, &vendor_block)) + return false; + + if (vendor_block.ieee_id != + HDMI_VENDOR_SPECIFIC_DATA_BLOCK_IEEE_REGISTRATION_ID) + return false; + + if (!vendor_block.byte6_valid) + return false; + + if (vendor_block.byte6.DC_48BIT) + color_depth->mask |= COLOR_DEPTH_INDEX_161616; + + if (vendor_block.byte6.DC_36BIT) + color_depth->mask |= COLOR_DEPTH_INDEX_121212; + + if (vendor_block.byte6.DC_30BIT) + color_depth->mask |= COLOR_DEPTH_INDEX_101010; + + return true; +} + +static bool get_cea861_support( + struct edid_base *edid, + struct cea861_support *cea861_support) +{ + bool ret = false; + struct edid_ext_cea *ext = FROM_EDID(edid); + + ASSERT(cea861_support != NULL); + + dal_memset(cea861_support, 0, sizeof(struct cea861_support)); + + cea861_support->revision = ext->data->revision; + + if (cea861_support->revision > CEA861_VERSION_1) { + + cea861_support->raw_features = ext->data->cea861b_byte3; + ret = true; + } + + return ret; +} + +static bool get_display_pixel_encoding( + struct edid_base *edid, + struct display_pixel_encoding_support *pixel_encoding) +{ + struct cea861_support cea861_support; + + if (!get_cea861_support(edid, &cea861_support)) + return false; + + /**TODO: add edid patch support*/ + if (cea861_support.features.YCRCB422) + pixel_encoding->mask |= PIXEL_ENCODING_MASK_YCBCR422; + + if (cea861_support.features.YCRCB444) + pixel_encoding->mask |= PIXEL_ENCODING_MASK_YCBCR444; + + return true; +} + +static bool get_cea_speaker_allocation_data_block( + struct edid_base *edid, + union cea_speaker_allocation_data_block *spkr_data) +{ + struct edid_ext_cea *ext = FROM_EDID(edid); + struct short_descr_info descr; + + if (!find_short_descr( + ext->data, + 0, + CEA_861B_DATA_BLOCK_TAGCODE_SPKR_ALLOCATION_DATA_BLOCK, + CEA_861B_DATA_BLOCK_EXT_TAGCODE_NONE, + &descr)) + return false; + + if (descr.len == sizeof(struct speaker_allocation_data_block)) + /* the first byte has the speaker description*/ + spkr_data->raw = ext->data->data_block[descr.offset]; + + return true; +} + +static bool get_cea_colorimetry_data_block( + struct edid_base *edid, + struct cea_colorimetry_data_block *colorimetry_data_block) +{ + struct short_descr_info descr = { 0 }; + struct colorimetry_data_block cmdb = { 0 }; + uint32_t block_len = sizeof(struct colorimetry_data_block); + struct edid_ext_cea *ext = FROM_EDID(edid); + + ASSERT(colorimetry_data_block != NULL); + + /* here we assume it is only one block and just take the first one*/ + if (!find_short_descr( + ext->data, + 0, + CEA_861B_DATA_BLOCK_TAGCODE_USE_EXTENDED_TAG, + CEA_861B_DATA_BLOCK_EXT_TAGCODE_COLORIMETRY_DATA_BLOCK, + &descr)) + return false; + + if (descr.len < block_len) + block_len = descr.len; + + dal_memmove(&cmdb, &ext->data->data_block[descr.offset], block_len); + + /*Translate*/ + colorimetry_data_block->flag.XV_YCC601 = GET_BIT(cmdb.byte3, 0); + colorimetry_data_block->flag.XV_YCC709 = GET_BIT(cmdb.byte3, 1); + colorimetry_data_block->flag.S_YCC601 = GET_BIT(cmdb.byte3, 2); + colorimetry_data_block->flag.ADOBE_YCC601 = GET_BIT(cmdb.byte3, 3); + colorimetry_data_block->flag.ADOBE_RGB = GET_BIT(cmdb.byte3, 4); + + colorimetry_data_block->metadata_flag.MD0 = GET_BIT(cmdb.byte4, 0); + colorimetry_data_block->metadata_flag.MD1 = GET_BIT(cmdb.byte4, 1); + colorimetry_data_block->metadata_flag.MD2 = GET_BIT(cmdb.byte4, 2); + colorimetry_data_block->metadata_flag.MD3 = GET_BIT(cmdb.byte4, 3); + + return true; +} + +static bool get_cea_video_capability_data_block( + struct edid_base *edid, + union cea_video_capability_data_block *caps) +{ + struct short_descr_info descr = { 0 }; + struct video_capability_data_block vcdb = { 0 }; + uint32_t block_len = sizeof(struct video_capability_data_block); + struct edid_ext_cea *ext = FROM_EDID(edid); + + ASSERT(caps != NULL); + + /* here we assume it is only one block and just take the first one*/ + if (!find_short_descr( + ext->data, + 0, + CEA_861B_DATA_BLOCK_TAGCODE_USE_EXTENDED_TAG, + CEA_861B_DATA_BLOCK_EXT_TAGCODE_VIDEO_CAPABILITY_DATA_BLOCK, + &descr)) + return false; + + if (descr.len < block_len) + block_len = descr.len; + + dal_memmove(&vcdb, &ext->data->data_block[descr.offset], block_len); + + caps->raw = vcdb.byte3; + return true; +} + +static void cache_stereo_3d_support_info(struct edid_ext_cea *ext) +{ + struct additional_video_fields video_fields; + struct short_descr_info descr = { 0 }; + uint32_t i = 0; + + video_fields.valid = false; + /* Get additional (optional) video capabilities from VSDB. + Here we assume there is only one VSDB in CEA extension*/ + if (find_short_descr( + ext->data, + 0, + CEA_861B_DATA_BLOCK_TAGCODE_VENDOR_SPECIFIC_DATA_BLOCK, + CEA_861B_DATA_BLOCK_EXT_TAGCODE_NONE, + &descr)) + get_additional_video_fields(ext, &descr, &video_fields); + + + if (!video_fields.valid) + return; + + for (i = 0; i < video_fields.hdmi_3d_ext_len; ++i) { + + uint32_t vic_idx = + video_fields.stereo_3d_ext_support[i].vic_index; + ext->cached_ext_3d_support[vic_idx] = + video_fields.stereo_3d_ext_support[i]; + } + + ext->mandatory_3d_support = + video_fields.cea_hdmi_vsdb_ext_cap.stereo_3d_present; + + ext->multi_3d_support = + video_fields.cea_hdmi_vsdb_ext_cap.stereo_3d_multi_present; + + if (ext->multi_3d_support != STEREO_3D_MULTI_ALL_FORMATS && + (ext->multi_3d_support != STEREO_3D_MULTI_MASKED_FORMATS)) + return; + + /* For the case of Stereo3DMulti_AllFormats: + 3D formats are assigned to all of the VICs listed in the first + 16 entries in the EDID. + For the case of Stereo3DMulti_MaskedFormats: + 3D formats are assigned to some of the VICs listed in the first + 16 entries in the EDID.*/ + for (i = 0; i < MAX_NUM_OF_HDMI_VSDB_3D_MULTI_SUPPORT; ++i) { + + if ((ext->multi_3d_support == STEREO_3D_MULTI_ALL_FORMATS) || + (video_fields.stereo_3d_mask & (1 << i))) { + + ext->cached_multi_3d_support[i] = + video_fields.stereo_3D_all_support; + + } else { + ext->cached_multi_3d_support[i].FRAME_PACKING = + false; + + ext->cached_multi_3d_support[i].SIDE_BY_SIDE_HALF = + false; + + ext->cached_multi_3d_support[i].TOP_AND_BOTTOM = + false; + } + } +} + +static bool add_cea861b_audio_modes( + struct edid_ext_cea *ext, + struct dcs_cea_audio_mode_list *audio_list) +{ + bool ret = false; + uint8_t offset = 0; + struct short_descr_info descr = { 0 }; + + /* loop through all short audio descriptors */ + while (find_short_descr(ext->data, + offset, + CEA_861B_DATA_BLOCK_TAGCODE_SHORT_AUDIO_DESCRIPTOR, + CEA_861B_DATA_BLOCK_EXT_TAGCODE_NONE, + &descr)) { + uint8_t index; + const uint8_t *sad = &ext->data->data_block[descr.offset]; + + for (index = 0; index < descr.len/3; ++index) { + struct cea_audio_mode audio_mode = { 0 }; + + audio_mode.format_code = sad[0]>>3; + audio_mode.channel_count = (sad[0] & 0x7) + 1; + audio_mode.sample_rate = sad[1] & 0x7F; + audio_mode.sample_size = sad[2]; + sad += 3; + ret = true; + if (audio_list) + dal_dcs_cea_audio_mode_list_append( + audio_list, &audio_mode); + } + + /* start next search at the end of this descriptor block */ + offset = descr.offset + descr.len; + } + return ret; +} + +static bool get_cea_audio_modes( + struct edid_base *edid, + struct dcs_cea_audio_mode_list *audio_list) +{ + struct edid_ext_cea *ext = FROM_EDID(edid); + + return add_cea861b_audio_modes(ext, audio_list); +} + +static uint8_t get_edid_extension_tag(struct edid_base *edid) +{ + struct edid_ext_cea *ext = FROM_EDID(edid); + + return ext->data->extension_tag; +} + +static const uint8_t *get_raw_data(struct edid_base *edid) +{ + struct edid_ext_cea *ext = FROM_EDID(edid); + + return (const uint8_t *)ext->data; +} + +static const uint32_t get_raw_size(struct edid_base *edid) +{ + return sizeof(struct edid_data_cea861_ext); +} + +static void destruct(struct edid_ext_cea *edid) +{ + +} + +static void destroy(struct edid_base **edid) +{ + destruct(FROM_EDID(*edid)); + dal_free(FROM_EDID(*edid)); + *edid = NULL; +} + +static const struct edid_funcs funcs = { + .destroy = destroy, + .get_display_tile_info = dal_edid_base_get_display_tile_info, + .get_min_drr_fps = dal_edid_base_get_min_drr_fps, + .get_drr_pixel_clk_khz = dal_edid_base_get_drr_pixel_clk_khz, + .is_non_continuous_frequency = + dal_edid_base_is_non_continuous_frequency, + .get_stereo_3d_support = dal_edid_base_get_stereo_3d_support, + .validate = dal_edid_base_validate, + .get_version = dal_edid_base_get_version, + .num_of_extension = dal_edid_base_num_of_extension, + .get_edid_extension_tag = get_edid_extension_tag, + .get_cea_audio_modes = get_cea_audio_modes, + .get_cea861_support = get_cea861_support, + .get_display_pixel_encoding = get_display_pixel_encoding, + .get_display_color_depth = get_display_color_depth, + .get_connector_type = get_connector_type, + .get_screen_info = dal_edid_base_get_screen_info, + .get_display_characteristics = + dal_edid_base_get_display_characteristics, + .get_monitor_range_limits = dal_edid_base_get_monitor_range_limits, + .get_display_name = dal_edid_base_get_display_name, + .get_vendor_product_id_info = dal_edid_base_get_vendor_product_id_info, + .get_supported_mode_timing = get_supported_mode_timing, + .get_cea_video_capability_data_block = + get_cea_video_capability_data_block, + .get_cea_colorimetry_data_block = + get_cea_colorimetry_data_block, + .get_cea_speaker_allocation_data_block = + get_cea_speaker_allocation_data_block, + .get_cea_vendor_specific_data_block = + get_cea_vendor_specific_data_block, + .get_raw_size = get_raw_size, + .get_raw_data = get_raw_data, +}; + +static bool construct( + struct edid_ext_cea *ext, + struct edid_ext_cea_init_data *init_data) +{ + if (!init_data) + return false; + + if (init_data->len == 0 || + init_data->buf == NULL || + init_data->edid_patch == NULL) + return false; + + if (!dal_edid_ext_cea_is_cea_ext(init_data->len, init_data->buf)) + return false; + + if (!dal_edid_base_construct(&ext->edid, init_data->ts)) + return false; + + ext->data = (struct edid_data_cea861_ext *)init_data->buf; + ext->edid_patch = init_data->edid_patch; + + ext->edid.funcs = &funcs; + + cache_stereo_3d_support_info(ext); + + return true; +} + +struct edid_base *dal_edid_ext_cea_create( + struct edid_ext_cea_init_data *init_data) +{ + struct edid_ext_cea *ext = NULL; + + ext = dal_alloc(sizeof(struct edid_ext_cea)); + + if (!ext) + return NULL; + + if (construct(ext, init_data)) + return &ext->edid; + + dal_free(ext); + BREAK_TO_DEBUGGER(); + return NULL; +} diff --git a/drivers/gpu/drm/amd/dal/dcs/edid/edid_ext_cea.h b/drivers/gpu/drm/amd/dal/dcs/edid/edid_ext_cea.h new file mode 100644 index 000000000000..fecb8915e5a4 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/edid/edid_ext_cea.h @@ -0,0 +1,41 @@ +/* + * Copyright 2012-15 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 + * + */ + +#ifndef __DAL_EDID_EXT_CEA_H__ +#define __DAL_EDID_EXT_CEA_H__ + +struct edid_ext_cea_init_data { + struct edid_patch *edid_patch; + struct timing_service *ts; + uint32_t len; + const uint8_t *buf; +}; + +struct edid_base *dal_edid_ext_cea_create( + struct edid_ext_cea_init_data *init_data); + +bool dal_edid_ext_cea_is_cea_ext(uint32_t len, const uint8_t *buf); + +#endif /* __DAL_EDID_EXT_CEA_H__ */ diff --git a/drivers/gpu/drm/amd/dal/dcs/edid/edid_ext_di.c b/drivers/gpu/drm/amd/dal/dcs/edid/edid_ext_di.c new file mode 100644 index 000000000000..04feb5227849 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/edid/edid_ext_di.c @@ -0,0 +1,327 @@ +/* + * Copyright 2012-15 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 + * + */ + +#include "dal_services.h" +#include "include/timing_service_interface.h" +#include "edid_base.h" +#include "edid_ext_di.h" + +#define DI_EXT__NUM_OF_BYTES_IN_VERSION_REVISION_FIELDS 4 +#define DI_EXT__NUM_OF_BYTES_IN_MIN_MAX_CROSSOVER_PIXELCLOCK_FIELDS 5 +#define DI_EXT__NUM_OF_BYTES_IN_DISPLAYDEVICE_FIELDS 6 +#define DI_EXT__NUM_OF_BYTES_IN_FRAMERATE_CONVERSION_FIELDS 5 +#define DI_EXT__NUM_OF_BYTES_IN_COLOR_LUMMINANCE_DECODING_FIELDS 4 +#define DI_EXT__NUM_OF_BYTES_IN_PACKETIZED_SUPPORT_FIELDS 16 +#define DI_EXT__NUM_OF_BYTES_IN_UNUSED_BYTES_FIELDS 17 +#define DI_EXT__NUM_OF_BYTES_IN_AUDIO_SUPPORT_FIELDS 9 +#define DI_EXT__NUM_OF_BYTES_IN_GAMMA_FIELDS 46 + +enum di_ext_interface_type { + DI_EXT_INTERFACE_TYPE_ANALOG = 0x00, + DI_EXT_INTERFACE_TYPE_DIGITAL = 0x01, + DI_EXT_INTERFACE_TYPE_DVI_SL = 0x02, + DI_EXT_INTERFACE_TYPE_DVI_DL_HR = 0x03, + DI_EXT_INTERFACE_TYPE_DVI_DL_HC = 0x04, + DI_EXT_INTERFACE_TYPE_DVI_CE = 0x05, + DI_EXT_INTERFACE_TYPE_PLUG_DISPLAY = 0x06, + DI_EXT_INTERFACE_TYPE_DFP = 0x07, + DI_EXT_INTERFACE_TYPE_LDI_SL = 0x08, + DI_EXT_INTERFACE_TYPE_LDI_DL = 0x09, + DI_EXT_INTERFACE_TYPE_LDI_CE = 0x0A +}; + +enum di_ext_data_format { + DI_EXT_DATA_FORMAT_ANALOG = 0x00, + DI_EXT_DATA_FORMAT_8BIT_RGB = 0x15, + DI_EXT_DATA_FORMAT_12BIT_RGB = 0x19, + DI_EXT_DATA_FORMAT_24BIT_RGB_SL = 0x24, + DI_EXT_DATA_FORMAT_48BIT_RGB_DL_HR = 0x48, + DI_EXT_DATA_FORMAT_48BIT_RGB_DL_HC = 0x49 +}; + +struct bgr_color_depth { + uint8_t blue; + uint8_t green; + uint8_t red; +}; + +struct ycbcr_color_depth { + uint8_t cb; + uint8_t y; + uint8_t cr; +}; + +struct di_ext_monitor_color_depths { + uint8_t dithering; + struct bgr_color_depth bgr_color_depth; + struct ycbcr_color_depth ycrcb_color_depth; +}; + +struct edid_data_di_ext { + uint8_t extension_tag; + uint8_t version; + + /* digital interface,12 bytes*/ + struct { + uint8_t standard; + + uint8_t version_revision + [DI_EXT__NUM_OF_BYTES_IN_VERSION_REVISION_FIELDS]; + + struct { + uint8_t description; + uint8_t data; + } format; + + uint8_t min_max_crossover_pix_clk + [DI_EXT__NUM_OF_BYTES_IN_MIN_MAX_CROSSOVER_PIXELCLOCK_FIELDS]; + + } digital_interface; + + uint8_t display_device[DI_EXT__NUM_OF_BYTES_IN_DISPLAYDEVICE_FIELDS]; + /*displayCapability, 35 bytes*/ + struct { + uint8_t misc_caps; + + uint8_t frame_rate_conversion + [DI_EXT__NUM_OF_BYTES_IN_FRAMERATE_CONVERSION_FIELDS]; + + uint8_t display_scan_orientation; + + uint8_t color_lumminance_decoding + [DI_EXT__NUM_OF_BYTES_IN_COLOR_LUMMINANCE_DECODING_FIELDS]; + + struct di_ext_monitor_color_depths monitor_color_depth; + + uint8_t aspect_ratio_conversion; + + uint8_t packetized_support + [DI_EXT__NUM_OF_BYTES_IN_PACKETIZED_SUPPORT_FIELDS]; + + } display_caps; + + uint8_t used_bytes[DI_EXT__NUM_OF_BYTES_IN_UNUSED_BYTES_FIELDS]; + + uint8_t audio_support[DI_EXT__NUM_OF_BYTES_IN_AUDIO_SUPPORT_FIELDS]; + + uint8_t gamma[DI_EXT__NUM_OF_BYTES_IN_GAMMA_FIELDS]; + + uint8_t checksum; +}; + +struct edid_ext_di { + struct edid_base edid; + struct edid_data_di_ext *data; +}; + +#define FROM_EDID(e) (container_of((e), struct edid_ext_di, edid)) + +static const uint8_t *get_raw_data(struct edid_base *edid) +{ + struct edid_ext_di *ext = FROM_EDID(edid); + + return (const uint8_t *)ext->data; +} + +static const uint32_t get_raw_size(struct edid_base *edid) +{ + return sizeof(struct edid_data_di_ext); +} + +bool dal_edid_ext_di_is_di_ext(uint32_t len, const uint8_t *buf) +{ + const struct edid_data_di_ext *data; + + if (len < sizeof(struct edid_data_di_ext)) + return false; /* di extension is 128 byte in length*/ + + data = (const struct edid_data_di_ext *)buf; + + if (EDID_EXTENSION_TAG_DI_EXT != data->extension_tag) + return false;/* Tag says it's not Di ext*/ + + return true; +} + +static bool get_display_color_depth( + struct edid_base *edid, + struct display_color_depth_support *color_depth) +{ + bool ret = false; + const struct bgr_color_depth *depth; + struct edid_ext_di *ext = FROM_EDID(edid); + + ASSERT(color_depth != NULL); + + if ((ext->data->digital_interface.standard != + DI_EXT_INTERFACE_TYPE_DVI_DL_HC) || + (ext->data->digital_interface.format.data != + DI_EXT_DATA_FORMAT_48BIT_RGB_DL_HC)) + return false; + + depth = &ext->data->display_caps.monitor_color_depth.bgr_color_depth; + + /* We support only identical depth for all colors*/ + if ((depth->blue != depth->green) || (depth->blue != depth->red)) + return false; + + switch (depth->blue) { + case 10: + color_depth->mask |= COLOR_DEPTH_INDEX_101010; + ret = true; + break; + + case 12: + color_depth->mask |= COLOR_DEPTH_INDEX_121212; + ret = true; + break; + + case 14: + color_depth->mask |= COLOR_DEPTH_INDEX_141414; + ret = true; + break; + + case 16: + color_depth->mask |= COLOR_DEPTH_INDEX_161616; + ret = true; + break; + + default: + break; + } + + return ret; +} + +static bool get_connector_type( + struct edid_base *edid, + enum dcs_edid_connector_type *type) +{ + struct edid_ext_di *ext = FROM_EDID(edid); + + switch (ext->data->digital_interface.standard) { + case DI_EXT_INTERFACE_TYPE_DVI_SL: + case DI_EXT_INTERFACE_TYPE_DVI_DL_HR: + case DI_EXT_INTERFACE_TYPE_DVI_DL_HC: + case DI_EXT_INTERFACE_TYPE_DVI_CE: + *type = EDID_CONNECTOR_DVI; + return true; + + default: + break; + } + + return false; +} + +static void destruct(struct edid_ext_di *edid) +{ + +} + +static void destroy(struct edid_base **edid) +{ + destruct(FROM_EDID(*edid)); + dal_free(FROM_EDID(*edid)); + *edid = NULL; +} + +static const struct edid_funcs funcs = { + .destroy = destroy, + .get_display_tile_info = dal_edid_base_get_display_tile_info, + .get_min_drr_fps = dal_edid_base_get_min_drr_fps, + .get_drr_pixel_clk_khz = dal_edid_base_get_drr_pixel_clk_khz, + .is_non_continuous_frequency = + dal_edid_base_is_non_continuous_frequency, + .get_stereo_3d_support = dal_edid_base_get_stereo_3d_support, + .validate = dal_edid_base_validate, + .get_version = dal_edid_base_get_version, + .num_of_extension = dal_edid_base_num_of_extension, + .get_edid_extension_tag = dal_edid_base_get_edid_extension_tag, + .get_cea_audio_modes = dal_edid_base_get_cea_audio_modes, + .get_cea861_support = dal_edid_base_get_cea861_support, + .get_display_pixel_encoding = dal_edid_base_get_display_pixel_encoding, + .get_display_color_depth = get_display_color_depth, + .get_connector_type = get_connector_type, + .get_screen_info = dal_edid_base_get_screen_info, + .get_display_characteristics = + dal_edid_base_get_display_characteristics, + .get_monitor_range_limits = dal_edid_base_get_monitor_range_limits, + .get_display_name = dal_edid_base_get_display_name, + .get_vendor_product_id_info = dal_edid_base_get_vendor_product_id_info, + .get_supported_mode_timing = dal_edid_base_get_supported_mode_timing, + .get_cea_video_capability_data_block = + dal_edid_base_get_cea_video_capability_data_block, + .get_cea_colorimetry_data_block = + dal_edid_base_get_cea_colorimetry_data_block, + .get_cea_speaker_allocation_data_block = + dal_edid_base_get_cea_speaker_allocation_data_block, + .get_cea_vendor_specific_data_block = + dal_edid_base_get_cea_vendor_specific_data_block, + .get_raw_size = get_raw_size, + .get_raw_data = get_raw_data, +}; + +static bool construct( + struct edid_ext_di *ext, + struct timing_service *ts, + uint32_t len, + const uint8_t *buf) +{ + if (len == 0 || buf == NULL) + return false; + + if (!dal_edid_ext_di_is_di_ext(len, buf)) + return false; + + if (!dal_edid_base_construct(&ext->edid, ts)) + return false; + + ext->data = (struct edid_data_di_ext *)buf; + + ext->edid.funcs = &funcs; + + return true; +} + +struct edid_base *dal_edid_ext_di_create( + struct timing_service *ts, + uint32_t len, + const uint8_t *buf) +{ + struct edid_ext_di *ext = NULL; + + ext = dal_alloc(sizeof(struct edid_ext_di)); + + if (!ext) + return NULL; + + if (construct(ext, ts, len, buf)) + return &ext->edid; + + dal_free(ext); + BREAK_TO_DEBUGGER(); + return NULL; +} diff --git a/drivers/gpu/drm/amd/dal/dcs/edid/edid_ext_di.h b/drivers/gpu/drm/amd/dal/dcs/edid/edid_ext_di.h new file mode 100644 index 000000000000..bdba5b6d0ae5 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/edid/edid_ext_di.h @@ -0,0 +1,37 @@ +/* + * Copyright 2012-15 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 + * + */ + +#ifndef __DAL_EDID_EXT_DI_H__ +#define __DAL_EDID_EXT_DI_H__ + +struct edid_base *dal_edid_ext_di_create( + struct timing_service *ts, + uint32_t len, + const uint8_t *buf); + +bool dal_edid_ext_di_is_di_ext(uint32_t len, const uint8_t *buf); + + +#endif /* __DAL_EDID_EXT_DI_H__ */ diff --git a/drivers/gpu/drm/amd/dal/dcs/edid/edid_ext_unknown.c b/drivers/gpu/drm/amd/dal/dcs/edid/edid_ext_unknown.c new file mode 100644 index 000000000000..133914595fbd --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/edid/edid_ext_unknown.c @@ -0,0 +1,135 @@ +/* + * Copyright 2012-15 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 + * + */ + +#include "dal_services.h" +#include "include/timing_service_interface.h" +#include "edid_base.h" +#include "edid_ext_unknown.h" + +struct edid_ext_unknown { + struct edid_base edid; + uint8_t *data; +}; + +#define FROM_EDID(e) (container_of((e), struct edid_ext_unknown, edid)) + +static const uint8_t *get_raw_data(struct edid_base *edid) +{ + struct edid_ext_unknown *ext = FROM_EDID(edid); + + return ext->data; +} + +static const uint32_t get_raw_size(struct edid_base *edid) +{ + return EDID_VER_1_STDLEN; +} + +static void destruct(struct edid_ext_unknown *ext) +{ + +} + +static void destroy(struct edid_base **edid) +{ + destruct(FROM_EDID(*edid)); + dal_free(FROM_EDID(*edid)); + *edid = NULL; +} + +static const struct edid_funcs funcs = { + .destroy = destroy, + .get_display_tile_info = dal_edid_base_get_display_tile_info, + .get_min_drr_fps = dal_edid_base_get_min_drr_fps, + .get_drr_pixel_clk_khz = dal_edid_base_get_drr_pixel_clk_khz, + .is_non_continuous_frequency = + dal_edid_base_is_non_continuous_frequency, + .get_stereo_3d_support = dal_edid_base_get_stereo_3d_support, + .validate = dal_edid_base_validate, + .get_version = dal_edid_base_get_version, + .num_of_extension = dal_edid_base_num_of_extension, + .get_edid_extension_tag = dal_edid_base_get_edid_extension_tag, + .get_cea_audio_modes = dal_edid_base_get_cea_audio_modes, + .get_cea861_support = dal_edid_base_get_cea861_support, + .get_display_pixel_encoding = dal_edid_base_get_display_pixel_encoding, + .get_display_color_depth = dal_edid_base_get_display_color_depth, + .get_connector_type = dal_edid_base_get_connector_type, + .get_screen_info = dal_edid_base_get_screen_info, + .get_display_characteristics = + dal_edid_base_get_display_characteristics, + .get_monitor_range_limits = dal_edid_base_get_monitor_range_limits, + .get_display_name = dal_edid_base_get_display_name, + .get_vendor_product_id_info = dal_edid_base_get_vendor_product_id_info, + .get_supported_mode_timing = dal_edid_base_get_supported_mode_timing, + .get_cea_video_capability_data_block = + dal_edid_base_get_cea_video_capability_data_block, + .get_cea_colorimetry_data_block = + dal_edid_base_get_cea_colorimetry_data_block, + .get_cea_speaker_allocation_data_block = + dal_edid_base_get_cea_speaker_allocation_data_block, + .get_cea_vendor_specific_data_block = + dal_edid_base_get_cea_vendor_specific_data_block, + .get_raw_size = get_raw_size, + .get_raw_data = get_raw_data, +}; + +static bool construct( + struct edid_ext_unknown *ext, + struct timing_service *ts, + uint32_t len, + const uint8_t *buf) +{ + if (!dal_edid_base_construct(&ext->edid, ts)) + return false; + + ext->data = (uint8_t *)buf; + ext->edid.funcs = &funcs; + return true; +} + +struct edid_base *dal_edid_ext_unknown_create( + struct timing_service *ts, + uint32_t len, + const uint8_t *buf) +{ + struct edid_ext_unknown *ext = NULL; + + ext = dal_alloc(sizeof(struct edid_ext_unknown)); + + if (!ext) + return NULL; + + if (construct(ext, ts, len, buf)) + return &ext->edid; + + dal_free(ext); + BREAK_TO_DEBUGGER(); + return NULL; +} + +bool dal_edid_ext_unknown_is_unknown_ext(uint32_t len, const uint8_t *buf) +{ + return len >= EDID_VER_1_STDLEN; +} diff --git a/drivers/gpu/drm/amd/dal/dcs/edid/edid_ext_unknown.h b/drivers/gpu/drm/amd/dal/dcs/edid/edid_ext_unknown.h new file mode 100644 index 000000000000..d1bbeb2fb9e3 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/edid/edid_ext_unknown.h @@ -0,0 +1,36 @@ +/* + * Copyright 2012-15 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 + * + */ + +#ifndef __DAL_EDID_EXT_UNKNOWN_H__ +#define __DAL_EDID_EXT_UNKNOWN_H__ + +struct edid_base *dal_edid_ext_unknown_create( + struct timing_service *ts, + uint32_t len, + const uint8_t *buf); + +bool dal_edid_ext_unknown_is_unknown_ext(uint32_t len, const uint8_t *buf); + +#endif /* __DAL_EDID_EXT_UNKNOWN_H__ */ diff --git a/drivers/gpu/drm/amd/dal/dcs/edid/edid_ext_vtb.c b/drivers/gpu/drm/amd/dal/dcs/edid/edid_ext_vtb.c new file mode 100644 index 000000000000..cadc3be03a5b --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/edid/edid_ext_vtb.c @@ -0,0 +1,439 @@ +/* + * Copyright 2012-15 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 + * + */ + +#include "dal_services.h" +#include "include/dcs_interface.h" +#include "include/timing_service_interface.h" +#include "edid_base.h" +#include "edid13.h" +#include "edid14.h" +#include "edid1x_data.h" +#include "edid_ext_vtb.h" + +#define VTB_DATA_BYTES 122 +#define DTB_NUM 6 +#define CVT_NUM 40 +#define STDT_NUM 61 + +#define DTB_BYTE_SIZE 18 +#define CVT_BYTE_SIZE 3 +#define STDT_BYTE_SIZE 2 + +struct edid_data_vtb_ext { + uint8_t extension_tag; + uint8_t version; + uint8_t detailed_timings_num; + uint8_t cvt_descr_num; + uint8_t standard_timings_num; + /*the presence of dtb ,cvt or stdt are optional , we may have or not*/ + /*data is stored sequencially and in order to find right offset + the calc. method is used*/ + union { + uint8_t detailed_timings[DTB_NUM * DTB_BYTE_SIZE]; + uint8_t cvt_timings[CVT_NUM * CVT_BYTE_SIZE]; + uint8_t standard_timings[STDT_NUM * STDT_BYTE_SIZE]; + uint8_t data[VTB_DATA_BYTES]; + }; + uint8_t checksum; +}; + +enum vtb_byte_offset { + VTB_BYTE_OFFSET_DETAILED_TIMING = 0, + VTB_BYTE_OFFSET_COORDINATED_VIDEO_TIMING, + VTB_BYTE_OFFSET_STANDARD_TIMING +}; + +struct edid_ext_vtb { + struct edid_base edid; + struct edid_data_vtb_ext *data; + uint32_t edid_version; +}; + +#define FROM_EDID(e) (container_of((e), struct edid_ext_vtb, edid)) + +static const uint8_t *get_vtb_offset( + struct edid_data_vtb_ext *ext_data, + enum vtb_byte_offset type, + uint32_t *items, + uint32_t *len) +{ + const uint8_t *byte_data = ext_data->data; + + uint32_t dt_num = ext_data->detailed_timings_num > DTB_NUM ? + DTB_NUM : ext_data->detailed_timings_num; + + uint32_t cvt_num = ext_data->cvt_descr_num > CVT_NUM ? + CVT_NUM : ext_data->cvt_descr_num; + + uint32_t stdt_num = ext_data->standard_timings_num > STDT_NUM ? + STDT_NUM : ext_data->standard_timings_num; + + switch (type) { + case VTB_BYTE_OFFSET_DETAILED_TIMING: + *len = dt_num * DTB_BYTE_SIZE; + *items = dt_num; + break; + case VTB_BYTE_OFFSET_COORDINATED_VIDEO_TIMING: + *len = cvt_num * CVT_BYTE_SIZE; + byte_data += dt_num * DTB_BYTE_SIZE; + *items = cvt_num; + break; + case VTB_BYTE_OFFSET_STANDARD_TIMING: + *len = stdt_num * STDT_BYTE_SIZE; + byte_data += dt_num * DTB_BYTE_SIZE + cvt_num * CVT_BYTE_SIZE; + *items = stdt_num; + break; + } + return byte_data; +} + +static bool add_detailed_timings( + struct edid_ext_vtb *ext, + struct dcs_mode_timing_list *list, + bool *preferred_mode_found) +{ + bool ret = false; + uint32_t i = 0; + uint32_t items = 0; + uint32_t len = 0; + const uint8_t *data = NULL; + struct mode_timing mode_timing; + + data = get_vtb_offset( + ext->data, VTB_BYTE_OFFSET_DETAILED_TIMING, &items, &len); + + for (i = 0; i < items; ++i) { + + const struct edid_detailed *detailed = + (struct edid_detailed *) (data + i * DTB_BYTE_SIZE); + + dal_memset(&mode_timing, 0, sizeof(mode_timing)); + + if (!dal_edid_detailed_to_timing( + &ext->edid, detailed, true, &mode_timing.crtc_timing)) + continue; + + /* update the mode information*/ + dal_edid_timing_to_mode_info( + &mode_timing.crtc_timing, &mode_timing.mode_info); + + /* Detailed Timing has no way of specifying pixelRepetition. + here we check if the Timing just parsed is 480i + pixelRepetion. if so we adjust the ModeTiming accordingly*/ + if (mode_timing.mode_info.flags.INTERLACE + && mode_timing.mode_info.pixel_width == 1440 + && mode_timing.mode_info.pixel_height == 480) { + mode_timing.mode_info.pixel_width /= 2; + mode_timing.crtc_timing.flags.PIXEL_REPETITION = 2; + } + + /* default to RGB 8bit */ + mode_timing.crtc_timing.display_color_depth = + DISPLAY_COLOR_DEPTH_888; + mode_timing.crtc_timing.pixel_encoding = PIXEL_ENCODING_RGB; + + /* If preferred mode yet not found - + select first detailed mode/timing as preferred */ + if (!(*preferred_mode_found)) { + mode_timing.mode_info.flags.PREFERRED = 1; + *preferred_mode_found = true; + } + + dal_dcs_mode_timing_list_append(list, &mode_timing); + + ret = true; + } + + return ret; +} + +static bool add_cvt_3byte_timing( + struct edid_ext_vtb *ext, + struct dcs_mode_timing_list *list, + bool *preferred_mode_found) +{ + bool ret = false; + uint32_t i = 0; + uint32_t items = 0; + uint32_t len = 0; + const uint8_t *data = NULL; + + data = get_vtb_offset(ext->data, + VTB_BYTE_OFFSET_COORDINATED_VIDEO_TIMING, &items, &len); + + for (i = 0; i < items; ++i) { + + const struct cvt_3byte_timing *cvt = + (struct cvt_3byte_timing *) (data + i * CVT_BYTE_SIZE); + if (dal_edid14_add_cvt_3byte_timing_from_descr( + &ext->edid, list, cvt)) + ret = true; + } + return ret; +} + +static bool retrieve_standard_mode( + struct edid_ext_vtb *ext, + const struct standard_timing *std_timing, + struct mode_info *mode_info) +{ + uint32_t h_active; + uint32_t v_active = 0; + uint32_t freq; + + /* reserve, do not use per spec*/ + if (std_timing->h_addressable == 0x00) + return false; + + /* Unused Standard Timing data fields shall be set to 01h, 01h, + as per spec*/ + if ((std_timing->h_addressable == 0x01) + && (std_timing->u.s_uchar == 0x01)) + return false; + + freq = std_timing->u.ratio_and_refresh_rate.REFRESH_RATE + 60; + h_active = (std_timing->h_addressable + 31) * 8; + + switch (std_timing->u.ratio_and_refresh_rate.RATIO) { + case RATIO_16_BY_10: + /* as per spec EDID structures prior to version 1, revision 3 + defined the bit (bits 7 & 6 at address 27h) combination of + 0 0 to indicate a 1 : 1 aspect ratio.*/ + if (ext->edid_version < 3) + v_active = h_active; + else + v_active = (h_active * 10) / 16; + break; + case RATIO_4_BY_3: + v_active = (h_active * 3) / 4; + break; + case RATIO_5_BY_4: + v_active = (h_active * 4) / 5; + break; + case RATIO_16_BY_9: + v_active = (h_active * 9) / 16; + break; + } + mode_info->pixel_width = h_active; + mode_info->pixel_height = v_active; + mode_info->field_rate = freq; + mode_info->timing_standard = TIMING_STANDARD_DMT; + mode_info->timing_source = TIMING_SOURCE_EDID_STANDARD; + + return true; +} + +static bool add_standard_timings( + struct edid_ext_vtb *ext, + struct dcs_mode_timing_list *list, + bool *preferred_mode_found) +{ + bool ret = false; + uint32_t i = 0; + uint32_t items = 0; + uint32_t len = 0; + const uint8_t *data = NULL; + struct mode_timing mode_timing; + + data = get_vtb_offset( + ext->data, VTB_BYTE_OFFSET_STANDARD_TIMING, &items, &len); + + for (i = 0; i < items; ++i) { + struct standard_timing *std_timing = + (struct standard_timing *) (data + i * STDT_BYTE_SIZE); + + if (!retrieve_standard_mode( + ext, std_timing, &mode_timing.mode_info)) + continue; + + if (!dal_edid_get_timing_for_vesa_mode( + &ext->edid, + &mode_timing.mode_info, + &mode_timing.crtc_timing)) + continue; + + /* If preferred mode yet not found - + * select first standard mode/timing as preferred*/ + if (!(*preferred_mode_found)) { + mode_timing.mode_info.flags.PREFERRED = 1; + *preferred_mode_found = true; + } + + dal_dcs_mode_timing_list_append(list, &mode_timing); + ret = true; + } + + return ret; +} + +static bool get_supported_mode_timing( + struct edid_base *edid, + struct dcs_mode_timing_list *list, + bool *preferred_mode_found) +{ + struct edid_ext_vtb *ext = FROM_EDID(edid); + + /* Calling sequence/order is important for preferred mode lookup*/ + bool det = add_detailed_timings( + ext, list, preferred_mode_found); + bool cvt = add_cvt_3byte_timing( + ext, list, preferred_mode_found); + bool stnd = add_standard_timings( + ext, list, preferred_mode_found); + + return det || cvt || stnd; +} + +static void validate(struct edid_base *edid) +{ + struct edid_ext_vtb *ext = FROM_EDID(edid); + + if (ext->data->checksum != dal_edid_compute_checksum(edid)) + edid->error.BAD_CHECKSUM = true; +} + +static const uint8_t *get_raw_data(struct edid_base *edid) +{ + struct edid_ext_vtb *ext = FROM_EDID(edid); + + return (const uint8_t *)ext->data; +} + +static const uint32_t get_raw_size(struct edid_base *edid) +{ + return sizeof(struct edid_data_vtb_ext); +} + +static uint8_t get_edid_extension_tag(struct edid_base *edid) +{ + return EDID_EXTENSION_TAG_VTB_EXT; +} + +static void destruct(struct edid_ext_vtb *edid) +{ + +} + +static void destroy(struct edid_base **edid) +{ + destruct(FROM_EDID(*edid)); + dal_free(FROM_EDID(*edid)); + *edid = NULL; +} + +static const struct edid_funcs funcs = { + .destroy = destroy, + .get_display_tile_info = dal_edid_base_get_display_tile_info, + .get_min_drr_fps = dal_edid_base_get_min_drr_fps, + .get_drr_pixel_clk_khz = dal_edid_base_get_drr_pixel_clk_khz, + .is_non_continuous_frequency = + dal_edid_base_is_non_continuous_frequency, + .get_stereo_3d_support = dal_edid_base_get_stereo_3d_support, + .validate = validate, + .get_version = dal_edid_base_get_version, + .num_of_extension = dal_edid_base_num_of_extension, + .get_edid_extension_tag = get_edid_extension_tag, + .get_cea_audio_modes = dal_edid_base_get_cea_audio_modes, + .get_cea861_support = dal_edid_base_get_cea861_support, + .get_display_pixel_encoding = dal_edid_base_get_display_pixel_encoding, + .get_display_color_depth = dal_edid_base_get_display_color_depth, + .get_connector_type = dal_edid_base_get_connector_type, + .get_screen_info = dal_edid_base_get_screen_info, + .get_display_characteristics = + dal_edid_base_get_display_characteristics, + .get_monitor_range_limits = dal_edid_base_get_monitor_range_limits, + .get_display_name = dal_edid_base_get_display_name, + .get_vendor_product_id_info = dal_edid_base_get_vendor_product_id_info, + .get_supported_mode_timing = get_supported_mode_timing, + .get_cea_video_capability_data_block = + dal_edid_base_get_cea_video_capability_data_block, + .get_cea_colorimetry_data_block = + dal_edid_base_get_cea_colorimetry_data_block, + .get_cea_speaker_allocation_data_block = + dal_edid_base_get_cea_speaker_allocation_data_block, + .get_cea_vendor_specific_data_block = + dal_edid_base_get_cea_vendor_specific_data_block, + .get_raw_size = get_raw_size, + .get_raw_data = get_raw_data, +}; + +static bool construct( + struct edid_ext_vtb *ext, + struct timing_service *ts, + uint32_t len, + const uint8_t *buf, + uint32_t version) +{ + if (len == 0 || buf == NULL) + return false; + + if (!dal_edid_ext_vtb_is_vtb_ext(len, buf)) + return false; + + if (!dal_edid_base_construct(&ext->edid, ts)) + return false; + + ext->edid_version = version; + ext->data = (struct edid_data_vtb_ext *)buf; + + ext->edid.funcs = &funcs; + return true; +} + +struct edid_base *dal_edid_ext_vtb_create( + struct timing_service *ts, + uint32_t len, + const uint8_t *buf, + uint32_t version) +{ + struct edid_ext_vtb *ext = NULL; + + ext = dal_alloc(sizeof(struct edid_ext_vtb)); + + if (!ext) + return NULL; + + if (construct(ext, ts, len, buf, version)) + return &ext->edid; + + dal_free(ext); + BREAK_TO_DEBUGGER(); + return NULL; +} + +bool dal_edid_ext_vtb_is_vtb_ext(uint32_t len, const uint8_t *buf) +{ + const struct edid_data_vtb_ext *ext; + + if (len < sizeof(struct edid_data_vtb_ext)) + return false; + + ext = (const struct edid_data_vtb_ext *)buf; + + if (!(EDID_EXTENSION_TAG_VTB_EXT == ext->extension_tag)) + return false; + + return true; +} diff --git a/drivers/gpu/drm/amd/dal/dcs/edid/edid_ext_vtb.h b/drivers/gpu/drm/amd/dal/dcs/edid/edid_ext_vtb.h new file mode 100644 index 000000000000..cbf974f5ed29 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/edid/edid_ext_vtb.h @@ -0,0 +1,38 @@ +/* + * Copyright 2012-15 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 + * + */ + +#ifndef __DAL_EDID_EXT_VTB_H__ +#define __DAL_EDID_EXT_VTB_H__ + +struct edid_base *dal_edid_ext_vtb_create( + struct timing_service *ts, + uint32_t len, + const uint8_t *buf, + uint32_t version); + +bool dal_edid_ext_vtb_is_vtb_ext(uint32_t len, const uint8_t *buf); + + +#endif /* __DAL_EDID_EXT_VTB_H__ */ diff --git a/drivers/gpu/drm/amd/dal/dcs/edid_mgr.c b/drivers/gpu/drm/amd/dal/dcs/edid_mgr.c new file mode 100644 index 000000000000..5938c3eb0b61 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/edid_mgr.c @@ -0,0 +1,488 @@ +/* + * Copyright 2012-15 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 + * + */ + +#include "dal_services.h" + +#include "include/ddc_service_types.h" + +#include "edid/edid13.h" +#include "edid/edid14.h" +#include "edid/edid20.h" +#include "edid/edid_ext_cea.h" +#include "edid/edid_ext_di.h" +#include "edid/edid_ext_vtb.h" +#include "edid/edid_ext_unknown.h" + +#include "edid_patch.h" +#include "edid_mgr.h" + +static struct edid_base *create_edid_base_block( + uint32_t len, + const uint8_t *buf, + struct timing_service *ts) +{ + struct edid_base *edid_base = NULL; + + if (dal_edid14_is_v_14(len, buf)) + edid_base = dal_edid14_create(ts, len, buf); + else if (dal_edid13_is_v_13(len, buf)) + edid_base = dal_edid13_create(ts, len, buf); + else if (dal_edid20_is_v_20(len, buf)) + edid_base = dal_edid20_create(ts, len, buf); + + if (edid_base) + dal_edid_validate(edid_base); + + return edid_base; +} + + +static struct edid_base *create_edid_ext_block( + uint32_t len, + const uint8_t *buf, + uint32_t edid_ver, + struct timing_service *ts, + struct edid_patch *edid_patch) +{ + struct edid_base *ext = NULL; + + if (dal_edid_ext_cea_is_cea_ext(len, buf)) { + struct edid_ext_cea_init_data init_data; + + init_data.ts = ts; + init_data.len = len; + init_data.buf = buf; + init_data.edid_patch = edid_patch; + ext = dal_edid_ext_cea_create(&init_data); + } else if (dal_edid_ext_di_is_di_ext(len, buf)) + ext = dal_edid_ext_di_create(ts, len, buf); + else if (dal_edid_ext_vtb_is_vtb_ext(len, buf)) + ext = dal_edid_ext_vtb_create(ts, len, buf, edid_ver); + else if (dal_edid_ext_unknown_is_unknown_ext(len, buf)) + ext = dal_edid_ext_unknown_create(ts, len, buf); + + if (ext) + dal_edid_validate(ext); + + return ext; +} + +static struct edid_base *create_edid_block( + struct edid_mgr *edid_mgr, + uint32_t len, + const uint8_t *buf) +{ + struct edid_base *head = NULL; + struct edid_base *edid = NULL; + uint32_t i; + + head = create_edid_base_block(len, buf, edid_mgr->ts); + if (!head || dal_edid_get_errors(head)->BAD_CHECKSUM) { + BREAK_TO_DEBUGGER(); + return head; + } + + edid = head; + len -= dal_edid_get_size(head); + buf += dal_edid_get_size(head); + + for (i = 0; i < dal_edid_get_num_of_extension(head); ++i) { + + struct edid_base *ext = + create_edid_ext_block( + len, + buf, + dal_edid_get_version(head), + edid_mgr->ts, + edid_mgr->edid_patch); + + if (!ext) { + BREAK_TO_DEBUGGER(); + break; + } + + dal_edid_set_next_block(edid, ext); + + len -= dal_edid_get_size(ext); + buf += dal_edid_get_size(ext); + + edid = ext; + } + + return head; +} + +static void free_edid_handle( + struct edid_mgr *edid_mgr, + struct edid_handle *edid_handle) +{ + if (!edid_handle) + return; + + if (edid_mgr->edid_list == edid_handle->edid_list) + edid_mgr->edid_list = NULL; + + if (edid_handle->edid_list) { + dal_edid_list_destroy(edid_handle->edid_list); + edid_handle->edid_list = NULL; + } + + if (edid_handle->edid_buffer) { + dal_free(edid_handle->edid_buffer); + edid_handle->edid_buffer = NULL; + } + + if (edid_handle->patched_edid_buffer) { + dal_free(edid_handle->patched_edid_buffer); + edid_handle->patched_edid_buffer = NULL; + } + + edid_handle->buffer_size = 0; +} + + +static bool edid_handle_construct( + struct edid_mgr *edid_mgr, + struct edid_handle *edid_handle, + bool apply_patches) +{ + if (apply_patches && + dal_edid_patch_get_patches_number(edid_mgr->edid_patch) > 0) + edid_handle->patched_edid_buffer = + dal_alloc(edid_handle->buffer_size); + + if (edid_handle->patched_edid_buffer) { + dal_memmove(edid_handle->patched_edid_buffer, + edid_handle->edid_buffer, + edid_handle->buffer_size); + dal_edid_patch_apply( + edid_mgr->edid_patch, + edid_handle->patched_edid_buffer); + edid_handle->edid_list = create_edid_block( + edid_mgr, + edid_handle->buffer_size, + edid_handle->patched_edid_buffer); + } else + edid_handle->edid_list = create_edid_block( + edid_mgr, + edid_handle->buffer_size, + edid_handle->edid_buffer); + + if (!edid_handle->edid_list) + free_edid_handle(edid_mgr, edid_handle); + + return edid_handle->edid_list != NULL; +} + +static bool edid_handle_destruct( + struct edid_handle *edid_handle) +{ + return false; +} + +static bool allocate_edid_handle( + struct edid_mgr *edid_mgr, + struct edid_handle *edid_handle, + uint32_t len, + const uint8_t *buf) +{ + free_edid_handle(edid_mgr, edid_handle); + + edid_handle->edid_buffer = dal_alloc(len); + + if (!edid_handle->edid_buffer) + return false; + + dal_memmove(edid_handle->edid_buffer, buf, len); + edid_handle->buffer_size = len; + dal_edid_patch_initialize( + edid_mgr->edid_patch, + edid_handle->edid_buffer, + edid_handle->buffer_size); + + return true; +} + +static bool is_same_edid_raw_data( + struct edid_handle *edid_handle, + uint32_t len, + const uint8_t *buf) +{ + /* We consider comparison failed when we do not have edid blocks*/ + if (buf == NULL || + edid_handle->edid_list == NULL || + len != edid_handle->buffer_size) + return false; + + return dal_memcmp(edid_handle->edid_buffer, buf, len) == 0; +} + + + +enum edid_retrieve_status dal_edid_mgr_override_raw_data( + struct edid_mgr *edid_mgr, + uint32_t len, + const uint8_t *buf) +{ + struct edid_handle *edid_handle = + edid_mgr->override_edid_handle.edid_list != NULL ? + &edid_mgr->override_edid_handle : + &edid_mgr->edid_handle; + + if (len == 0 || buf == NULL) { + /* Request to delete override Edid*/ + if (edid_mgr->override_edid_handle.edid_list == NULL) { + free_edid_handle( + edid_mgr, + &edid_mgr->override_edid_handle); + return EDID_RETRIEVE_SAME_EDID; + } + + /*We need to return back to physical Edid - + * consider it as successful override to new EDID*/ + dal_edid_patch_initialize( + edid_mgr->edid_patch, + edid_handle->edid_buffer, + edid_handle->buffer_size); + free_edid_handle( + edid_mgr, + &edid_mgr->override_edid_handle); + return EDID_RETRIEVE_SUCCESS; + } + + /* New override same as current override/physical: Nothing to do */ + if (is_same_edid_raw_data(edid_handle, len, buf)) + return EDID_RETRIEVE_SAME_EDID; + + /* Allocate buffer for override Edid and copy there new one */ + if (!allocate_edid_handle( + edid_mgr, + &edid_mgr->override_edid_handle, + len, + buf)) + return EDID_RETRIEVE_FAIL; + + /* Initialized Override Edid handle without patching it + * (are we sure we do not want ot patch it?) */ + if (!edid_handle_construct( + edid_mgr, &edid_mgr->override_edid_handle, false)) + return EDID_RETRIEVE_FAIL; + + /* successfully */ + edid_mgr->edid_list = edid_mgr->override_edid_handle.edid_list; + + return EDID_RETRIEVE_SUCCESS; +} + +enum edid_retrieve_status dal_edid_mgr_update_edid_raw_data( + struct edid_mgr *edid_mgr, + uint32_t len, + const uint8_t *buf) +{ + enum edid_retrieve_status ret = EDID_RETRIEVE_FAIL; + + if (edid_mgr->edid_handle.edid_list) + ret = EDID_RETRIEVE_FAIL_WITH_PREVIOUS_SUCCESS; + + /* Request to delete Edid */ + if (len == 0 || buf == NULL) { + free_edid_handle(edid_mgr, &edid_mgr->edid_handle); + free_edid_handle(edid_mgr, &edid_mgr->override_edid_handle); + } else { + + /* If new Edid buffer same as current - ignore*/ + if (is_same_edid_raw_data(&edid_mgr->edid_handle, len, buf)) { + ret = EDID_RETRIEVE_SAME_EDID; + } else { + /* Allocate buffer for physical Edid + * and copy there new one */ + if (allocate_edid_handle( + edid_mgr, + &edid_mgr->edid_handle, len, buf)) { + edid_mgr->edid_list = + edid_mgr->edid_handle.edid_list; + ret = EDID_RETRIEVE_SUCCESS; + } + } + + } + + /* On failure we update previous status here, on success we will + * update prev status in UpdateEdidFromLastRetrieved */ + if (ret == EDID_RETRIEVE_FAIL || + ret == EDID_RETRIEVE_FAIL_WITH_PREVIOUS_SUCCESS) + edid_mgr->prev_status = ret; + + return ret; +} + +enum edid_retrieve_status dal_edid_mgr_update_edid_from_last_retrieved( + struct edid_mgr *edid_mgr) +{ +/* Initialize physical Edid handle including patching it. If we successfully + * initialized - then override Edid has to be removed (otherwise it will mask + * new Edid), so override Edid good until we detect new monitor */ + + enum edid_retrieve_status ret = EDID_RETRIEVE_FAIL; + + if (edid_mgr->edid_handle.edid_buffer != NULL) { + + if (edid_handle_construct( + edid_mgr, &edid_mgr->edid_handle, true)) { + + edid_handle_destruct(&edid_mgr->override_edid_handle); + ret = EDID_RETRIEVE_SUCCESS; + edid_mgr->edid_list = edid_mgr->edid_handle.edid_list; + + } else if (edid_mgr->prev_status == EDID_RETRIEVE_SUCCESS) { + + BREAK_TO_DEBUGGER(); + ret = EDID_RETRIEVE_FAIL_WITH_PREVIOUS_SUCCESS; + } + } else { + BREAK_TO_DEBUGGER(); + } + + edid_mgr->prev_status = ret; + return ret; +} + +uint32_t dal_edid_mgr_get_edid_raw_data_size( + const struct edid_mgr *edid_mgr) +{ + if (edid_mgr->override_edid_handle.edid_list != NULL) + return edid_mgr->override_edid_handle.buffer_size; + else if (edid_mgr->edid_handle.edid_list != NULL) + return edid_mgr->edid_handle.buffer_size; + + return 0; +} + +const uint8_t *dal_edid_mgr_get_edid_raw_data( + const struct edid_mgr *edid_mgr, + uint32_t *size) +{ + const struct edid_handle *edid_handle = NULL; + + if (edid_mgr->override_edid_handle.edid_list) + edid_handle = &edid_mgr->override_edid_handle; + else if (edid_mgr->edid_handle.edid_list) + edid_handle = &edid_mgr->edid_handle; + + if (!edid_handle) + return NULL; + + if (size) + *size = edid_handle->buffer_size; + + return edid_handle->patched_edid_buffer != NULL ? + edid_handle->patched_edid_buffer : edid_handle->edid_buffer; +} + +struct edid_base *dal_edid_mgr_get_edid( + const struct edid_mgr *edid_mgr) +{ + return edid_mgr->edid_list; +} + +const struct monitor_patch_info *dal_edid_mgr_get_monitor_patch_info( + const struct edid_mgr *edid_mgr, + enum monitor_patch_type type) +{ + return dal_edid_patch_get_monitor_patch_info( + edid_mgr->edid_patch, type); +} + +bool dal_edid_mgr_set_monitor_patch_info( + struct edid_mgr *edid_mgr, + struct monitor_patch_info *info) +{ + return dal_edid_patch_set_monitor_patch_info( + edid_mgr->edid_patch, info); +} + +union dcs_monitor_patch_flags dal_edid_mgr_get_monitor_patch_flags( + const struct edid_mgr *edid_mgr) +{ + return dal_edid_patch_get_monitor_patch_flags(edid_mgr->edid_patch); +} + +void dal_edid_mgr_update_dp_receiver_id_based_monitor_patches( + struct edid_mgr *edid_mgr, + struct dp_receiver_id_info *info) +{ + dal_edid_patch_update_dp_receiver_id_based_monitor_patches( + edid_mgr->edid_patch, + info); +} + +static bool construct(struct edid_mgr *edid_mgr, + struct timing_service *ts, + struct adapter_service *as) +{ + edid_mgr->ts = ts; + + edid_mgr->edid_patch = dal_edid_patch_create(as); + + if (!edid_mgr->edid_patch) + return false; + + return true; +} + +struct edid_mgr *dal_edid_mgr_create( + struct timing_service *ts, + struct adapter_service *as) +{ + struct edid_mgr *edid_mgr = dal_alloc(sizeof(struct edid_mgr)); + + if (!edid_mgr) + return NULL; + + if (construct(edid_mgr, ts, as)) + return edid_mgr; + + dal_free(edid_mgr); + return NULL; +} + +static void destruct(struct edid_mgr *edid_mgr) +{ + free_edid_handle(edid_mgr, &edid_mgr->edid_handle); + free_edid_handle(edid_mgr, &edid_mgr->override_edid_handle); + dal_edid_patch_destroy(&edid_mgr->edid_patch); +} + +void dal_edid_mgr_destroy(struct edid_mgr **mgr) +{ + + if (!mgr || !*mgr) + return; + + destruct(*mgr); + dal_free(*mgr); + *mgr = NULL; + +} diff --git a/drivers/gpu/drm/amd/dal/dcs/edid_mgr.h b/drivers/gpu/drm/amd/dal/dcs/edid_mgr.h new file mode 100644 index 000000000000..9957dacd70a0 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/edid_mgr.h @@ -0,0 +1,98 @@ +/* + * Copyright 2012-15 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 + * + */ + +#ifndef __DAL_EDID_MGR_H__ +#define __DAL_EDID_MGR_H__ + +#include "edid/edid_base.h" + +struct dp_receiver_id_info; + +struct edid_handle { + struct edid_base *edid_list; + uint8_t *edid_buffer; + uint8_t *patched_edid_buffer; + uint32_t buffer_size; +}; + +struct edid_patch; + +struct edid_mgr { + struct timing_service *ts; + enum edid_retrieve_status prev_status; + struct edid_handle edid_handle; + struct edid_handle override_edid_handle; + struct edid_base *edid_list; + struct edid_patch *edid_patch; + +}; + +struct adapter_service; + +struct edid_mgr *dal_edid_mgr_create( + struct timing_service *ts, + struct adapter_service *as); + +void dal_edid_mgr_destroy(struct edid_mgr **mgr); + +enum edid_retrieve_status dal_edid_mgr_override_raw_data( + struct edid_mgr *edid_mgr, + uint32_t len, + const uint8_t *buf); + +enum edid_retrieve_status dal_edid_mgr_update_edid_raw_data( + struct edid_mgr *edid_mgr, + uint32_t len, + const uint8_t *buf); + +enum edid_retrieve_status dal_edid_mgr_update_edid_from_last_retrieved( + struct edid_mgr *edid_mgr); + +uint32_t dal_edid_mgr_get_edid_raw_data_size( + const struct edid_mgr *edid_mgr); + +const uint8_t *dal_edid_mgr_get_edid_raw_data( + const struct edid_mgr *edid_mgr, + uint32_t *size); + +struct edid_base *dal_edid_mgr_get_edid( + const struct edid_mgr *edid_mgr); + +const struct monitor_patch_info *dal_edid_mgr_get_monitor_patch_info( + const struct edid_mgr *edid_mgr, + enum monitor_patch_type type); + +bool dal_edid_mgr_set_monitor_patch_info( + struct edid_mgr *edid_mgr, + struct monitor_patch_info *info); + +union dcs_monitor_patch_flags dal_edid_mgr_get_monitor_patch_flags( + const struct edid_mgr *edid_mgr); + +void dal_edid_mgr_update_dp_receiver_id_based_monitor_patches( + struct edid_mgr *edid_mgr, + struct dp_receiver_id_info *info); + +#endif /* __DAL_EDID_MGR_H__ */ diff --git a/drivers/gpu/drm/amd/dal/dcs/edid_patch.c b/drivers/gpu/drm/amd/dal/dcs/edid_patch.c new file mode 100644 index 000000000000..7b8919241262 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/edid_patch.c @@ -0,0 +1,920 @@ +/* + * Copyright 2012-15 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 + * + */ + +#include "dal_services.h" + +#include "include/vector.h" +#include "include/dcs_types.h" +#include "include/ddc_service_types.h" +#include "include/adapter_service_interface.h" + +#include "edid_patch.h" +#include "edid/edid_base.h" +#include "edid/edid.h" +#include "edid/edid1x_data.h" + +struct monitor_patch_list { + struct vector vector; + union dcs_monitor_patch_flags flags; +}; + +static bool monitor_patch_list_construct(struct monitor_patch_list *mpl) +{ + return dal_vector_construct(&mpl->vector, 64, + sizeof(struct monitor_patch_info)); +} + +static void monitor_patch_list_destruct(struct monitor_patch_list *mpl) +{ + dal_vector_destruct(&mpl->vector); +} + +static void monitor_patch_list_insert( + struct monitor_patch_list *mpl, + const struct monitor_patch_info *info) +{ + switch (info->type) { + case MONITOR_PATCH_TYPE_ERROR_CHECKSUM: + mpl->flags.flags.ERROR_CHECKSUM = true; + break; + case MONITOR_PATCH_TYPE_HDTV_WITH_PURE_DFP_EDID: + mpl->flags.flags.HDTV_WITH_PURE_DFP_EDID = true; + break; + case MONITOR_PATCH_TYPE_DO_NOT_USE_DETAILED_TIMING: + mpl->flags.flags.DO_NOT_USE_DETAILED_TIMING = true; + break; + case MONITOR_PATCH_TYPE_DO_NOT_USE_RANGE_LIMITATION: + mpl->flags.flags.DO_NOT_USE_RANGE_LIMITATION = true; + break; + case MONITOR_PATCH_TYPE_EDID_EXTENTION_ERROR_CHECKSUM: + mpl->flags.flags.EDID_EXTENTION_ERROR_CHECKSUM = true; + break; + case MONITOR_PATCH_TYPE_TURN_OFF_DISPLAY_BEFORE_MODE_CHANGE: + mpl->flags.flags.TURN_OFF_DISPLAY_BEFORE_MODE_CHANGE = true; + break; + case MONITOR_PATCH_TYPE_RESTRICT_VESA_MODE_TIMING: + mpl->flags.flags.RESTRICT_VESA_MODE_TIMING = true; + break; + case MONITOR_PATCH_TYPE_DO_NOT_USE_EDID_MAX_PIX_CLK: + mpl->flags.flags.DO_NOT_USE_EDID_MAX_PIX_CLK = true; + break; + case MONITOR_PATCH_TYPE_VENDOR_0: + mpl->flags.flags.VENDOR_0 = true; + break; + case MONITOR_PATCH_TYPE_RANDOM_CRT: + mpl->flags.flags.RANDOM_CRT = true; + break; + case MONITOR_PATCH_TYPE_VENDOR_1: + mpl->flags.flags.VENDOR_1 = true; + break; + case MONITOR_PATCH_TYPE_LIMIT_PANEL_SUPPORT_RGB_ONLY: + mpl->flags.flags.LIMIT_PANEL_SUPPORT_RGB_ONLY = true; + break; + case MONITOR_PATCH_TYPE_PACKED_PIXEL_FORMAT: + mpl->flags.flags.PACKED_PIXEL_FORMAT = true; + break; + case MONITOR_PATCH_TYPE_LARGE_PANEL: + mpl->flags.flags.LARGE_PANEL = true; + break; + case MONITOR_PATCH_TYPE_STEREO_SUPPORT: + mpl->flags.flags.STEREO_SUPPORT = true; + break; + case MONITOR_PATCH_TYPE_DUAL_EDID_PANEL: + mpl->flags.flags.DUAL_EDID_PANEL = true; + break; + case MONITOR_PATCH_TYPE_IGNORE_19X12_STD_TIMING: + mpl->flags.flags.IGNORE_19X12_STD_TIMING = true; + break; + case MONITOR_PATCH_TYPE_MULTIPLE_PACKED_TYPE: + mpl->flags.flags.MULTIPLE_PACKED_TYPE = true; + break; + case MONITOR_PATCH_TYPE_RESET_TX_ON_DISPLAY_POWER_ON: + mpl->flags.flags.RESET_TX_ON_DISPLAY_POWER_ON = true; + break; + case MONITOR_PATCH_TYPE_ALLOW_ONLY_CE_MODE: + mpl->flags.flags.ALLOW_ONLY_CE_MODE = true; + break; + case MONITOR_PATCH_TYPE_RESTRICT_PROT_DUAL_LINK_DVI: + mpl->flags.flags.RESTRICT_PROT_DUAL_LINK_DVI = true; + break; + case MONITOR_PATCH_TYPE_FORCE_LINK_RATE: + mpl->flags.flags.FORCE_LINK_RATE = true; + break; + case MONITOR_PATCH_TYPE_DELAY_AFTER_DP_RECEIVER_POWER_UP: + mpl->flags.flags.DELAY_AFTER_DP_RECEIVER_POWER_UP = true; + break; + case MONITOR_PATCH_TYPE_KEEP_DP_RECEIVER_POWERED: + mpl->flags.flags.KEEP_DP_RECEIVER_POWERED = true; + break; + case MONITOR_PATCH_TYPE_DELAY_BEFORE_READ_EDID: + mpl->flags.flags.DELAY_BEFORE_READ_EDID = true; + break; + case MONITOR_PATCH_TYPE_DELAY_AFTER_PIXEL_FORMAT_CHANGE: + mpl->flags.flags.DELAY_AFTER_PIXEL_FORMAT_CHANGE = true; + break; + case MONITOR_PATCH_TYPE_INCREASE_DEFER_WRITE_RETRY_I2C_OVER_AUX: + mpl->flags.flags.INCREASE_DEFER_WRITE_RETRY_I2C_OVER_AUX = true; + break; + case MONITOR_PATCH_TYPE_NO_DEFAULT_TIMINGS: + mpl->flags.flags.NO_DEFAULT_TIMINGS = true; + break; + case MONITOR_PATCH_TYPE_ADD_CEA861_DETAILED_TIMING_VIC16: + mpl->flags.flags.ADD_CEA861_DETAILED_TIMING_VIC16 = true; + break; + case MONITOR_PATCH_TYPE_ADD_CEA861_DETAILED_TIMING_VIC31: + mpl->flags.flags.ADD_CEA861_DETAILED_TIMING_VIC31 = true; + break; + case MONITOR_PATCH_TYPE_DELAY_BEFORE_UNMUTE: + mpl->flags.flags.DELAY_BEFORE_UNMUTE = true; + break; + case MONITOR_PATCH_TYPE_RETRY_LINK_TRAINING_ON_FAILURE: + mpl->flags.flags.RETRY_LINK_TRAINING_ON_FAILURE = true; + break; + case MONITOR_PATCH_TYPE_ALLOW_AUX_WHEN_HPD_LOW: + mpl->flags.flags.ALLOW_AUX_WHEN_HPD_LOW = true; + break; + case MONITOR_PATCH_TYPE_TILED_DISPLAY: + mpl->flags.flags.TILED_DISPLAY = true; + break; + case MONITOR_PATCH_TYPE_DISABLE_PSR_ENTRY_ABORT: + mpl->flags.flags.DISABLE_PSR_ENTRY_ABORT = true; + break; + case MONITOR_PATCH_TYPE_VID_STREAM_DIFFER_TO_SYNC: + mpl->flags.flags.VID_STREAM_DIFFER_TO_SYNC = true; + break; + case MONITOR_PATCH_TYPE_DELAY_AFTER_DISABLE_BACKLIGHT_DFS_BYPASS: + mpl->flags.flags.DELAY_AFTER_DISABLE_BACKLIGHT_DFS_BYPASS = + true; + break; + default: + break; + } + dal_vector_append(&mpl->vector, info); +} + +static inline struct monitor_patch_info *monitor_patch_list_get_patch_info( + struct monitor_patch_list *mpl, + enum monitor_patch_type type) +{ + uint32_t i; + + for (i = 0; i < dal_vector_get_count(&mpl->vector); ++i) { + struct monitor_patch_info *info = + dal_vector_at_index(&mpl->vector, i); + if (info->type == type) + return info; + } + + return NULL; +} + +static inline uint32_t monitor_patch_list_size(struct monitor_patch_list *mpl) +{ + return dal_vector_get_count(&mpl->vector); +} + +static struct monitor_patch_info *monitor_patch_list_get_patch_at( + struct monitor_patch_list *mpl, + uint32_t i) +{ + return dal_vector_at_index(&mpl->vector, i); +} + +struct edid_patch { + struct monitor_patch_list mpl; + enum monitor_manufacturer_id manufacturer_id; + enum monitor_product_id product_id; + uint8_t extensions_num; + uint8_t edid_version_major; + uint8_t edid_version_minor; + union dcs_monitor_patch_flags flags; + enum dcs_packed_pixel_format packed_pixel_format; +}; + +static bool construct(struct edid_patch *ep, struct adapter_service *as) +{ + if (!as) + return false; + + ep->packed_pixel_format = + dal_adapter_service_get_feature_flags(as). + bits.PACKED_PIXEL_FORMAT; + + return monitor_patch_list_construct(&ep->mpl); +} + +struct edid_patch *dal_edid_patch_create(struct adapter_service *as) +{ + struct edid_patch *ep = dal_alloc(sizeof(struct edid_patch)); + + if (!ep) + return NULL; + + if (construct(ep, as)) + return ep; + + dal_free(ep); + return NULL; +} + +static void destruct(struct edid_patch *ep) +{ + monitor_patch_list_destruct(&ep->mpl); +} + +void dal_edid_patch_destroy(struct edid_patch **ep) +{ + if (!ep || !*ep) + return; + destruct(*ep); + dal_free(*ep); + *ep = NULL; +} + +static enum edid_tiled_display_type translate_tiled_display( + enum monitor_manufacturer_id manufacturer_id, + enum monitor_product_id product_id) +{ + if (manufacturer_id == MONITOR_MANUFACTURER_ID_14 && + product_id == MONITOR_PRODUCT_ID_37) + return EDID_TILED_DISPLAY_1; + + if (manufacturer_id == MONITOR_MANUFACTURER_ID_20 && + product_id == MONITOR_PRODUCT_ID_42) + return EDID_TILED_DISPLAY_2; + + return EDID_TILED_DISPLAY_NONE; +} + +/* + * dal_edid_patch_initialize + * + * Parses EDID manufacture/product and based on this, initialize the instance. + * Actually binds the instance to this EDID. + * Should be called before any other API. + * + */ +bool dal_edid_patch_initialize( + struct edid_patch *ep, + const uint8_t *edid_buf, + uint32_t edid_len) +{ + uint32_t entries_num = dal_monitor_tables_get_count(); + uint32_t i; + + if (edid_buf == NULL) + return false; + + if (!dal_edid_get_version_raw( + edid_buf, + edid_len, + &ep->edid_version_major, + &ep->edid_version_minor)) + return false; + + if (ep->edid_version_major == 1) { + const struct edid_data_v1x *edid_data = + (const struct edid_data_v1x *) edid_buf; + + ep->manufacturer_id = (edid_data->vendor_id[1] << 8) + + edid_data->vendor_id[0]; + ep->product_id = (edid_data->vendor_id[3] << 8) + + edid_data->vendor_id[2]; + ep->extensions_num = edid_data->ext_blk_cnt; + } else { + return false; + } + + /* Build patch list. Packed pixel format property will be cached, + * besides list entry */ + for (i = 0; i < entries_num; ++i) { + const struct monitor_patch_info *entry = + dal_monitor_tables_get_entry_at(i); + + if (entry == NULL) + continue; + + if ((entry->manufacturer_id == ep->manufacturer_id && + (entry->product_id == ep->product_id || + entry->product_id == + MONITOR_PRODUCT_ID_0)) || + (entry->manufacturer_id == + MONITOR_MANUFACTURER_ID_0 && + entry->product_id == + MONITOR_PRODUCT_ID_0)) { + + struct monitor_patch_info info = *entry; + + if (info.type == MONITOR_PATCH_TYPE_TILED_DISPLAY) + info.param = + translate_tiled_display( + entry->manufacturer_id, + entry->product_id); + + /* Insert will never add patch with same type */ + monitor_patch_list_insert(&ep->mpl, &info); + } + } + + return true; +} + +static void patch_header_error(uint8_t *buff) +{ + buff[0] = 0x00; + buff[1] = 0xff; + buff[2] = 0xff; + buff[3] = 0xff; + buff[4] = 0xff; + buff[5] = 0xff; + buff[6] = 0xff; + buff[7] = 0x00; +} + +static void patch_vendor1_workaround( + struct edid_patch *ep, + uint8_t *buff) +{ + /* Edid wrong with zero VBlank, regulate it according to + * CEA-861rCV2 specification.This monitor DO support below established + * timing with HDMI interface. But it doesn't report. + */ + buff[0x23] = 0xad; + buff[0x24] = 0xcf; + + /* 1st detailed timing entry at extended block: Mode 1920 x 540 x 30, + * the ucVBlankingL8 should be 0x16 rather than 0x00 + * according to specification. After correcting ucVBlankingL8, the + * checksum is right also! + */ + if (ep->extensions_num > 0 && buff[0xa0] == 0) + buff[0xa0] = 0x16; +} + +/* + * patch_checksum_error + * + * Recalculates and writes back checksum of EDID block. It can be base block or + * valid extension + */ +static void patch_checksum_error( + struct edid_patch *ep, + uint8_t *buff, + uint32_t block_number) +{ + uint32_t length = EDID_VER_1_STDLEN; + uint8_t checksum = 0; + uint32_t i; + + /* blockNumber = extension index + 1 */ + if (block_number > ep->extensions_num) + return; + + buff += block_number * length; + + for (i = 0; i < length - 1; ++i) + checksum += buff[i]; + + buff[length-1] = (uint8_t)(0x100 - checksum); +} + +static void get_edid1xx_std_mode( + struct edid_patch *ep, + const struct standard_timing *std_timing, + struct mode_info *mode_info) +{ + uint32_t v_active = 0; + uint32_t h_active; + uint32_t frequency; + /* Unused Standard Timing data fields shall be set to 01h, 01h, as per + * specification */ + if (std_timing->h_addressable == 0x00 || + (std_timing->h_addressable == 0x01 && + std_timing->u.s_uchar == 0x01)) + return; + + h_active = (std_timing->h_addressable + 31) * 8; + frequency = std_timing->u.ratio_and_refresh_rate.REFRESH_RATE + 60; + + switch (std_timing->u.ratio_and_refresh_rate.RATIO) { + case RATIO_16_BY_10: + if (ep->edid_version_major == 1 && + ep->edid_version_minor < 3) { + /* as per spec EDID structures prior to version 1, + * revision 3 defined the bit (bits 7 & 6 at address + * 27h) combination of 0 0 to indicate a 1 : 1 aspect + * ratio. + */ + v_active = h_active; + } else + v_active = (h_active * 10) / 16; + break; + case RATIO_4_BY_3: + v_active = (h_active * 3) / 4; + break; + + case RATIO_5_BY_4: + v_active = (h_active * 4) / 5; + break; + + case RATIO_16_BY_9: + v_active = (h_active * 9) / 16; + break; + + default: + break; + } + + mode_info->pixel_width = h_active; + mode_info->pixel_height = v_active; + mode_info->field_rate = frequency; + mode_info->timing_standard = TIMING_STANDARD_DMT; + mode_info->timing_source = TIMING_SOURCE_EDID_STANDARD; +} + +/* + * patch_19x12_std_timing_error + * + * Removes 1900x1200@60 mode from standard modes section (does not check + * detailed timings section for std modes). + */ +static void patch_19x12_std_timing_error( + struct edid_patch *ep, + uint8_t *buff) +{ + bool checksum_changed = false; + uint32_t i; + + /* Parse standard timings in standard timing section */ + for (i = 0; i < NUM_OF_EDID1X_STANDARD_TIMING; ++i) { + struct mode_info info = { 0 }; + struct standard_timing *timing = + (struct standard_timing *)&buff[0x26+(i*2)]; + + get_edid1xx_std_mode(ep, timing, &info); + + if (info.pixel_width == 1920 && info.pixel_height == 1200 && + info.field_rate == 60) { + timing->h_addressable = 0x01; + timing->u.s_uchar = 0x01; + checksum_changed = true; + break; + } + } + + if (checksum_changed) + patch_checksum_error(ep, buff, 0); +} + +/* + * patch_dual_edid_panel_error + * + * Patches the edid by forcing the digital edid to an analog edid. It does so by + * zeroing the digital byte. + */ +static void patch_dual_edid_panel_error( + struct edid_patch *ep, + uint8_t *buff) +{ + if (ep->edid_version_major == 1) { + struct edid_data_v1x *edid_data = + (struct edid_data_v1x *)buff; + + /* bit 7 of byte 0 of basicDisplayParameters determines digital + * (bit 7 = 1) or analog (bit 7 = 0) + */ + if (edid_data->basic_display_params[0] & 0x80) { + /* clear out the entire byte, because the rest of bits + * 0 - 6 have different meanings depending on bit 7, + * so we shouldn't keep the existing bits. + */ + edid_data->basic_display_params[0] = 0; + + /* Analog Edid cannot have extensions */ + edid_data->ext_blk_cnt = 0; + + /* we also want to recalculate the checksum */ + patch_checksum_error(ep, buff, 0); + } + } +} + +/* + * patch_multipacked_type_panel_edid + * + * Patch the Edid detailed timing blocks based on reserved manufacture timing + * byte, and .inf select pack type. + */ +static void patch_multipacked_type_panel_edid( + struct edid_patch *ep, + uint8_t *buff) +{ + uint32_t i; + bool checksum_changed = false; + struct edid_data_v1x *edid_data = (struct edid_data_v1x *)&buff[0]; + const struct monitor_patch_info *info = + monitor_patch_list_get_patch_info( + &ep->mpl, + MONITOR_PATCH_TYPE_MULTIPLE_PACKED_TYPE); + /* check whether the packed type. */ + union edid13_multipacked_panel_manufacture_reserved_timing_info timing_info; + + timing_info.all = edid_data->established_timings[2]; + + if (timing_info.all == 0) + return; /*this panel is not packed pixel panel. do nothing. */ + + if (info->param == DCS_PACKED_PIXEL_FORMAT_B70_G70_R70 && + !timing_info.bits.G8) + return; + + if (info->param == DCS_PACKED_PIXEL_FORMAT_B70_R30_G70_R74 && + !timing_info.bits.G10 && + !timing_info.bits.G12) + return; + + /* Patch horizontal sizes of detailed timings for packed pixel format */ + for (i = 0; i < NUM_OF_EDID1X_DETAILED_TIMING; i++) { + struct edid_detailed *edid_detailed = (struct edid_detailed *) + &edid_data->edid_detailed_timings[i]; + uint32_t h_addressable; + uint32_t h_blank; + uint32_t h_total; + uint32_t new_h_total; + uint32_t pix_clk; + + if (edid_detailed->pix_clk == 0) + continue; + + h_addressable = edid_detailed->pix_width_8_low + + (edid_detailed->byte4.PIX_WIDTH_4_HIGH << 8); + h_blank = edid_detailed->h_blank_8_low + + (edid_detailed->byte4.H_BLANK_4_HIGH << 8); + h_total = h_addressable + (2 * edid_detailed->h_border) + + h_blank; + new_h_total = h_total; + pix_clk = edid_detailed->pix_clk; + + if (info->param == DCS_PACKED_PIXEL_FORMAT_B70_G70_R70) { + /* G8: NewHaddressable = ((Edid'sHaddressable +23)/24)*8 + * must be % 24; % 24 not matching its own table, + * conflict specification + */ + h_addressable = (h_addressable + 23) / 3 - + ((h_addressable + 23) / 3) % 8; + } else if (info->param == + DCS_PACKED_PIXEL_FORMAT_B70_R30_G70_R74) { + /* G12: NewHaddressable = ((Edid'sHaddressable + 7) / 8) + * * 4, must be 8; + */ + h_addressable = (h_addressable + 7) / 2 - + ((h_addressable + 7) / 2) % 8; + } else + continue; + + /* NewHtotal = NewHaddressable + original Blank. */ + new_h_total = h_addressable + (2 * edid_detailed->h_border) + + h_blank; + + /* New DVI pixel clock = Edid Pixelclock * (NewHtotal / + * EdidHtotal). */ + pix_clk = (edid_detailed->pix_clk * new_h_total) / h_total; + + /* if HRx flag set, NewDviPixelclock *= 2; */ + if ((timing_info.bits.HR0 && i == 0) + || (timing_info.bits.HR1 && i == 1)) + pix_clk = pix_clk * 2; + + /* Now, let's overwrite the original. */ + edid_detailed->pix_width_8_low = h_addressable & 0xFF; + edid_detailed->byte4.PIX_WIDTH_4_HIGH = h_addressable >> 8; + edid_detailed->pix_clk = (uint16_t) pix_clk; + checksum_changed = true; + } + + if (checksum_changed) + patch_checksum_error(ep, buff, 0); +} + +/* + * dal_edid_patch_apply + * + * Apply all relevant patches to the EDID buffer. EDID buffer should match one + * given at "Initialize" + * + */ +void dal_edid_patch_apply(struct edid_patch *ep, uint8_t *buff) +{ + uint32_t i; + uint32_t patch_list_size = monitor_patch_list_size(&ep->mpl); + struct monitor_patch_info *info; + + if (!buff) + return; + + for (i = 0; i < patch_list_size; i++) { + info = monitor_patch_list_get_patch_at(&ep->mpl, i); + switch (info->type) { + case MONITOR_PATCH_TYPE_ERROR_CHECKSUM: + patch_header_error(buff); + patch_checksum_error(ep, buff, 0); + break; + + case MONITOR_PATCH_TYPE_VENDOR_1: + patch_vendor1_workaround(ep, buff); + patch_checksum_error(ep, buff, 0); + patch_checksum_error(ep, buff, 1); + break; + + case MONITOR_PATCH_TYPE_EDID_EXTENTION_ERROR_CHECKSUM: + patch_checksum_error(ep, buff, 1); + break; + + case MONITOR_PATCH_TYPE_IGNORE_19X12_STD_TIMING: + patch_19x12_std_timing_error(ep, buff); + break; + + case MONITOR_PATCH_TYPE_DUAL_EDID_PANEL: + if (info->param != 0) + patch_dual_edid_panel_error(ep, buff); + break; + case MONITOR_PATCH_TYPE_MULTIPLE_PACKED_TYPE: { + bool apply = info->param == + DCS_PACKED_PIXEL_FORMAT_B70_R30_G70_R74 || + info->param == + DCS_PACKED_PIXEL_FORMAT_B70_G70_R70; + if (ep->packed_pixel_format != 0 && apply) + patch_multipacked_type_panel_edid(ep, buff); + break; + } + case MONITOR_PATCH_TYPE_PACKED_PIXEL_FORMAT: + break; + + default: + break; + } + } +} + +union dcs_monitor_patch_flags dal_edid_patch_get_monitor_patch_flags( + struct edid_patch *ep) +{ + return ep->flags; +} + +/* + * dal_edid_patch_get_monitor_patch_info + * + * Get patch info for specific patch type. Info includes patch type and patch + * parameter + * Returns NULL if display does not require such patch + * + */ +const struct monitor_patch_info *dal_edid_patch_get_monitor_patch_info( + struct edid_patch *ep, + enum monitor_patch_type type) +{ + return monitor_patch_list_get_patch_info(&ep->mpl, type); +} + +bool dal_edid_patch_set_monitor_patch_info( + struct edid_patch *ep, + struct monitor_patch_info *info) +{ + struct monitor_patch_info *found_info; + + if (!info) + return false; + + found_info = monitor_patch_list_get_patch_info(&ep->mpl, info->type); + + if (!found_info) + return false; + + found_info->param = info->param; + return true; +} + +uint32_t dal_edid_patch_get_patches_number(struct edid_patch *ep) +{ + return monitor_patch_list_size(&ep->mpl); +} + +/* + * dal_edid_patch_update_dp_receiver_id_based_monitor_patches + * + * Updates patches which are based on DPReceiver information. This should only + * be called after edid mfr/prod id based patches are already applied (for now). + * + */ +void dal_edid_patch_update_dp_receiver_id_based_monitor_patches( + struct edid_patch *ep, + struct dp_receiver_id_info *info) +{ + uint32_t delay_after_power_up = 0; + bool keep_receiver_powered = false; + bool disable_psr_entry_abort = false; + unsigned int delay_after_disable_backlight_dfs_bypass = 0; + + if (!info) + return; + + switch (info->sink_id) { + case DP_SINK_DEVICE_ID_2: +/* + * First batch of PSR panels with TCON from ParadeTech shows an intermittent + * black flash when PSR Abort sequence executed. + * From debug comments from Parade: + * The bug is the corner case of handling PSR abort. It happens when following + * happens: + * 1. TCON receives PSR in-active command back in the n-k frame + * 2. TCON starting to exit PSR state. Because of synchronization, it may take k + * frames to finish the transition (from PSR to live mode) + * 3. TCON receives PSR active command in the n frame + * 4. TCON receives PSR abort command in the n+1 frame + Under this condition, our current PSR TCON will miss the PSR abort command. + This causes the black screen flash. +*/ + if (!dal_strncmp(info->sink_id_str, + DP_SINK_DEV_STRING_ID2_REV0, + sizeof(info->sink_id_str))) + disable_psr_entry_abort = true; + else if (info->sink_id_str[1] == + DP_SINK_DEV_STRING_ID2_REV1_HW_ID_HIGH_BYTE) { + /* Second generation PSR TCON from parade also show this + * issue. Keep abort disabled for now. The device that + * we need this work-around has following ID strings: + * DPCD 00400: 0x00 (Parade OUI byte 0) + * DPCD 00401: 0x1C (Parade OUI byte 1) + * DPCD 00402: 0xF8 (Parade OUI byte 2) + * DPCD 00403: 0x61, or 0x62, or 0x63, or 0x72, or 0x73 + * (HW ID low byte, the same silicon has several + * package/feature flavors) + * DPCD 00404: 0x06 (HW ID high byte) + */ + if ((info->sink_id_str[0] == DP_SINK_DEV_STRING_ID2_REV1_HW_ID_LOW_BYTE1) || + (info->sink_id_str[0] == DP_SINK_DEV_STRING_ID2_REV1_HW_ID_LOW_BYTE2) || + (info->sink_id_str[0] == DP_SINK_DEV_STRING_ID2_REV1_HW_ID_LOW_BYTE3) || + (info->sink_id_str[0] == DP_SINK_DEV_STRING_ID2_REV1_HW_ID_LOW_BYTE4) || + (info->sink_id_str[0] == DP_SINK_DEV_STRING_ID2_REV1_HW_ID_LOW_BYTE5)) + disable_psr_entry_abort = true; + + /* Parade TCON on PSR panels have a backlight issue. If + * backlight is toggled from high -> low for ~20ms -> + * high, backlight stops working properly and becomes + * very dim. + * To resolve this issue, let us detect this TCON and + * apply a patch to add delay to prevent this sequence. + */ + if (info->sink_hw_revision < 0x2) + delay_after_disable_backlight_dfs_bypass = 100; + } + break; + default: + break; + } + + switch (info->branch_id) { + case DP_BRANCH_DEVICE_ID_1: +/* Some active dongles (DP-VGA, DP-DLDVI converters) power down all internal + * circuits including AUX communication preventing reading DPCD table and EDID + * (spec violation). Encoder will skip DP RX power down on disable_output to + * keep receiver powered all the time.*/ + if (!dal_strncmp(info->branch_name, DP_VGA_CONVERTER_ID_1, + sizeof(info->branch_name)) || + !dal_strncmp(info->branch_name, + DP_DVI_CONVERTER_ID_1, + sizeof(info->branch_name))) + keep_receiver_powered = true; + break; + + case DP_BRANCH_DEVICE_ID_4: +/* Workaround for some DP-VGA dongle + * We will add default 350 ms, after power up to let receiver "get used" to the + * state + */ + if (!dal_strncmp(info->branch_name, DP_VGA_CONVERTER_ID_4, + sizeof(info->branch_name)) || + (!dal_strncmp(info->branch_name, + DP_VGA_CONVERTER_ID_4, + sizeof(info->branch_name)))) + delay_after_power_up = 350; + break; + + default: + break; + } + + /* now we update the patches based on the values we found above. */ + + /* handle MONITOR_PATCH_TYPE_DELAY_AFTER_DP_RECEIVER_POWER_UP */ + if (delay_after_power_up > 0) { + struct monitor_patch_info info; + + info.type = + MONITOR_PATCH_TYPE_DELAY_AFTER_DP_RECEIVER_POWER_UP; + info.param = delay_after_power_up; + info.manufacturer_id = + MONITOR_MANUFACTURER_ID_0; + info.product_id = MONITOR_PRODUCT_ID_0; + + if (ep->mpl.flags.flags.DELAY_AFTER_DP_RECEIVER_POWER_UP) { + /* if patch is already applied, we only update the patch + * param if the delay is larger than the currently set + * one. This assumes that DP receiver id based patches + * are done after edid mfr/prod id patches are done. */ + if (delay_after_power_up > + monitor_patch_list_get_patch_info( + &ep->mpl, + info.type)->param) + dal_edid_patch_set_monitor_patch_info( + ep, &info); + } else { + /* otherwise, we don't have the delay patch currently + * applied, so insert it to the list */ + + /* Insert will never add patch with same type */ + monitor_patch_list_insert(&ep->mpl, &info); + } + } + + /* handle MONITOR_PATCH_TYPE_KEEP_DP_RECEIVER_POWERED */ + if (keep_receiver_powered) { + /* MONITOR_PATCH_TYPE_KEEP_DP_RECEIVER_POWERED is a boolean + * patch (patch param is zero, so it will either be applied or + * not. If it isn't applied yet, we insert it to the list. */ + if (!ep->mpl.flags.flags.KEEP_DP_RECEIVER_POWERED) { + struct monitor_patch_info info; + + info.type = + MONITOR_PATCH_TYPE_KEEP_DP_RECEIVER_POWERED; + info.param = 0; + info.manufacturer_id = + MONITOR_MANUFACTURER_ID_0; + info.product_id = MONITOR_PRODUCT_ID_0; + + /* Insert will never add patch with same type */ + monitor_patch_list_insert(&ep->mpl, &info); + } + } + + /* handle MONITOR_PATCH_TYPE_DisablePsrEntryAbort */ + if (disable_psr_entry_abort) { + /* MONITOR_PATCH_TYPE_DisablePsrEntryAbort is a boolean patch + * (patch param is zero, so it will either be applied or not. + * If it isn't applied yet, we insert it to the list. */ + if (!ep->mpl.flags.flags.DISABLE_PSR_ENTRY_ABORT) { + struct monitor_patch_info info; + + info.type = + MONITOR_PATCH_TYPE_DISABLE_PSR_ENTRY_ABORT; + info.param = 0; + info.manufacturer_id = + MONITOR_MANUFACTURER_ID_0; + info.product_id = MONITOR_PRODUCT_ID_0; + + /* Insert will never add patch with same type */ + monitor_patch_list_insert(&ep->mpl, &info); + } + } + + /* handle MONITOR_PATCH_TYPE_DELAY_AFTER_DISABLE_BACKLIGHT_DFS_BYPASS*/ + if (delay_after_disable_backlight_dfs_bypass) { + if (!ep->mpl.flags.flags. + DELAY_AFTER_DISABLE_BACKLIGHT_DFS_BYPASS) { + struct monitor_patch_info info; + + info.type = + MONITOR_PATCH_TYPE_DELAY_AFTER_DISABLE_BACKLIGHT_DFS_BYPASS; + info.param = delay_after_disable_backlight_dfs_bypass; + info.manufacturer_id = + MONITOR_MANUFACTURER_ID_0; + info.product_id = MONITOR_PRODUCT_ID_0; + + /* Insert will never add patch with same type */ + monitor_patch_list_insert(&ep->mpl, &info); + } + } +} diff --git a/drivers/gpu/drm/amd/dal/dcs/edid_patch.h b/drivers/gpu/drm/amd/dal/dcs/edid_patch.h new file mode 100644 index 000000000000..3dcf0c4f6281 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/edid_patch.h @@ -0,0 +1,83 @@ +/* + * Copyright 2012-15 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 + * + */ + +#ifndef __DAL_EDID_PATCH_H__ +#define __DAL_EDID_PATCH_H__ + +#include "monitor_tables.h" + +enum edid_tiled_display_type { + EDID_TILED_DISPLAY_NONE = 0, + EDID_TILED_DISPLAY_1 = 1, + EDID_TILED_DISPLAY_2 = 2 +/* Add more Tiled Display as required*/ +}; + +/** + * the union of define for multi-packed panel + */ +union edid13_multipacked_panel_manufacture_reserved_timing_info { + struct { + uint8_t HR0:1; /* half vRefreshRate in Detailed timing 0 */ + uint8_t HR1:1; /* half vRefreshRate in Detailed timing 1 */ + uint8_t RESERVED:2; + uint8_t G8:1; /* 8bits grey packed */ + uint8_t G10:1; /* 10bits grey packed */ + uint8_t G12:1; /* 12bits grey packed */ + uint8_t RESERVED2:1; + } bits; + uint8_t all; +}; + +struct adapter_service; +struct edid_patch *dal_edid_patch_create(struct adapter_service *as); +void dal_edid_patch_destroy(struct edid_patch **ep); + +uint32_t dal_edid_patch_get_patches_number(struct edid_patch *ep); + +void dal_edid_patch_apply(struct edid_patch *ep, uint8_t *buff); + +bool dal_edid_patch_initialize( + struct edid_patch *ep, + const uint8_t *edid_buf, + uint32_t edid_len); + +struct dp_receiver_id_info; +void dal_edid_patch_update_dp_receiver_id_based_monitor_patches( + struct edid_patch *ep, + struct dp_receiver_id_info *info); + +const struct monitor_patch_info *dal_edid_patch_get_monitor_patch_info( + struct edid_patch *ep, + enum monitor_patch_type type); + +bool dal_edid_patch_set_monitor_patch_info( + struct edid_patch *ep, + struct monitor_patch_info *info); + +union dcs_monitor_patch_flags dal_edid_patch_get_monitor_patch_flags( + struct edid_patch *ep); + +#endif /* __DAL_EDID_PATCH_H__ */ diff --git a/drivers/gpu/drm/amd/dal/dcs/hdtv_dco.c b/drivers/gpu/drm/amd/dal/dcs/hdtv_dco.c new file mode 100644 index 000000000000..b6bb15815143 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/hdtv_dco.c @@ -0,0 +1,32 @@ +/* + * Copyright 2012-14 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 + * + */ + +struct hdtv_dco_funcs { + +}; + +struct hdtv_dco { + struct hdtv_dco_funcs *funcs; +}; diff --git a/drivers/gpu/drm/amd/dal/dcs/hdtv_dco.h b/drivers/gpu/drm/amd/dal/dcs/hdtv_dco.h new file mode 100644 index 000000000000..3151bd559e82 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/hdtv_dco.h @@ -0,0 +1,49 @@ +/* + * Copyright 2012-14 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 + * + */ + +#ifndef __DAL_HDTV_DCO_H__ +#define __DAL_HDTV_DCO_H__ + +struct hdtv_dco; +struct timing_service; +struct adapter_service; +struct dcs; + +struct hdtv_dco *dal_hdtv_dco_create_dvi( + struct timing_service *ts, + struct adapter_service *as, + struct dcs *dcs); + +struct hdtv_dco *dal_hdtv_dco_create_vga( + struct timing_service *ts, + struct adapter_service *as, + struct dcs *dcs); + +struct hdtv_dco *dal_hdtv_dco_create_cv( + struct timing_service *ts, + struct adapter_service *as, + struct dcs *dcs); + +#endif /* __DAL_HDTV_DCO_H__ */ diff --git a/drivers/gpu/drm/amd/dal/dcs/monitor_tables.c b/drivers/gpu/drm/amd/dal/dcs/monitor_tables.c new file mode 100644 index 000000000000..8f36cf95cc67 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/monitor_tables.c @@ -0,0 +1,288 @@ +/* + * Copyright 2012-15 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 + * + */ + +#include "dal_services.h" + +#include "monitor_tables.h" + +#define DELAY_100MS 100 +#define DELAY_120MS 120 +#define DELAY_150MS 150 +#define DELAY_250MS 250 +#define DELAY_500MS 500 +#define DELAY_1250MS 1250 +#define DELAY_50MS 50 + +#define MAX_PIXEL_CLOCK_245MHZ 245 + +#define RETRY_TIMEOUT_2000MS 2000 + +#define LINK_RATE_LOW 0x06 + +static const struct monitor_patch_info monitor_patch_table[] = { + { MONITOR_MANUFACTURER_ID_1, MONITOR_PRODUCT_ID_1, + MONITOR_PATCH_TYPE_DO_NOT_USE_DETAILED_TIMING, 0 }, + { MONITOR_MANUFACTURER_ID_27, MONITOR_PRODUCT_ID_60, + MONITOR_PATCH_TYPE_DO_NOT_USE_RANGE_LIMITATION, 0 }, + { MONITOR_MANUFACTURER_ID_4, + MONITOR_PRODUCT_ID_4, + MONITOR_PATCH_TYPE_RANDOM_CRT, 0 }, + { MONITOR_MANUFACTURER_ID_24, MONITOR_PRODUCT_ID_57, + MONITOR_PATCH_TYPE_VENDOR_1, 0 }, + { MONITOR_MANUFACTURER_ID_5, + MONITOR_PRODUCT_ID_13, + MONITOR_PATCH_TYPE_VENDOR_0, 0 }, + { MONITOR_MANUFACTURER_ID_10, MONITOR_PRODUCT_ID_22, + MONITOR_PATCH_TYPE_DO_NOT_USE_EDID_MAX_PIX_CLK, + MAX_PIXEL_CLOCK_245MHZ }, + { MONITOR_MANUFACTURER_ID_2, MONITOR_PRODUCT_ID_2, + MONITOR_PATCH_TYPE_RESTRICT_VESA_MODE_TIMING, 0 }, + { MONITOR_MANUFACTURER_ID_14, + MONITOR_PRODUCT_ID_33, + MONITOR_PATCH_TYPE_HDTV_WITH_PURE_DFP_EDID, 0 }, + { MONITOR_MANUFACTURER_ID_11, MONITOR_PRODUCT_ID_23, + MONITOR_PATCH_TYPE_TURN_OFF_DISPLAY_BEFORE_MODE_CHANGE, 0 }, + { MONITOR_MANUFACTURER_ID_12, MONITOR_PRODUCT_ID_24, + MONITOR_PATCH_TYPE_TURN_OFF_DISPLAY_BEFORE_MODE_CHANGE, 0 }, + { MONITOR_MANUFACTURER_ID_4, + MONITOR_PRODUCT_ID_9, + MONITOR_PATCH_TYPE_TURN_OFF_DISPLAY_BEFORE_MODE_CHANGE, 0 }, + { MONITOR_MANUFACTURER_ID_13, MONITOR_PRODUCT_ID_28, + MONITOR_PATCH_TYPE_TURN_OFF_DISPLAY_BEFORE_MODE_CHANGE, + DELAY_120MS }, + { MONITOR_MANUFACTURER_ID_13, MONITOR_PRODUCT_ID_29, + MONITOR_PATCH_TYPE_TURN_OFF_DISPLAY_BEFORE_MODE_CHANGE, + DELAY_120MS }, + { MONITOR_MANUFACTURER_ID_13, MONITOR_PRODUCT_ID_30, + MONITOR_PATCH_TYPE_TURN_OFF_DISPLAY_BEFORE_MODE_CHANGE, + DELAY_120MS }, + { MONITOR_MANUFACTURER_ID_13, MONITOR_PRODUCT_ID_31, + MONITOR_PATCH_TYPE_TURN_OFF_DISPLAY_BEFORE_MODE_CHANGE, + DELAY_120MS }, + { MONITOR_MANUFACTURER_ID_28, MONITOR_PRODUCT_ID_69, + MONITOR_PATCH_TYPE_TURN_OFF_DISPLAY_BEFORE_MODE_CHANGE, + DELAY_120MS }, + { MONITOR_MANUFACTURER_ID_21, MONITOR_PRODUCT_ID_43, + MONITOR_PATCH_TYPE_TURN_OFF_DISPLAY_BEFORE_MODE_CHANGE, 0 }, + { MONITOR_MANUFACTURER_ID_21, MONITOR_PRODUCT_ID_44, + MONITOR_PATCH_TYPE_TURN_OFF_DISPLAY_BEFORE_MODE_CHANGE, 0 }, + { MONITOR_MANUFACTURER_ID_21, MONITOR_PRODUCT_ID_46, + MONITOR_PATCH_TYPE_INCREASE_DEFER_WRITE_RETRY_I2C_OVER_AUX, 0 }, + { MONITOR_MANUFACTURER_ID_23, MONITOR_PRODUCT_ID_56, + MONITOR_PATCH_TYPE_EDID_EXTENTION_ERROR_CHECK_SUM, 0 }, + { MONITOR_MANUFACTURER_ID_6, MONITOR_PRODUCT_ID_17, + MONITOR_PATCH_TYPE_EDID_EXTENTION_ERROR_CHECK_SUM, 0 }, + { MONITOR_MANUFACTURER_ID_25, + MONITOR_PRODUCT_ID_59, + MONITOR_PATCH_TYPE_ERROR_CHECKSUM, 0 }, + { MONITOR_MANUFACTURER_ID_26, MONITOR_PRODUCT_ID_58, + MONITOR_PATCH_TYPE_ERROR_CHECKSUM, 0 }, + { MONITOR_MANUFACTURER_ID_4, + MONITOR_PRODUCT_ID_5, + MONITOR_PATCH_TYPE_ERROR_CHECKSUM, 0 }, + { MONITOR_MANUFACTURER_ID_4, MONITOR_PRODUCT_ID_6, + MONITOR_PATCH_TYPE_ERROR_CHECKSUM, 0 }, + { MONITOR_MANUFACTURER_ID_31, MONITOR_PRODUCT_ID_72, + MONITOR_PATCH_TYPE_LIMIT_PANEL_SUPPORT_RGB_ONLY, 0 }, + { MONITOR_MANUFACTURER_ID_32, MONITOR_PRODUCT_ID_75, + MONITOR_PATCH_TYPE_DUAL_EDID_PANEL, 0 }, + { MONITOR_MANUFACTURER_ID_32, MONITOR_PRODUCT_ID_77, + MONITOR_PATCH_TYPE_DUAL_EDID_PANEL, 0 }, + { MONITOR_MANUFACTURER_ID_32, MONITOR_PRODUCT_ID_78, + MONITOR_PATCH_TYPE_DUAL_EDID_PANEL, 0 }, + { MONITOR_MANUFACTURER_ID_32, MONITOR_PRODUCT_ID_79, + MONITOR_PATCH_TYPE_DUAL_EDID_PANEL, 0 }, + { MONITOR_MANUFACTURER_ID_32, MONITOR_PRODUCT_ID_80, + MONITOR_PATCH_TYPE_DUAL_EDID_PANEL, 0 }, + { MONITOR_MANUFACTURER_ID_32, MONITOR_PRODUCT_ID_81, + MONITOR_PATCH_TYPE_DUAL_EDID_PANEL, 0 }, + { MONITOR_MANUFACTURER_ID_32, MONITOR_PRODUCT_ID_82, + MONITOR_PATCH_TYPE_DUAL_EDID_PANEL, 0 }, + { MONITOR_MANUFACTURER_ID_32, MONITOR_PRODUCT_ID_83, + MONITOR_PATCH_TYPE_DUAL_EDID_PANEL, 0 }, + { MONITOR_MANUFACTURER_ID_32, MONITOR_PRODUCT_ID_84, + MONITOR_PATCH_TYPE_DUAL_EDID_PANEL, 0 }, + { MONITOR_MANUFACTURER_ID_32, MONITOR_PRODUCT_ID_85, + MONITOR_PATCH_TYPE_DUAL_EDID_PANEL, 0 }, + { MONITOR_MANUFACTURER_ID_32, MONITOR_PRODUCT_ID_86, + MONITOR_PATCH_TYPE_FORCE_LINK_RATE, LINK_RATE_LOW }, + { MONITOR_MANUFACTURER_ID_32, MONITOR_PRODUCT_ID_89, + MONITOR_PATCH_TYPE_FORCE_LINK_RATE, LINK_RATE_LOW }, + { MONITOR_MANUFACTURER_ID_5, MONITOR_PRODUCT_ID_14, + MONITOR_PATCH_TYPE_IGNORE_19X12_STD_TIMING, 0 }, + { MONITOR_MANUFACTURER_ID_14, MONITOR_PRODUCT_ID_32, + MONITOR_PATCH_TYPE_RESET_TX_ON_DISPLAY_POWER_ON, 0 }, + { MONITOR_MANUFACTURER_ID_22, MONITOR_PRODUCT_ID_62, + MONITOR_PATCH_TYPE_RESET_TX_ON_DISPLAY_POWER_ON, 0 }, + { MONITOR_MANUFACTURER_ID_15, + MONITOR_MANUFACTURER_ID_16, + MONITOR_PATCH_TYPE_RESET_TX_ON_DISPLAY_POWER_ON, 0 }, + { MONITOR_MANUFACTURER_ID_17, MONITOR_PRODUCT_ID_34, + MONITOR_PATCH_TYPE_RESET_TX_ON_DISPLAY_POWER_ON, 0 }, + { MONITOR_MANUFACTURER_ID_18, MONITOR_PRODUCT_ID_35, + MONITOR_PATCH_TYPE_RESET_TX_ON_DISPLAY_POWER_ON, 0 }, + { MONITOR_MANUFACTURER_ID_31, MONITOR_PRODUCT_ID_74, + MONITOR_PATCH_TYPE_RESET_TX_ON_DISPLAY_POWER_ON, 0 }, + { MONITOR_MANUFACTURER_ID_22, MONITOR_PRODUCT_ID_64, + MONITOR_PATCH_TYPE_RESET_TX_ON_DISPLAY_POWER_ON, 0 }, + { MONITOR_MANUFACTURER_ID_36, MONITOR_PRODUCT_ID_93, + MONITOR_PATCH_TYPE_RESET_TX_ON_DISPLAY_POWER_ON, 0 }, + { MONITOR_MANUFACTURER_ID_32, MONITOR_PRODUCT_ID_0, + MONITOR_PATCH_TYPE_VENDOR_2, 0 }, + { MONITOR_MANUFACTURER_ID_33, MONITOR_PRODUCT_ID_0, + MONITOR_PATCH_TYPE_VENDOR_2, 0 }, + { MONITOR_MANUFACTURER_ID_32, MONITOR_PRODUCT_ID_76, + MONITOR_PATCH_TYPE_RESET_TX_ON_DISPLAY_POWER_ON, 0 }, + { MONITOR_MANUFACTURER_ID_35, MONITOR_PRODUCT_ID_90, + MONITOR_PATCH_TYPE_LIMIT_PANEL_SUPPORT_RGB_ONLY, 0 }, + { MONITOR_MANUFACTURER_ID_35, MONITOR_PRODUCT_ID_91, + MONITOR_PATCH_TYPE_INCREASE_DEFER_WRITE_RETRY_I2C_OVER_AUX, 0 }, + { MONITOR_MANUFACTURER_ID_35, MONITOR_PRODUCT_ID_92, + MONITOR_PATCH_TYPE_INCREASE_DEFER_WRITE_RETRY_I2C_OVER_AUX, 0 }, + { MONITOR_MANUFACTURER_ID_4, + MONITOR_PRODUCT_ID_11, + MONITOR_PATCH_TYPE_RESTRICT_PROT_DUAL_LINK_DVI, 0 }, + { MONITOR_MANUFACTURER_ID_4, + MONITOR_PRODUCT_ID_12, + MONITOR_PATCH_TYPE_RESTRICT_PROT_DUAL_LINK_DVI, 0 }, + { MONITOR_MANUFACTURER_ID_10, MONITOR_PRODUCT_ID_21, + MONITOR_PATCH_TYPE_RESTRICT_PROT_DUAL_LINK_DVI, 0 }, + { MONITOR_MANUFACTURER_ID_12, MONITOR_PRODUCT_ID_25, + MONITOR_PATCH_TYPE_DELAY_AFTER_DP_RECEIVER_POWER_UP, + DELAY_150MS }, + { MONITOR_MANUFACTURER_ID_32, MONITOR_PRODUCT_ID_88, + MONITOR_PATCH_TYPE_DELAY_BEFORE_READ_EDID, 25 }, + { MONITOR_MANUFACTURER_ID_12, MONITOR_PRODUCT_ID_26, + MONITOR_PATCH_TYPE_DELAY_AFTER_DP_RECEIVER_POWER_UP, + DELAY_150MS }, + { MONITOR_MANUFACTURER_ID_32, MONITOR_PRODUCT_ID_87, + MONITOR_PATCH_TYPE_NO_DEFAULT_TIMINGS, 0 }, + { MONITOR_MANUFACTURER_ID_22, MONITOR_PRODUCT_ID_65, + MONITOR_PATCH_TYPE_ADD_CEA861_DETAILED_TIMING_VIC31, 0 }, + { MONITOR_MANUFACTURER_ID_22, MONITOR_PRODUCT_ID_66, + MONITOR_PATCH_TYPE_ADD_CEA861_DETAILED_TIMING_VIC16, 0 }, + { MONITOR_MANUFACTURER_ID_22, MONITOR_PRODUCT_ID_66, + MONITOR_PATCH_TYPE_ADD_CEA861_DETAILED_TIMING_VIC31, 0 }, + { MONITOR_MANUFACTURER_ID_22, MONITOR_PRODUCT_ID_67, + MONITOR_PATCH_TYPE_ADD_CEA861_DETAILED_TIMING_VIC16, 0 }, + { MONITOR_MANUFACTURER_ID_22, MONITOR_PRODUCT_ID_67, + MONITOR_PATCH_TYPE_ADD_CEA861_DETAILED_TIMING_VIC31, 0 }, + { MONITOR_MANUFACTURER_ID_21, MONITOR_PRODUCT_ID_0, + MONITOR_PATCH_TYPE_DELAY_BEFORE_UNMUTE, DELAY_150MS }, + { MONITOR_MANUFACTURER_ID_21, MONITOR_PRODUCT_ID_47, + MONITOR_PATCH_TYPE_RETRY_LINK_TRAINING_ON_FAILURE, + RETRY_TIMEOUT_2000MS }, + { MONITOR_MANUFACTURER_ID_22, MONITOR_PRODUCT_ID_67, + MONITOR_PATCH_TYPE_ALLOW_AUX_WHEN_HPD_LOW, 0 }, + { MONITOR_MANUFACTURER_ID_22, MONITOR_PRODUCT_ID_66, + MONITOR_PATCH_TYPE_ALLOW_AUX_WHEN_HPD_LOW, 0 }, + { MONITOR_MANUFACTURER_ID_22, MONITOR_PRODUCT_ID_68, + MONITOR_PATCH_TYPE_ALLOW_AUX_WHEN_HPD_LOW, 0 }, + { MONITOR_MANUFACTURER_ID_17, MONITOR_PRODUCT_ID_36, + MONITOR_PATCH_TYPE_TILED_DISPLAY, 0 }, + { MONITOR_MANUFACTURER_ID_14, MONITOR_PRODUCT_ID_38, + MONITOR_PATCH_TYPE_TILED_DISPLAY, 0 }, + { MONITOR_MANUFACTURER_ID_14, MONITOR_PRODUCT_ID_37, + MONITOR_PATCH_TYPE_TILED_DISPLAY, 0 }, + { MONITOR_MANUFACTURER_ID_21, MONITOR_PRODUCT_ID_39, + MONITOR_PATCH_TYPE_TILED_DISPLAY, 0 }, + { MONITOR_MANUFACTURER_ID_21, MONITOR_PRODUCT_ID_40, + MONITOR_PATCH_TYPE_TILED_DISPLAY, 0 }, + { MONITOR_MANUFACTURER_ID_21, MONITOR_PRODUCT_ID_41, + MONITOR_PATCH_TYPE_TILED_DISPLAY, 0 }, + { MONITOR_MANUFACTURER_ID_20, MONITOR_PRODUCT_ID_42, + MONITOR_PATCH_TYPE_TILED_DISPLAY, 0 }, + { MONITOR_MANUFACTURER_ID_2, MONITOR_PRODUCT_ID_2, + MONITOR_PATCH_TYPE_LARGE_PANEL, 0 }, + { MONITOR_MANUFACTURER_ID_4, + MONITOR_PRODUCT_ID_7, + MONITOR_PATCH_TYPE_LARGE_PANEL, 0 }, + { MONITOR_MANUFACTURER_ID_4, + MONITOR_PRODUCT_ID_7_2, + MONITOR_PATCH_TYPE_LARGE_PANEL, 0 }, + { MONITOR_MANUFACTURER_ID_7, MONITOR_PRODUCT_ID_18, + MONITOR_PATCH_TYPE_LARGE_PANEL, 0 }, + { MONITOR_MANUFACTURER_ID_9, MONITOR_PRODUCT_ID_20, + MONITOR_PATCH_TYPE_LARGE_PANEL, 0 }, + { MONITOR_MANUFACTURER_ID_21, MONITOR_PRODUCT_ID_48, + MONITOR_PATCH_TYPE_VID_STREAM_DIFFER_TO_SYNC, 0 }, + { MONITOR_MANUFACTURER_ID_21, MONITOR_PRODUCT_ID_49, + MONITOR_PATCH_TYPE_VID_STREAM_DIFFER_TO_SYNC, 0 }, + { MONITOR_MANUFACTURER_ID_5, MONITOR_PRODUCT_ID_15, + MONITOR_PATCH_TYPE_DELAY_AFTER_DP_RECEIVER_POWER_UP, + DELAY_250MS }, + + { MONITOR_MANUFACTURER_ID_21, MONITOR_PRODUCT_ID_50, + MONITOR_PATCH_TYPE_EXTRA_DELAY_ON_DISCONNECT, DELAY_500MS }, + { MONITOR_MANUFACTURER_ID_21, MONITOR_PRODUCT_ID_51, + MONITOR_PATCH_TYPE_EXTRA_DELAY_ON_DISCONNECT, DELAY_500MS }, + { MONITOR_MANUFACTURER_ID_37, MONITOR_PRODUCT_ID_94, + MONITOR_PATCH_TYPE_EXTRA_DELAY_ON_DISCONNECT, DELAY_1250MS }, + { MONITOR_MANUFACTURER_ID_21, MONITOR_PRODUCT_ID_46_HDMI, + MONITOR_PATCH_TYPE_EXTRA_DELAY_ON_DISCONNECT, DELAY_250MS }, + { MONITOR_MANUFACTURER_ID_21, MONITOR_PRODUCT_ID_53, + MONITOR_PATCH_TYPE_EXTRA_DELAY_ON_DISCONNECT, DELAY_1250MS }, + { MONITOR_MANUFACTURER_ID_21, MONITOR_PRODUCT_ID_54, + MONITOR_PATCH_TYPE_DELAY_BEFORE_READ_EDID, DELAY_50MS }, + { MONITOR_MANUFACTURER_ID_21, MONITOR_PRODUCT_ID_55, + MONITOR_PATCH_TYPE_EXTRA_DELAY_ON_DISCONNECT, DELAY_1250MS }, + { MONITOR_MANUFACTURER_ID_12, MONITOR_PRODUCT_ID_27, + MONITOR_PATCH_TYPE_SINGLE_MODE_PACKED_PIXEL, 0 }, + { MONITOR_MANUFACTURER_ID_22, MONITOR_PRODUCT_ID_0, + MONITOR_PATCH_TYPE_DELAY_AFTER_PIXEL_FORMAT_CHANGE, + DELAY_150MS }, +}; + +uint32_t dal_monitor_tables_get_count(void) +{ + return ARRAY_SIZE(monitor_patch_table); +} + +const struct monitor_patch_info *dal_monitor_tables_get_entry_at(uint32_t i) +{ + if (i >= dal_monitor_tables_get_count()) { + dal_error("%s: incorrect index %d\n", __func__, i); + return NULL; + } + return &monitor_patch_table[i]; +} + +const struct monitor_patch_info *dal_monitor_tables_find_entry( + enum monitor_manufacturer_id manufacturer_id, + enum monitor_product_id product_id, + enum monitor_patch_type patch_type) +{ + uint32_t i; + + for (i = 0; i < dal_monitor_tables_get_count(); ++i) { + const struct monitor_patch_info *entry = + &monitor_patch_table[i]; + if (entry->manufacturer_id == manufacturer_id && + entry->product_id == product_id && + entry->type == patch_type) + return entry; + } + return NULL; +} diff --git a/drivers/gpu/drm/amd/dal/dcs/monitor_tables.h b/drivers/gpu/drm/amd/dal/dcs/monitor_tables.h new file mode 100644 index 000000000000..e4066c4f1496 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/monitor_tables.h @@ -0,0 +1,34 @@ +/* + * Copyright 2012-15 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 + * + */ + +#ifndef __DAL_MONITOR_TABLES_H__ +#define __DAL_MONITOR_TABLES_H__ + +#include "include/dcs_types.h" + +uint32_t dal_monitor_tables_get_count(void); +const struct monitor_patch_info *dal_monitor_tables_get_entry_at(uint32_t i); + +#endif diff --git a/drivers/gpu/drm/amd/dal/dcs/remote_display_receiver_modes.c b/drivers/gpu/drm/amd/dal/dcs/remote_display_receiver_modes.c new file mode 100644 index 000000000000..7a3ca3787446 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/remote_display_receiver_modes.c @@ -0,0 +1,555 @@ +/* + * Copyright 2012-15 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 + * + */ + +#include "dal_services.h" + +#include "include/adapter_service_interface.h" +#include "include/timing_service_interface.h" +#include "include/audio_types.h" +#include "include/dcs_interface.h" + +#include "remote_display_receiver_modes.h" + +const struct cea_audio_mode default_audio_modes[] = { + /* 0 - LPCM, 2 channels, 44.1Hz, 16 bits*/ + {AUDIO_FORMAT_CODE_LINEARPCM, 2, 2, {1} }, + /* 1 - LPCM, 2 channels, 48Hz, 16 bits,*/ + {AUDIO_FORMAT_CODE_LINEARPCM, 2, 4, {1} }, + /* 2 - AAC, 2 channels, 48Hz, 16 bits,*/ + {AUDIO_FORMAT_CODE_AAC, 2, 4, {2} }, + /* 3 - AAC, 4 channels, 48Hz, 16 bits,*/ + {AUDIO_FORMAT_CODE_AAC, 4, 4, {2} }, + /* 4 - AAC, 6 channels, 48Hz, 16 bits,*/ + {AUDIO_FORMAT_CODE_AAC, 6, 4, {2} }, + /* 5 - AAC, 8 channels, 48Hz, 16 bits,*/ + {AUDIO_FORMAT_CODE_AAC, 8, 4, {2} }, + /* 6 - AC3, 2 channels, 48Hz, 16 bits,*/ + {AUDIO_FORMAT_CODE_AC3, 2, 4, {2} }, + /* 7 - AC3, 4 channels, 48Hz, 16 bits,*/ + {AUDIO_FORMAT_CODE_AC3, 4, 4, {2} }, + /* 8 - AC3, 6 channels , 48Hz, 16 bits,*/ + {AUDIO_FORMAT_CODE_AC3, 6, 4, {2} } +}; + + +struct remote_display_receiver_modes { + const struct timing_service *ts; + bool supports_miracast; + struct dal_remote_display_receiver_capability rdrm_caps; +}; + +const struct mode_info rdrm_default_cea_modes[] = { + {640, 480, 60, TIMING_STANDARD_DMT, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 0 */ + {720, 480, 60, TIMING_STANDARD_CEA861, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 1 */ + {720, 480, 60, TIMING_STANDARD_CEA861, TIMING_SOURCE_DEFAULT, + {1, 0, 0, 0, 0, 0} }, /* 2 */ + {720, 576, 50, TIMING_STANDARD_CEA861, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 3 */ + {720, 576, 50, TIMING_STANDARD_CEA861, TIMING_SOURCE_DEFAULT, + {1, 0, 0, 0, 0, 0} }, /* 4 */ + {1280, 720, 30, TIMING_STANDARD_CEA861, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 5 */ + {1280, 720, 60, TIMING_STANDARD_CEA861, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 6 */ + {1920, 1080, 30, TIMING_STANDARD_CEA861, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 7 */ + {1920, 1080, 60, TIMING_STANDARD_CEA861, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 8 */ + {1920, 1080, 60, TIMING_STANDARD_CEA861, TIMING_SOURCE_DEFAULT, + {1, 0, 0, 0, 0, 0} }, /* 9 */ + {1280, 720, 25, TIMING_STANDARD_CEA861, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 10 */ + {1280, 720, 50, TIMING_STANDARD_CEA861, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 11 */ + {1920, 1080, 25, TIMING_STANDARD_CEA861, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 12 */ + {1920, 1080, 50, TIMING_STANDARD_CEA861, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 13 */ + {1920, 1080, 50, TIMING_STANDARD_CEA861, TIMING_SOURCE_DEFAULT, + {1, 0, 0, 0, 0, 0} }, /* 14 */ + {1280, 720, 24, TIMING_STANDARD_CEA861, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 15 */ + {1920, 1080, 24, TIMING_STANDARD_CEA861, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 16 */ +}; + +/* + * Some of the modes in this table are not "real modes". We have to keep these + * entries in table because receiver cap is a bit vector and if we use a + * different table format, we need to add translation logic else where in DAL. + * For those modes, we set timing standard to undefined, and we will not insert + * them into the mode list. + */ +const struct mode_info rdrm_default_vesa_modes[] = { + {800, 600, 30, TIMING_STANDARD_DMT, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 0 */ + {800, 600, 60, TIMING_STANDARD_DMT, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 1 */ + {1024, 768, 30, TIMING_STANDARD_DMT, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 2 */ + {1024, 768, 60, TIMING_STANDARD_DMT, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 3 */ + {1152, 864, 30, TIMING_STANDARD_UNDEFINED, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 4 */ + {1152, 864, 60, TIMING_STANDARD_UNDEFINED, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 5 */ + {1280, 768, 30, TIMING_STANDARD_DMT, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 6 */ + {1280, 768, 60, TIMING_STANDARD_DMT, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 7 */ + {1280, 800, 30, TIMING_STANDARD_DMT, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 8 */ + {1280, 800, 60, TIMING_STANDARD_DMT, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 9 */ + {1360, 768, 30, TIMING_STANDARD_DMT, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 10 */ + {1360, 768, 60, TIMING_STANDARD_DMT, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 11 */ + {1366, 768, 30, TIMING_STANDARD_DMT, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 12 */ + {1366, 768, 60, TIMING_STANDARD_DMT, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 13 */ + {1280, 1024, 30, TIMING_STANDARD_DMT, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 14 */ + {1280, 1024, 60, TIMING_STANDARD_DMT, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 15 */ + {1400, 1050, 30, TIMING_STANDARD_DMT, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 16 */ + {1400, 1050, 60, TIMING_STANDARD_DMT, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 17 */ + {1400, 900, 30, TIMING_STANDARD_DMT, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 18 */ + {1400, 900, 60, TIMING_STANDARD_DMT, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 19 */ + {1600, 900, 30, TIMING_STANDARD_DMT, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 20 */ + {1600, 900, 60, TIMING_STANDARD_DMT, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 21 */ + {1600, 1200, 30, TIMING_STANDARD_DMT, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 22 */ + {1600, 1200, 60, TIMING_STANDARD_DMT, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 23 */ + {1680, 1024, 30, TIMING_STANDARD_UNDEFINED, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 24 */ + {1680, 1024, 60, TIMING_STANDARD_UNDEFINED, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 25 */ + {1680, 1050, 30, TIMING_STANDARD_DMT, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 26 */ + {1680, 1050, 60, TIMING_STANDARD_DMT, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 27 */ + {1920, 1200, 30, TIMING_STANDARD_DMT, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 28 */ + {1920, 1200, 60, TIMING_STANDARD_DMT, TIMING_SOURCE_DEFAULT, + {0, 0, 0, 0, 0, 0} }, /* 29 */ +}; + +/************************* + *** private functions *** + *************************/ + +static bool remote_display_receiver_modes_construct( + struct remote_display_receiver_modes *rdrm, + struct remote_display_receiver_modes_init_data *rdrm_init_data) +{ + rdrm->ts = rdrm_init_data->ts; + rdrm->supports_miracast = rdrm_init_data->supports_miracast; + return true; +} + +/** +* This function only expects at most 1 bit set +* in both the sample rate and sample depth field (if LPCM). +*/ +static bool is_cea_audio_mode_supported( + struct remote_display_receiver_modes *rdrm, + const struct cea_audio_mode *const audio_mode) +{ + uint32_t i = 0; + unsigned int num_of_entries = + sizeof(default_audio_modes)/ + sizeof(default_audio_modes[0]); + + for (i = 0; i < num_of_entries; i++) { + + if (audio_mode->format_code != + default_audio_modes[i].format_code && + audio_mode->channel_count != + default_audio_modes[i].channel_count && + audio_mode->sample_rate != + default_audio_modes[i].sample_rate) + continue; + + switch (audio_mode->format_code) { + case AUDIO_FORMAT_CODE_LINEARPCM: + if (audio_mode->sample_size == + default_audio_modes[i].sample_size) + return (rdrm->rdrm_caps.audio.raw & + (1 << i)) ? true : false; + + break; + case AUDIO_FORMAT_CODE_AC3: + case AUDIO_FORMAT_CODE_MPEG1: + case AUDIO_FORMAT_CODE_MP3: + case AUDIO_FORMAT_CODE_MPEG2: + case AUDIO_FORMAT_CODE_AAC: + case AUDIO_FORMAT_CODE_DTS: + case AUDIO_FORMAT_CODE_ATRAC: + /*Format 2 to 8*/ + if (audio_mode->max_bit_rate == + default_audio_modes[i].max_bit_rate) + return (rdrm->rdrm_caps.audio.raw & + (1 << i)) ? true : false; + break; + default: + return false; + } + } + + return false; +} + +/* + * insert_into_timing_list + * + * @brief + * Add the given mode into timing list + * + * @param + * struct remote_display_receiver_modes *rdrm - [in] remote display receiver + * struct dcs_mode_timing_list *list - [out] list to be appended at. + * const struct mode_info *mode - [in] desired mode to be added to the list + */ +static bool insert_into_timing_list( + struct remote_display_receiver_modes *rdrm, + struct dcs_mode_timing_list *list, + const struct mode_info *mode) +{ + struct mode_timing mt; + struct mode_info mi = {0}; + bool result = false; + + dal_memset(&mt, 0, sizeof(mt)); + mi = *mode; + + /* For 30 Hz case, we want to grab timing of 60Hz, and halve the pixel + * clock later on */ + if (mode->field_rate == 30) + mi.field_rate = 60; + + /* Query the timing for a given mode */ + if (dal_timing_service_get_timing_for_mode( + rdrm->ts, &mi, &mt.crtc_timing)) { + mt.mode_info = *mode; + + if (mode->field_rate == 30) { + mt.crtc_timing.pix_clk_khz /= 2; + mt.crtc_timing.vic = 0; + mt.crtc_timing.hdmi_vic = 0; + } + + if (dal_dcs_mode_timing_list_append(list, &mt)) + result = true; + } + + return result; +} + +/************************ + *** public functions *** + ************************/ + +struct remote_display_receiver_modes* +dal_remote_display_receiver_modes_create( + struct remote_display_receiver_modes_init_data *rdrm_init_data) +{ + struct remote_display_receiver_modes *rdrm; + + rdrm = dal_alloc(sizeof(struct remote_display_receiver_modes)); + + if (!rdrm) + return NULL; + + if (remote_display_receiver_modes_construct(rdrm, rdrm_init_data)) + return rdrm; + + dal_free(rdrm); + + return NULL; +} + +void dal_remote_display_receiver_modes_destroy( + struct remote_display_receiver_modes **rdrm) +{ + dal_free(*rdrm); + *rdrm = NULL; +} + +void dal_remote_display_receiver_set_capabilities( + struct remote_display_receiver_modes *rdrm, + const struct dal_remote_display_receiver_capability *rdrm_caps) +{ + rdrm->rdrm_caps.audio.raw = rdrm_caps->audio.raw; + rdrm->rdrm_caps.vesa_mode.raw = rdrm_caps->vesa_mode.raw; + rdrm->rdrm_caps.cea_mode.raw = rdrm_caps->cea_mode.raw; + rdrm->rdrm_caps.hh_mode.raw = rdrm_caps->hh_mode.raw; + rdrm->rdrm_caps.stereo_3d_mode.raw = rdrm_caps->stereo_3d_mode.raw; + rdrm->rdrm_caps.cea_mode.raw |= 0x1; /* Mandatory mode for WFD */ +} + +void dal_remote_display_receiver_clear_capabilities( + struct remote_display_receiver_modes *rdrm) +{ + dal_memset(&rdrm->rdrm_caps, 0, sizeof(rdrm->rdrm_caps)); +} + +bool dal_rdr_get_supported_cea_audio_mode( + struct remote_display_receiver_modes *rdrm, + const struct cea_audio_mode *const cea_audio_mode, + struct cea_audio_mode *const output_mode) +{ + const uint8_t size_of_sample_rate_field = 8; + const uint8_t size_of_sample_size_field = 8; + uint32_t cur_sample_rate_bit = 0; + uint32_t cur_sample_size_bit = 0; + + bool at_least_one_mode_valid = false; + struct cea_audio_mode temp_mode = {0}; + + /* Create a copy of the cea_audio_mode, but zero + * out the sample_rate and bitfield as we don't + * actually know which ones are supported. + */ + *output_mode = *cea_audio_mode; + output_mode->sample_rate = 0; + + /* Create a copy if the input audio mode. + * We will use this copy to check the audio mode + * one sample rate/sample size combination at a time. + */ + temp_mode = *cea_audio_mode; + + switch (cea_audio_mode->format_code) { + case AUDIO_FORMAT_CODE_LINEARPCM: + output_mode->sample_size = 0; + + /* Iterate through all sample rate bits.*/ + for (cur_sample_rate_bit = 0; + cur_sample_rate_bit < + size_of_sample_rate_field; + cur_sample_rate_bit++) { + /* Mask the current sample rate bit.*/ + temp_mode.sample_rate = + cea_audio_mode->sample_rate & + (0x1 << cur_sample_rate_bit); + + /* If the sample rate bit is set, + * we check if the wireless receiver supports it. + */ + if (!temp_mode.sample_rate) + continue; + + /* For LPCM, though we must also + * check the sample size bitfield.*/ + for (cur_sample_size_bit = 0; + cur_sample_size_bit < + size_of_sample_size_field; + cur_sample_size_bit++) { + + /* Mask the current sample size bit*/ + temp_mode.sample_size = + cea_audio_mode->sample_size & + (0x1 << cur_sample_size_bit); + + /* If the sample size bit + * is set, we check if + * the wireless receiver + * supports it.*/ + if (!temp_mode.sample_size) + continue; + + /* If the sample rate/size is supported, + * then we add both to the resultant set.*/ + if (!is_cea_audio_mode_supported( + rdrm, + &temp_mode)) + continue; + + output_mode->sample_rate |= + temp_mode.sample_rate; + output_mode->sample_size |= + temp_mode.sample_size; + at_least_one_mode_valid = true; + + } + } + break; + case AUDIO_FORMAT_CODE_AC3: + case AUDIO_FORMAT_CODE_MPEG1: + case AUDIO_FORMAT_CODE_MP3: + case AUDIO_FORMAT_CODE_MPEG2: + case AUDIO_FORMAT_CODE_AAC: + case AUDIO_FORMAT_CODE_DTS: + case AUDIO_FORMAT_CODE_ATRAC: + output_mode->max_bit_rate = 0; + temp_mode.max_bit_rate = + cea_audio_mode->max_bit_rate; + + /* Iterate through all sample rate bits.*/ + for (cur_sample_rate_bit = 0; cur_sample_rate_bit < + size_of_sample_rate_field; cur_sample_rate_bit++) { + /* Mask the current sample rate bit.*/ + temp_mode.sample_rate = + cea_audio_mode->sample_rate & + (0x1 << cur_sample_rate_bit); + + /* If the sample rate bit is set, + * we check if the wireless receiver supports it.*/ + if (!temp_mode.sample_rate) + continue; + + /* If this sample rate is supported, + * then add it to the resultant set.*/ + if (!is_cea_audio_mode_supported( + rdrm, + &temp_mode)) + continue; + + output_mode->sample_rate |= + temp_mode.sample_rate; + output_mode->max_bit_rate = + temp_mode.max_bit_rate; + at_least_one_mode_valid = true; + } + break; + case AUDIO_FORMAT_CODE_1BITAUDIO: + case AUDIO_FORMAT_CODE_DOLBYDIGITALPLUS: + case AUDIO_FORMAT_CODE_DTS_HD: + case AUDIO_FORMAT_CODE_MAT_MLP: + case AUDIO_FORMAT_CODE_DST: + case AUDIO_FORMAT_CODE_WMAPRO: + output_mode->audio_codec_vendor_specific = 0; + temp_mode.audio_codec_vendor_specific = + cea_audio_mode->audio_codec_vendor_specific; + + /* Iterate through all sample rate bits.*/ + for (cur_sample_rate_bit = 0; cur_sample_rate_bit < + size_of_sample_rate_field; cur_sample_rate_bit++) { + /* Mask the current sample rate bit.*/ + temp_mode.sample_rate = + cea_audio_mode->sample_rate & + (0x1 << cur_sample_rate_bit); + + /* If the sample rate bit is set, + * we check if the wireless receiver supports it.*/ + if (!temp_mode.sample_rate) + continue; + + /* If this sample rate is supported, + * then add it to the resultant set.*/ + if (!is_cea_audio_mode_supported(rdrm, + &temp_mode)) + continue; + + output_mode->sample_rate |= + temp_mode.sample_rate; + output_mode->audio_codec_vendor_specific = + temp_mode.audio_codec_vendor_specific; + at_least_one_mode_valid = true; + + } + break; + default: + break; + } + + return at_least_one_mode_valid; +} + +/* + * dal_remote_display_receiver_get_supported_mode_timing + * + * @brief + * Add CEA mode and VESA mode into supported timing list + * + * @param + * struct remote_display_receiver_modes *rdrm - [in] remote dipslay receiver + * struct dcs_mode_timing_list *list - [out] new modes are added in this list + * + * @return + * true if any mode is added, false otherwise. + */ +bool dal_remote_display_receiver_get_supported_mode_timing( + struct remote_display_receiver_modes *rdrm, + struct dcs_mode_timing_list *list) +{ + uint32_t list_count = 0; + uint32_t i = 0; + bool result = false; + struct mode_info mode = {0}; + + if (list == NULL) + return false; + + /* Add all the CEA modes */ + list_count = sizeof(rdrm_default_cea_modes) / + sizeof(rdrm_default_cea_modes[0]); + + for (i = 0; i < list_count; ++i) { + if (rdrm->rdrm_caps.cea_mode.raw & (1 << i)) { + mode = rdrm_default_cea_modes[i]; + + if (insert_into_timing_list(rdrm, list, &mode)) + result = true; + + /* Insert the video-optimized timing as well */ + mode.flags.VIDEO_OPTIMIZED_RATE = + (mode.flags.VIDEO_OPTIMIZED_RATE == 1) ? 0 : 1; + if (insert_into_timing_list(rdrm, list, &mode)) + result = true; + } + } + + /* Add all the VESA modes */ + list_count = sizeof(rdrm_default_vesa_modes) / + sizeof(rdrm_default_vesa_modes[0]); + + for (i = 0; i < list_count; ++i) { + if (rdrm->rdrm_caps.vesa_mode.raw & (1 << i)) { + + mode = rdrm_default_vesa_modes[i]; + + if (mode.timing_standard == TIMING_STANDARD_UNDEFINED) + continue; + + if (insert_into_timing_list(rdrm, list, &mode)) + result = true; + } + } + + /* return true if any mode was added */ + return result; +} diff --git a/drivers/gpu/drm/amd/dal/dcs/remote_display_receiver_modes.h b/drivers/gpu/drm/amd/dal/dcs/remote_display_receiver_modes.h new file mode 100644 index 000000000000..609c365e9810 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/remote_display_receiver_modes.h @@ -0,0 +1,62 @@ +/* + * Copyright 2012-15 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 + * + */ + +#ifndef __DAL_REMOTE_DISPLAY_RECEIVER_H +#define __DAL_REMOTE_DISPLAY_RECEIVER_H + +#include "include/dcs_types.h" + +struct remote_display_receiver_modes; + +struct remote_display_receiver_modes_init_data { + struct timing_service *ts; + bool supports_miracast; +}; + + +struct remote_display_receiver_modes *dal_remote_display_receiver_modes_create( + struct remote_display_receiver_modes_init_data *rdrm_init_data); + +void dal_remote_display_receiver_modes_destroy( + struct remote_display_receiver_modes **rdrm); + + +void dal_remote_display_receiver_set_capabilities( + struct remote_display_receiver_modes *rdrm, + const struct dal_remote_display_receiver_capability *rdrm_caps); + +void dal_remote_display_receiver_clear_capabilities( + struct remote_display_receiver_modes *rdrm); + +bool dal_rdr_get_supported_cea_audio_mode( + struct remote_display_receiver_modes *rdrm, + const struct cea_audio_mode *const cea_audio_mode, + struct cea_audio_mode *const actual_cea_audio_mode); + +bool dal_remote_display_receiver_get_supported_mode_timing( + struct remote_display_receiver_modes *rdrm, + struct dcs_mode_timing_list *list); + +#endif /* __DAL_REMOTE_DISPLAY_RECEIVER_H */ diff --git a/drivers/gpu/drm/amd/dal/dcs/vbios_dco.c b/drivers/gpu/drm/amd/dal/dcs/vbios_dco.c new file mode 100644 index 000000000000..67490c30bef4 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/vbios_dco.c @@ -0,0 +1,327 @@ +/* + * Copyright 2012-15 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 + * + */ + +#include "dal_services.h" +#include "vbios_dco.h" +#include "include/dcs_interface.h" +#include "include/grph_object_ctrl_defs.h" +#include "include/adapter_service_interface.h" +#include "include/timing_service_interface.h" + +struct vbios_dco { + struct adapter_service *as; + struct embedded_panel_info panel_info; + uint8_t *edid_data; + uint32_t edid_len; +}; + +static const struct lcd_resolution lcd_low_resolution[] = { + { 320, 200 }, + { 320, 240 }, + { 400, 300 }, + { 512, 384 }, + { 640, 480 }, + { 800, 600 }, + { 1024, 768 }, + { 1152, 864 }, + { 1280, 1024 }, + { 1600, 1200 }, + { 1792, 1344 }, + { 1800, 1440 }, + { 1920, 1440 }, + { 2048, 1536 } +}; + +bool dal_vbios_dco_construct( + struct vbios_dco *dco, + struct adapter_service *as) +{ + bool ret = false; + + if (!as) + return ret; + + dco->as = as; + + if (dal_adapter_service_get_embedded_panel_info(as, &dco->panel_info)) { + ret = true; + + if (dal_adapter_service_get_faked_edid_len( + as, &dco->edid_len)) { + + dco->edid_data = dal_alloc(dco->edid_len); + + if (!dco->edid_data) + return false; + + if (!dal_adapter_service_get_faked_edid_buf( + as, dco->edid_data, dco->edid_len)) { + + dal_free(dco->edid_data); + ret = false; + } + } + } + return ret; +} + +struct vbios_dco *dal_vbios_dco_create( + struct adapter_service *as) +{ + struct vbios_dco *dco; + + dco = dal_alloc(sizeof(struct vbios_dco)); + + if (!dco) + return NULL; + + if (dal_vbios_dco_construct(dco, as)) + return dco; + + dal_free(dco); + return NULL; +} + +void dal_vbios_dco_destruct( + struct vbios_dco *dco) +{ + if (dco->edid_data) + dal_free(dco->edid_data); +} + +void dal_vbios_dco_destroy( + struct vbios_dco **dco) +{ + if (!dco || !*dco) + return; + + dal_vbios_dco_destruct(*dco); + dal_free(*dco); + *dco = NULL; +} + +static void vbios_timing_to_crtc_timing( + struct device_timing *device_timing, + struct crtc_timing *crtc_timing) +{ + ASSERT(device_timing != NULL); + ASSERT(crtc_timing != NULL); + + crtc_timing->pix_clk_khz = device_timing->pixel_clk; + + crtc_timing->h_addressable = device_timing->horizontal_addressable; + crtc_timing->h_total = device_timing->horizontal_addressable + + device_timing->horizontal_blanking_time; + crtc_timing->h_front_porch = device_timing->horizontal_sync_offset; + crtc_timing->h_sync_width = device_timing->horizontal_sync_width; + crtc_timing->h_border_left = device_timing->horizontal_border; + crtc_timing->h_border_right = device_timing->horizontal_border; + + crtc_timing->v_addressable = device_timing->vertical_addressable; + crtc_timing->v_total = device_timing->vertical_addressable + + device_timing->vertical_blanking_time; + crtc_timing->v_front_porch = device_timing->vertical_sync_offset; + crtc_timing->v_sync_width = device_timing->vertical_sync_width; + crtc_timing->v_border_top = device_timing->vertical_border; + crtc_timing->v_border_bottom = device_timing->vertical_border; + + crtc_timing->timing_standard = TIMING_STANDARD_EXPLICIT; + crtc_timing->pixel_encoding = PIXEL_ENCODING_RGB; + crtc_timing->display_color_depth = device_timing->misc_info.RGB888 ? + DISPLAY_COLOR_DEPTH_888 : DISPLAY_COLOR_DEPTH_666; + + crtc_timing->flags.INTERLACE = device_timing->misc_info.INTERLACE; + crtc_timing->flags.HSYNC_POSITIVE_POLARITY = + device_timing->misc_info.H_SYNC_POLARITY; + crtc_timing->flags.VSYNC_POSITIVE_POLARITY = + device_timing->misc_info.V_SYNC_POLARITY; + + if (device_timing->misc_info.H_REPLICATION_BY2) + crtc_timing->flags.PIXEL_REPETITION = 2; +} + +static bool get_vbios_native_mode_timing( + struct vbios_dco *dco, + struct mode_timing *mode_timing, + bool *preferred_mode_found) +{ + + if (dco->panel_info.lcd_timing.pixel_clk == 0) + return false; + + vbios_timing_to_crtc_timing( + &dco->panel_info.lcd_timing, + &mode_timing->crtc_timing); + + dal_timing_service_create_mode_info_from_timing( + &mode_timing->crtc_timing, &mode_timing->mode_info); + + mode_timing->mode_info.timing_standard = + mode_timing->crtc_timing.timing_standard; + mode_timing->mode_info.timing_source = TIMING_SOURCE_VBIOS; + + /*If preferred mode yet not found - + * select native bios mode/timing as preferred*/ + if (!(*preferred_mode_found)) { + mode_timing->mode_info.flags.PREFERRED = 1; + *preferred_mode_found = true; + } + + return true; +} + +static void add_patch_mode_timing( + struct vbios_dco *dco, + struct crtc_timing *timing, + struct dcs_mode_timing_list *list) +{ + struct embedded_panel_patch_mode patch_mode; + uint32_t index = 0; + uint32_t refresh_rate = + (timing->pix_clk_khz * PIXEL_CLOCK_MULTIPLIER) / + (timing->h_total * timing->v_total); + + while (dal_adapter_service_enum_embedded_panel_patch_mode( + dco->as, index++, &patch_mode)) { + + struct mode_timing mode_timing = { { 0 } }; + + if (!patch_mode.width) + continue; + + mode_timing.crtc_timing = *timing; + mode_timing.mode_info.pixel_width = patch_mode.width; + mode_timing.mode_info.pixel_height = patch_mode.height; + mode_timing.mode_info.field_rate = refresh_rate; + mode_timing.mode_info.timing_standard = + TIMING_STANDARD_EXPLICIT; + mode_timing.mode_info.timing_source = TIMING_SOURCE_VBIOS; + + dal_dcs_mode_timing_list_append(list, &mode_timing); + } +} + +bool dal_vbios_dco_add_mode_timing( + struct vbios_dco *dco, + struct dcs_mode_timing_list *list, + bool *preffered_mode_found) +{ + struct mode_timing mode_timing = { { 0 } }; + + ASSERT(list != NULL); + + if (!get_vbios_native_mode_timing( + dco, &mode_timing, preffered_mode_found)) + return false; + + dal_dcs_mode_timing_list_append(list, &mode_timing); + + add_patch_mode_timing(dco, &mode_timing.crtc_timing, list); + + return true; +} + +bool dal_vbios_dco_get_panel_misc_info( + struct vbios_dco *dco, union panel_misc_info *panel_info) +{ + if (!panel_info) + return false; + + panel_info->bits.API_ENABLED = + dco->panel_info.lcd_timing.misc_info.API_ENABLED; + + panel_info->bits.COMPOSITE_SYNC = + dco->panel_info.lcd_timing.misc_info.COMPOSITE_SYNC; + + panel_info->bits.DOUBLE_CLOCK = + dco->panel_info.lcd_timing.misc_info.DOUBLE_CLOCK; + + panel_info->bits.GREY_LEVEL = + dco->panel_info.lcd_timing.misc_info.GREY_LEVEL; + + panel_info->bits.H_CUT_OFF = + dco->panel_info.lcd_timing.misc_info.HORIZONTAL_CUT_OFF; + + panel_info->bits.H_REPLICATION_BY_2 = + dco->panel_info.lcd_timing.misc_info.H_REPLICATION_BY2; + + panel_info->bits.H_SYNC_POLARITY = + dco->panel_info.lcd_timing.misc_info.H_SYNC_POLARITY; + + panel_info->bits.INTERLACE = + dco->panel_info.lcd_timing.misc_info.INTERLACE; + + panel_info->bits.RGB888 = + dco->panel_info.lcd_timing.misc_info.RGB888; + + panel_info->bits.SPATIAL = + dco->panel_info.lcd_timing.misc_info.SPATIAL; + + panel_info->bits.TEMPORAL = + dco->panel_info.lcd_timing.misc_info.TEMPORAL; + + panel_info->bits.V_CUT_OFF = + dco->panel_info.lcd_timing.misc_info.VERTICAL_CUT_OFF; + + panel_info->bits.V_REPLICATION_BY_2 = + dco->panel_info.lcd_timing.misc_info.V_REPLICATION_BY2; + + panel_info->bits.V_SYNC_POLARITY = + dco->panel_info.lcd_timing.misc_info.V_SYNC_POLARITY; + return true; + +} + +bool dal_vbios_dco_is_pixel_clk_ss_supported( + struct vbios_dco *dco) +{ + return dco->panel_info.ss_id != 0; +} + +uint32_t dal_vbios_dco_get_edid_buff_len( + struct vbios_dco *dco) +{ + return dco->edid_len; +} + +uint8_t *dal_vbios_dco_get_edid_buff( + struct vbios_dco *dco) +{ + return dco->edid_data; +} + +uint32_t dal_vbios_dco_get_pixel_clk_for_drr_khz( + struct vbios_dco *dco) +{ + return dco->panel_info.drr_enabled ? + dco->panel_info.lcd_timing.pixel_clk : 0; +} + +uint32_t dal_vbios_dco_get_min_fps_for_drr( + struct vbios_dco *dco) +{ + return dco->panel_info.drr_enabled ? + dco->panel_info.min_drr_refresh_rate : 0; +} diff --git a/drivers/gpu/drm/amd/dal/dcs/vbios_dco.h b/drivers/gpu/drm/amd/dal/dcs/vbios_dco.h new file mode 100644 index 000000000000..dcb7e05e888f --- /dev/null +++ b/drivers/gpu/drm/amd/dal/dcs/vbios_dco.h @@ -0,0 +1,77 @@ +/* + * Copyright 2012-15 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 + * + */ + +#ifndef __DAL_VBIOS_DCO__ +#define __DAL_VBIOS_DCO__ + +struct lcd_resolution { + uint32_t width; + uint32_t height; +}; + +struct vbios_dco; +union panel_misc_info; +struct adapter_service; +struct dcs_mode_timing_list; + +struct vbios_dco *dal_vbios_dco_create( + struct adapter_service *as); + +void dal_vbios_dco_destroy( + struct vbios_dco **dco); + + +bool dal_vbios_dco_construct( + struct vbios_dco *dco, + struct adapter_service *as); + +void dal_vbios_dco_destruct( + struct vbios_dco *dco); + +bool dal_vbios_dco_add_mode_timing( + struct vbios_dco *dco, + struct dcs_mode_timing_list *list, + bool *preffered_mode_found); + +bool dal_vbios_dco_get_panel_misc_info( + struct vbios_dco *dco, + union panel_misc_info *panel_info); + +bool dal_vbios_dco_is_pixel_clk_ss_supported( + struct vbios_dco *dco); + +uint32_t dal_vbios_dco_get_edid_buff_len( + struct vbios_dco *dco); + +uint8_t *dal_vbios_dco_get_edid_buff( + struct vbios_dco *dco); + +uint32_t dal_vbios_dco_get_pixel_clk_for_drr_khz( + struct vbios_dco *dco); + +uint32_t dal_vbios_dco_get_min_fps_for_drr( + struct vbios_dco *dco); + +#endif /* __DAL_VBIOS_DCO__ */ diff --git a/drivers/gpu/drm/amd/dal/include/dcs_interface.h b/drivers/gpu/drm/amd/dal/include/dcs_interface.h new file mode 100644 index 000000000000..1e8b69a20c29 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/include/dcs_interface.h @@ -0,0 +1,376 @@ +/* Copyright 2012-15 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 + * + */ +#ifndef __DAL_DCS_INTERFACE_H__ +#define __DAL_DCS_INTERFACE_H__ + +#include "dcs_types.h" +#include "grph_object_id.h" +#include "flat_set.h" + +struct dal_context; +struct dcs; +struct ddc_service; +enum ddc_transaction_type; +enum ddc_result; +struct display_sink_capability; +enum timing_3d_format; + +struct dcs_cea_audio_mode_list; +struct dcs_customized_mode_list; + +struct dcs_init_data { + struct dal_context *dal; + struct adapter_service *as; + struct timing_service *ts; + enum dcs_interface_type interface_type; + struct graphics_object_id grph_obj_id; +}; + +struct dcs_mode_timing_list { + struct flat_set list; +}; + +struct dcs_mode_timing_list *dal_dcs_mode_timing_list_create( + uint32_t list_size); + +void dal_dcs_mode_timing_list_destroy( + struct dcs_mode_timing_list **list); + +bool dal_dcs_mode_timing_list_append( + struct dcs_mode_timing_list *list, + const struct mode_timing *mode_timing); +uint32_t dal_dcs_mode_timing_list_get_count( + const struct dcs_mode_timing_list *list); + +void dal_dcs_mode_timing_list_remove_at_index( + struct dcs_mode_timing_list *list, + uint32_t index); + +struct mode_timing *dal_dcs_mode_timing_list_at_index( + struct dcs_mode_timing_list *list, + uint32_t index); + +void dal_dcs_mode_timing_list_clear(struct dcs_mode_timing_list *list); + +struct dcs_cea_audio_mode_list *dal_dcs_cea_audio_mode_list_create( + uint32_t list_size); + +void dal_dcs_cea_audio_mode_list_destroy( + struct dcs_cea_audio_mode_list **list); + +bool dal_dcs_cea_audio_mode_list_append( + struct dcs_cea_audio_mode_list *list, + struct cea_audio_mode *cea_audio_mode); +uint32_t dal_dcs_cea_audio_mode_list_get_count( + const struct dcs_cea_audio_mode_list *list); +void dal_dcs_cea_audio_mode_list_clear( + struct dcs_cea_audio_mode_list *list); + +struct cea_audio_mode *dal_dcs_cea_audio_mode_list_at_index( + const struct dcs_cea_audio_mode_list *list, + uint32_t index); + +struct dcs *dal_dcs_create(const struct dcs_init_data *init_data); + +void dal_dcs_destroy(struct dcs **dcs); + +enum edid_retrieve_status dal_dcs_retrieve_raw_edid(struct dcs *dcs); + +uint32_t dal_dcs_get_edid_raw_data_size(struct dcs *dcs); + +enum edid_retrieve_status dal_dcs_override_raw_edid( + struct dcs *dcs, + uint32_t len, + uint8_t *data); + +const uint8_t *dal_dcs_get_edid_raw_data( + struct dcs *dcs, + uint32_t *buff_size); + +enum edid_retrieve_status dal_dcs_update_edid_from_last_retrieved( + struct dcs *dcs); + +/*Update DDC Service. returns the old DdcService being replaced*/ +struct ddc_service *dal_dcs_update_ddc( + struct dcs *dcs, + struct ddc_service *ddc); + +void dal_dcs_set_transaction_type( + struct dcs *dcs, + enum ddc_transaction_type type); + +/*updates the ModeTimingList of given path with +ModeTiming reported by this DCS*/ +void dal_dcs_update_ts_timing_list_on_display( + struct dcs *dcs, + uint32_t display_index); + +/* DDC query on generic slave address*/ +bool dal_dcs_query_ddc_data( + struct dcs *dcs, + uint32_t address, + uint8_t *write_buf, + uint32_t write_buff_size, + uint8_t *read_buff, + uint32_t read_buff_size); + +bool dal_dcs_get_vendor_product_id_info( + struct dcs *dcs, + struct vendor_product_id_info *info); + +bool dal_dcs_get_display_name(struct dcs *dcs, uint8_t *name, uint32_t size); + +bool dal_dcs_get_display_characteristics( + struct dcs *dcs, + struct display_characteristics *characteristics); + +bool dal_dcs_get_screen_info( + struct dcs *dcs, + struct edid_screen_info *info); + +enum dcs_edid_connector_type dal_dcs_get_connector_type(struct dcs *dcs); + +bool dal_dcs_get_display_pixel_encoding( + struct dcs *dcs, + struct display_pixel_encoding_support *pe); + +enum display_dongle_type dal_dcs_get_dongle_type(struct dcs *dcs); + +void dal_dcs_query_sink_capability( + struct dcs *dcs, + struct display_sink_capability *sink_cap, + bool hpd_sense_bit); + +void dal_dcs_reset_sink_capability(struct dcs *dcs); + +bool dal_dcs_get_sink_capability( + struct dcs *dcs, + struct display_sink_capability *sink_cap); + +bool dal_dcs_emulate_sink_capability( + struct dcs *dcs, + struct display_sink_capability *sink_cap); + +bool dal_dcs_get_display_color_depth( + struct dcs *dcs, + struct display_color_depth_support *color_depth); + +bool dal_dcs_get_display_pixel_encoding( + struct dcs *dcs, + struct display_pixel_encoding_support *pixel_encoding); + +bool dal_dcs_get_cea861_support( + struct dcs *dcs, + struct cea861_support *cea861_support); + +bool dal_dcs_get_cea_vendor_specific_data_block( + struct dcs *dcs, + struct cea_vendor_specific_data_block *vendor_block); + +bool dal_dcs_get_cea_speaker_allocation_data_block( + struct dcs *dcs, + enum signal_type signal, + union cea_speaker_allocation_data_block *spkr_data); + +bool dal_dcs_get_cea_colorimetry_data_block( + struct dcs *dcs, + struct cea_colorimetry_data_block *colorimetry_data_block); + +bool dal_dcs_get_cea_video_capability_data_block( + struct dcs *dcs, + union cea_video_capability_data_block *video_capability_data_block); + +uint32_t dal_dcs_get_extensions_num(struct dcs *dcs); + +const struct dcs_cea_audio_mode_list *dal_dcs_get_cea_audio_modes( + struct dcs *dcs, + enum signal_type signal); + +bool dal_dcs_is_audio_supported(struct dcs *dcs); + +bool dal_dcs_validate_customized_mode( + struct dcs *dcs, + const struct dcs_customized_mode *customized_mode); + +bool dal_dcs_add_customized_mode( + struct dcs *dcs, + struct dcs_customized_mode *customized_mode); + +bool dal_dcs_delete_customized_mode(struct dcs *dcs, uint32_t index); + +const struct dcs_customized_mode_list *dal_dcs_get_customized_modes( + struct dcs *dcs); + +bool dal_dcs_delete_mode_timing_override( + struct dcs *dcs, + struct dcs_override_mode_timing *dcs_mode_timing); + +bool dal_dcs_set_mode_timing_override( + struct dcs *dcs, + uint32_t display_index, + struct dcs_override_mode_timing *dcs_mode_timing); + +bool dal_dcs_get_timing_override_for_mode( + struct dcs *dcs, + uint32_t display_index, + struct mode_info *mode_info, + struct dcs_override_mode_timing_list *dcs_mode_timing_list); + +uint32_t dal_dcs_get_num_mode_timing_overrides(struct dcs *dcs); + +bool dal_dcs_get_timing_override_list( + struct dcs *dcs, + uint32_t display_index, + struct dcs_override_mode_timing_list *dcs_mode_timing_list, + uint32_t size); + +bool dal_dcs_get_supported_force_hdtv_mode( + struct dcs *dcs, + union hdtv_mode_support *hdtv_mode); + +bool dal_dcs_get_user_force_hdtv_mode( + struct dcs *dcs, + union hdtv_mode_support *hdtv_mode); + +bool dal_dcs_set_user_force_hdtv_mode( + struct dcs *dcs, + const union hdtv_mode_support *hdtv_mode); + +bool dal_dcs_get_fid9204_allow_ce_mode_only_option( + struct dcs *dcs, + bool is_hdmi, + bool *enable); + +bool dal_dcs_set_fid9204_allow_ce_mode_only_option( + struct dcs *dcs, + bool is_hdmi, + bool enable); + +bool dal_dcs_get_panel_misc_info( + struct dcs *dcs, + union panel_misc_info *panel_info); + +enum ddc_result dal_dcs_dpcd_read( + struct dcs *dcs, + uint32_t address, + uint8_t *buffer, + uint32_t length); + +enum ddc_result dal_dcs_dpcd_write( + struct dcs *dcs, + uint32_t address, + const uint8_t *buffer, + uint32_t length); + +bool dal_dcs_get_range_limit( + struct dcs *dcs, + struct display_range_limits *limit); + +bool dal_dcs_set_range_limit_override( + struct dcs *dcs, + struct display_range_limits *limit); + +bool dal_dcs_get_user_select_limit( + struct dcs *dcs, + struct monitor_user_select_limits *limit); + +bool dal_dcs_set_user_select_limit( + struct dcs *dcs, + struct monitor_user_select_limits *limit); + +bool dal_dcs_get_dongle_mode_support( + struct dcs *dcs, + union hdtv_mode_support *hdtv_mode); + +bool dal_dcs_get_timing_limits( + struct dcs *dcs, + struct timing_limits *timing_limits); + +bool dal_dcs_get_drr_config( + struct dcs *dcs, + struct drr_config *config); + +bool dal_dcs_force_dp_audio(struct dcs *dcs, bool force_audio_on); + +bool dal_dcs_is_dp_audio_forced(struct dcs *dcs); + +const struct monitor_patch_info *dal_dcs_get_monitor_patch_info( + struct dcs *dcs, + enum monitor_patch_type patch_type); + +bool dal_dcs_set_monitor_patch_info( + struct dcs *dcs, + struct monitor_patch_info *patch_info); + +union dcs_monitor_patch_flags dal_dcs_get_monitor_patch_flags(struct dcs *dcs); + +enum dcs_packed_pixel_format dal_dcs_get_enabled_packed_pixel_format( + struct dcs *dcs); + +enum dcs_packed_pixel_format dal_dcs_get_monitor_packed_pixel_format( + struct dcs *dcs); + +bool dal_dcs_report_single_selected_timing(struct dcs *dcs); + +bool dal_dcs_can_tile_scale(struct dcs *dcs); + +void dal_dcs_set_single_selected_timing_restriction( + struct dcs *dcs, + bool value); + +const struct dcs_edid_supported_max_bw *dal_dcs_get_edid_supported_max_bw( + struct dcs *dcs); + +bool dal_dcs_is_non_continous_frequency(struct dcs *dcs); + +struct dcs_stereo_3d_features dal_dcs_get_stereo_3d_features( + struct dcs *dcs, + enum timing_3d_format format); + +union stereo_3d_support dal_dcs_get_stereo_3d_support(struct dcs *dcs); + +void dal_dcs_override_stereo_3d_support( + struct dcs *dcs, + union stereo_3d_support support); + +void dal_dcs_set_remote_display_receiver_capabilities( + struct dcs *dcs, + const struct dal_remote_display_receiver_capability *cap); + +void dal_dcs_clear_remote_display_receiver_capabilities(struct dcs *dcs); + +bool dal_dcs_get_display_tile_info( + struct dcs *dcs, + struct dcs_display_tile *display_tile, + bool first_display); + +bool dal_dcs_get_container_id(struct dcs *dcs, + struct dcs_container_id *container_id); + +bool dal_dcs_set_container_id(struct dcs *dcs, + struct dcs_container_id *container_id); + +union dcs_monitor_patch_flags dal_dcs_get_monitor_patch_flags(struct dcs *dcs); + +#endif /* __DAL_DCS_INTERFACE_H__ */ diff --git a/drivers/gpu/drm/amd/dal/include/dcs_types.h b/drivers/gpu/drm/amd/dal/include/dcs_types.h new file mode 100644 index 000000000000..623c43baee06 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/include/dcs_types.h @@ -0,0 +1,752 @@ +/* + * Copyright 2012-15 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 + * + */ + +#ifndef __DAL_DCS_TYPES_H__ +#define __DAL_DCS_TYPES_H__ + +#include "timing_service_types.h" +#include "signal_types.h" + +#define NUM_OF_BYTE_EDID_COLOR_CHARACTERISTICS 10 +#define MAX_NUM_OF_HDMI_VSDB_3D_EXTENDED_SUPPORT 21 +#define MAX_NUM_OF_HDMI_VSDB_VICS 7 +#define MAX_NUM_OF_HDMI_VSDB_3D_MULTI_SUPPORT 16 +#define TILED_DISPLAY_HORIZONTAL 1920 +#define TILED_DISPLAY_VERTICAL 2160 + +struct drr_config { + /* minimum frame per second for dynamic + * refresh rate feature; 0 if drr support not found*/ + uint32_t min_fps_in_microhz; + bool force_lock_on_event; + bool lock_to_master_vsync; + + struct { + uint8_t FORCED_BY_REGKEY_OR_ESCAPE:1; + uint8_t FORCED_BY_VBIOS:1; + uint8_t SUPPORTED_BY_EDID:1; + } support_method; +}; + +struct timing_limits { + uint32_t min_pixel_clock_in_khz; + uint32_t max_pixel_clock_in_khz; +}; + +struct vendor_product_id_info { + uint32_t manufacturer_id; + uint32_t product_id; + uint32_t serial_id; + uint32_t manufacture_week; + uint32_t manufacture_year; +}; + +struct display_range_limits { + uint32_t min_v_rate_hz; + uint32_t max_v_rate_hz; + uint32_t min_h_rate_khz; + uint32_t max_h_rateIn_khz; + uint32_t max_pix_clk_khz; + bool use_override; +}; + +struct monitor_user_select_limits { + bool use_ati_override; + uint32_t max_h_res; + uint32_t max_v_res; + uint32_t max_refresh_rate; +}; + +enum edid_screen_aspect_ratio { + EDID_SCREEN_AR_UNKNOWN = 0, + EDID_SCREEN_AR_PROJECTOR, + EDID_SCREEN_AR_16X9, + EDID_SCREEN_AR_16X10, + EDID_SCREEN_AR_4X3, + EDID_SCREEN_AR_5X4, + EDID_SCREEN_AR_9X16, + EDID_SCREEN_AR_10X16, + EDID_SCREEN_AR_3X4, + EDID_SCREEN_AR_4X5 +}; + +struct edid_screen_info { + enum edid_screen_aspect_ratio aspect_ratio; + uint32_t width; + uint32_t height; +}; + +struct display_characteristics { + uint8_t gamma; + uint8_t color_characteristics[NUM_OF_BYTE_EDID_COLOR_CHARACTERISTICS]; +}; + +union cv_smart_dongle_modes { + uint8_t all; + struct cv_smart_dongle_switches { + uint8_t MODE_1080I:1; + uint8_t MODE_720P:1; + uint8_t MODE_540P:1; + uint8_t MODE_480P:1; + uint8_t MODE_480I:1; + uint8_t MODE_16_9:1; + } switches; +}; + +struct cea_audio_mode { + uint8_t format_code; /* ucData[0] [6:3]*/ + uint8_t channel_count; /* ucData[0] [2:0]*/ + uint8_t sample_rate; /* ucData[1]*/ + union { + uint8_t sample_size; /* for LPCM*/ + /* for Audio Formats 2-8 (Max bit rate divided by 8 kHz)*/ + uint8_t max_bit_rate; + uint8_t audio_codec_vendor_specific; /* for Audio Formats 9-15*/ + }; +}; + +union cea_speaker_allocation_data_block { + struct { + uint32_t FL_FR:1; + uint32_t LFE:1; + uint32_t FC:1; + uint32_t RL_RR:1; + uint32_t RC:1; + uint32_t FLC_FRC:1; + uint32_t RLC_RRC:1; + } bits; + uint32_t raw; +}; + +struct cea_colorimetry_data_block { + struct { + uint32_t XV_YCC601:1; + uint32_t XV_YCC709:1; + uint32_t S_YCC601:1; + uint32_t ADOBE_YCC601:1; + uint32_t ADOBE_RGB:1; + + } flag; + struct { + uint32_t MD0:1; + uint32_t MD1:1; + uint32_t MD2:1; + uint32_t MD3:1; + } metadata_flag; +}; + +union cea_video_capability_data_block { + struct { + uint8_t S_CE0:1; + uint8_t S_CE1:1; + uint8_t S_IT0:1; + uint8_t S_IT1:1; + uint8_t S_PT0:1; + uint8_t S_PT1:1; + uint8_t QS:1; + uint8_t QY:1; + } bits; + uint8_t raw; +}; + +enum stereo_3d_multi_presence { + STEREO_3D_MULTI_NOT_PRESENT = 0, + STEREO_3D_MULTI_ALL_FORMATS, + STEREO_3D_MULTI_MASKED_FORMATS, + STEREO_3D_MULTI_RESERVED +}; + +enum cea_hdmi_vic { + CEA_HDMI_VIC_RESERVED = 0, + CEA_HDMI_VIC_4KX2K_30, + CEA_HDMI_VIC_4KX2K_25, + CEA_HDMI_VIC_4KX2K_24, + CEA_HDMI_VIC_4KX2K_24_SMPTE +}; + +struct cea_hdmi_vsdb_extended_caps { + uint32_t reserved; + uint32_t image_size; + enum stereo_3d_multi_presence stereo_3d_multi_present; + bool stereo_3d_present; + uint32_t hdmi_3d_len; + uint32_t hdmi_vic_len; +}; + +struct cea_vendor_specific_data_block { + + uint32_t ieee_id; + + struct commonent_phy { + uint32_t PHY_ADDR_A:4; + uint32_t PHY_ADDR_B:4; + uint32_t PHY_ADDR_C:4; + uint32_t PHY_ADDR_D:4; + } commonent_phy_addr; + + struct byte6 { + uint32_t SUPPORTS_AI:1; + uint32_t DC_48BIT:1; + uint32_t DC_36BIT:1; + uint32_t DC_30BIT:1; + uint32_t DC_Y444:1; + uint32_t DVI_DUAL:1; + uint32_t RESERVED:2; + } byte6;/* link capabilities*/ + bool byte6_valid; + + uint32_t max_tmds_clk_mhz; + + struct byte8 { + uint32_t LATENCY_FIELDS_PRESENT:1; + uint32_t ILATENCY_FIELDS_PRESENT:1; + uint32_t HDMI_VIDEO_PRESENT:1; + uint32_t RESERVED:1; + uint32_t CNC3_GAME:1; + uint32_t CNC2_CINEMA:1; + uint32_t CNC1_PHOTO:1; + uint32_t CNC0_GRAPHICS:1; + } byte8; + /*bit 6-7: latency flags to indicate valid latency fields*/ + /*bit 5: support of additional video format capabilities*/ + /* bit 0-3: flags indicating which content type is supported*/ + uint32_t video_latency; + uint32_t audio_latency; + uint32_t i_video_latency; + uint32_t i_audio_latency; + + struct cea_hdmi_vsdb_extended_caps hdmi_vsdb_extended_caps; + + enum stereo_3d_multi_presence stereo_3d_multi_present; + + struct { + bool FRAME_PACKING:1; + bool SIDE_BY_SIDE_HALF:1; + bool TOP_AND_BOTTOM:1; + } stereo_3d_all_support; + uint16_t stereo_3d_mask; + + enum cea_hdmi_vic hdmi_vic[MAX_NUM_OF_HDMI_VSDB_VICS]; + struct stereo_3d_extended_support { + struct { + bool FRAME_PACKING:1; + bool SIDE_BY_SIDE_HALF:1; + bool TOP_AND_BOTTOM:1; + } format; + uint32_t vic_index; + uint32_t value; + uint32_t size; + } stereo_3d_extended_support[MAX_NUM_OF_HDMI_VSDB_3D_EXTENDED_SUPPORT]; +}; + +struct cea861_support { + + uint32_t revision; + union { + struct { + uint32_t NATIVE_COUNT:4; + uint32_t BASE_AUDIO:1; + uint32_t YCRCB444:1; + uint32_t YCRCB422:1; + uint32_t UNDER_SCAN:1; + } features; + uint8_t raw_features; + }; +}; + +struct dcs_customized_mode { + struct { + uint32_t READ_ONLY:1; + uint32_t ADD_BY_DRIVER:1; + uint32_t INTERLACED:1; + uint32_t BASE_MODE:1; + } flags; + struct mode_info base_mode_info; + struct mode_info customized_mode_info; +}; + +struct dcs_override_mode_timing { + /* possible timing standards, bit vector of TimingStandard*/ + uint32_t possible_timing_standards; + /* indicate driver default timing is used , no override*/ + bool use_driver_default_timing; + struct mode_timing mode_timing; +}; + +struct dcs_override_mode_timing_list { + uint32_t max_num_overrides; + uint32_t num_overrides; + struct dcs_override_mode_timing mode_timings[1]; +}; + +/* "interface type" is different from Signal Type because + * an "interface type" can be driven by more than one Signal Type. + * For example, INTERFACE_TYPE_DVI can be driven by + * Single or Dual link DVI signal. */ +enum dcs_interface_type { + INTERFACE_TYPE_VGA = 0, + INTERFACE_TYPE_DVI, + INTERFACE_TYPE_CV, + INTERFACE_TYPE_TV, + INTERFACE_TYPE_LVDS, + INTERFACE_TYPE_DP, + INTERFACE_TYPE_WIRELESS, + INTERFACE_TYPE_CF, + INTERFACE_TYPE_EDP +}; + +enum dcs_edid_connector_type { + EDID_CONNECTOR_UNKNOWN = 0, + EDID_CONNECTOR_ANALOG = 1, + EDID_CONNECTOR_DIGITAL = 10, + EDID_CONNECTOR_DVI = 11, + EDID_CONNECTOR_HDMIA = 12, + EDID_CONNECTOR_MDDI = 14, + EDID_CONNECTOR_DISPLAYPORT = 15 +}; + +union panel_misc_info { + struct { + uint32_t H_CUT_OFF:1; + uint32_t H_SYNC_POLARITY:1;/*0=Active High, 1=Active Low*/ + uint32_t V_SYNC_POLARITY:1; /*0=Active High, 1=Active Low*/ + uint32_t V_CUT_OFF:1; + uint32_t H_REPLICATION_BY_2:1; + uint32_t V_REPLICATION_BY_2:1; + uint32_t COMPOSITE_SYNC:1; + uint32_t INTERLACE:1; + uint32_t DOUBLE_CLOCK:1; + uint32_t RGB888:1; + uint32_t GREY_LEVEL:2; + uint32_t SPATIAL:1; + uint32_t TEMPORAL:1; + uint32_t API_ENABLED:1; + } bits; + uint32_t raw; +}; + +union hdtv_mode_support { + struct { + uint32_t HDTV_SUPPORT_480I:1; + uint32_t HDTV_SUPPORT_480P:1; + uint32_t HDTV_SUPPORT_576I25:1; + uint32_t HDTV_SUPPORT_576P50:1; + uint32_t HDTV_SUPPORT_720P:1; + uint32_t HDTV_SUPPORT_720P50:1; + uint32_t HDTV_SUPPORT_1080I:1; + uint32_t HDTV_SUPPORT_1080I25:1; + uint32_t HDTV_SUPPORT_1080P:1; + uint32_t HDTV_SUPPORT_1080P50:1; + uint32_t HDTV_SUPPORT_1080P24:1; + uint32_t HDTV_SUPPORT_1080P25:1; + uint32_t HDTV_SUPPORT_1080P30:1; + uint32_t HDTV_SUPPORT_16X9:1; + } bits; + uint32_t raw; +}; + +enum edid_retrieve_status { + EDID_RETRIEVE_SUCCESS = 0, + EDID_RETRIEVE_FAIL, + EDID_RETRIEVE_SAME_EDID, + EDID_RETRIEVE_FAIL_WITH_PREVIOUS_SUCCESS +}; + +#define DCS_DECODE_EDID_RETRIEVE_STATUS(status) \ + (status == EDID_RETRIEVE_SUCCESS) ? "EDID_RETRIEVE_SUCCESS" : \ + (status == EDID_RETRIEVE_FAIL) ? "EDID_RETRIEVE_FAIL" : \ + (status == EDID_RETRIEVE_SAME_EDID) ? "EDID_RETRIEVE_SAME_EDID" : \ + (status == EDID_RETRIEVE_FAIL_WITH_PREVIOUS_SUCCESS) ? \ + "EDID_RETRIEVE_FAIL_WITH_PREVIOUS_SUCCESS" : "Unknown" + + +#ifndef TV_SIGNALFORMAT_DEFINED +#define TV_SIGNALFORMAT_DEFINED +enum tv_signal_format { + TV_SIGNAL_FORMAT_UNKNOWN, + TV_SIGNAL_FORMAT_NTSC, + TV_SIGNAL_FORMAT_NTSC_J, + TV_SIGNAL_FORMAT_PAL, + TV_SIGNAL_FORMAT_PAL_M, + TV_SIGNAL_FORMAT_PAL_CN, + TV_SIGNAL_FORMAT_SECAM +}; +#endif + +enum tv_signal_format_result { + TV_SIGNAL_FORMAT_RESULT_OK, + TV_SIGNAL_FORMAT_SET_MODE_REQ, + TV_SIGNAL_FORMAT_REBOOT_REQ, + TV_SIGNAL_FORMAT_ERROR +}; + +enum pixel_encoding_mask { + PIXEL_ENCODING_MASK_YCBCR444 = 0x01, + PIXEL_ENCODING_MASK_YCBCR422 = 0x02, + PIXEL_ENCODING_MASK_RGB = 0x04, +}; + +struct display_pixel_encoding_support { + uint32_t mask; +}; + +enum color_depth_index { + COLOR_DEPTH_INDEX_UNKNOWN, + COLOR_DEPTH_INDEX_666 = 0x01, + COLOR_DEPTH_INDEX_888 = 0x02, + COLOR_DEPTH_INDEX_101010 = 0x04, + COLOR_DEPTH_INDEX_121212 = 0x08, + COLOR_DEPTH_INDEX_141414 = 0x10, + COLOR_DEPTH_INDEX_161616 = 0x20, + COLOR_DEPTH_INDEX_LAST = 0x40, +}; + +struct display_color_depth_support { + uint32_t mask; + bool deep_color_native_res_only; +}; + +struct display_color_and_pixel_support { + struct display_color_depth_support color_depth_support; + struct display_pixel_encoding_support pixel_encoding_support; + bool deep_color_y444_support; +}; + +enum dcs_packed_pixel_format { + DCS_PACKED_PIXEL_FORMAT_NOT_PACKED = 0, + DCS_PACKED_PIXEL_FORMAT_SPLIT_G70_B54_R70_B10 = 1, + DCS_PACKED_PIXEL_FORMAT_R70_G76 = 2, + DCS_PACKED_PIXEL_FORMAT_SPLIT_B70_G10_R70_G76 = 3, + DCS_PACKED_PIXEL_FORMAT_G70_B54_R70_B10 = 4, + DCS_PACKED_PIXEL_FORMAT_G70_B54 = 5, + DCS_PACKED_PIXEL_FORMAT_B70_R30_G70_R74 = 6, + DCS_PACKED_PIXEL_FORMAT_B70_G70_R70 = 7, + DCS_PACKED_PIXEL_FORMAT_B70_R32_G70_R76 = 8, +}; + +enum monitor_manufacturer_id { + MONITOR_MANUFACTURER_ID_0 = 0x0000, + MONITOR_MANUFACTURER_ID_1 = 0x3834, + MONITOR_MANUFACTURER_ID_2 = 0x4d24, + MONITOR_MANUFACTURER_ID_3 = 0x293E, + MONITOR_MANUFACTURER_ID_4 = 0x635a, + MONITOR_MANUFACTURER_ID_5 = 0x1006, + MONITOR_MANUFACTURER_ID_6 = 0xc32a, + MONITOR_MANUFACTURER_ID_7 = 0x4d24, + MONITOR_MANUFACTURER_ID_8 = 0x110e, + MONITOR_MANUFACTURER_ID_9 = 0xaf0d, + MONITOR_MANUFACTURER_ID_10 = 0x6D1E, + MONITOR_MANUFACTURER_ID_11 = 0xA338, + MONITOR_MANUFACTURER_ID_12 = 0xC315, + MONITOR_MANUFACTURER_ID_13 = 0xD94D, + MONITOR_MANUFACTURER_ID_14 = 0x104D, + MONITOR_MANUFACTURER_ID_15 = 0x855C, + MONITOR_MANUFACTURER_ID_16 = 0x4251, + MONITOR_MANUFACTURER_ID_17 = 0xA934, + MONITOR_MANUFACTURER_ID_18 = 0x0C41, + /* TODO: Update when EDID is available */ + MONITOR_MANUFACTURER_ID_19 = 0xDEAD, + MONITOR_MANUFACTURER_ID_20 = 0x6904, + MONITOR_MANUFACTURER_ID_21 = 0xAC10, + MONITOR_MANUFACTURER_ID_22 = 0x2D4C, + MONITOR_MANUFACTURER_ID_23 = 0x144E, + MONITOR_MANUFACTURER_ID_24 = 0x6c50, + MONITOR_MANUFACTURER_ID_26 = 0x0c41, + MONITOR_MANUFACTURER_ID_27 = 0x143E, + MONITOR_MANUFACTURER_ID_25 = 0xffff, + MONITOR_MANUFACTURER_ID_28 = 0x3421, + MONITOR_MANUFACTURER_ID_29 = 0x2D19, + MONITOR_MANUFACTURER_ID_30 = 0x8B52, + MONITOR_MANUFACTURER_ID_31 = 0x7204, + MONITOR_MANUFACTURER_ID_32 = 0xF022, + MONITOR_MANUFACTURER_ID_33 = 0x0E11, + MONITOR_MANUFACTURER_ID_34 = 0xD241, + MONITOR_MANUFACTURER_ID_35 = 0xAE30, + MONITOR_MANUFACTURER_ID_36 = 0xF91E, + MONITOR_MANUFACTURER_ID_37 = 0xAB4C, +}; + +enum monitor_product_id { + MONITOR_PRODUCT_ID_0 = 0x0000, + MONITOR_PRODUCT_ID_1 = 0x0BCC, + MONITOR_PRODUCT_ID_2 = 0x251F, + MONITOR_PRODUCT_ID_3 = 0x5241, + MONITOR_PRODUCT_ID_4 = 0x6919, + MONITOR_PRODUCT_ID_5 = 0xee18, + MONITOR_PRODUCT_ID_6 = 0xf008, + MONITOR_PRODUCT_ID_7 = 0x2f0c, + MONITOR_PRODUCT_ID_7_2 = 0x3411, + MONITOR_PRODUCT_ID_9 = 0x4208, + MONITOR_PRODUCT_ID_10 = 0xE51D, + MONITOR_PRODUCT_ID_11 = 0x7E22, + MONITOR_PRODUCT_ID_12 = 0x0E23, + MONITOR_PRODUCT_ID_13 = 0x9d08, + MONITOR_PRODUCT_ID_14 = 0x9236, + MONITOR_PRODUCT_ID_15 = 0x9227, + MONITOR_PRODUCT_ID_16 = 0x0220, + MONITOR_PRODUCT_ID_17 = 0x4920, + MONITOR_PRODUCT_ID_18 = 0x251f, + MONITOR_PRODUCT_ID_19 = 0x1395, + MONITOR_PRODUCT_ID_20 = 0xc04e, + MONITOR_PRODUCT_ID_21 = 0x5787, + MONITOR_PRODUCT_ID_22 = 0x5A71, + MONITOR_PRODUCT_ID_23 = 0x6622, + MONITOR_PRODUCT_ID_24 = 0x20C1, + MONITOR_PRODUCT_ID_25 = 0x2110, + MONITOR_PRODUCT_ID_26 = 0x2006, + MONITOR_PRODUCT_ID_27 = 0x1827, + MONITOR_PRODUCT_ID_28 = 0x0EA0, + MONITOR_PRODUCT_ID_29 = 0x03D0, + MONITOR_PRODUCT_ID_30 = 0x01D2, + MONITOR_PRODUCT_ID_31 = 0x2801, + MONITOR_PRODUCT_ID_32 = 0x0FB3, + MONITOR_PRODUCT_ID_33 = 0x0FB1, + MONITOR_PRODUCT_ID_34 = 0xA045, + MONITOR_PRODUCT_ID_35 = 0x0001, + MONITOR_PRODUCT_ID_36 = 0xA296, + MONITOR_PRODUCT_ID_38 = 0x21DC, + MONITOR_PRODUCT_ID_37 = 0x21EA, + MONITOR_PRODUCT_ID_39 = 0x4093, + MONITOR_PRODUCT_ID_40 = 0x4094, + MONITOR_PRODUCT_ID_41 = 0x4094, + MONITOR_PRODUCT_ID_42 = 0x32A2, + MONITOR_PRODUCT_ID_43 = 0xE009, + MONITOR_PRODUCT_ID_44 = 0xA010, + MONITOR_PRODUCT_ID_45 = 0x405C, + MONITOR_PRODUCT_ID_46 = 0xF017, + MONITOR_PRODUCT_ID_47 = 0xD026, + MONITOR_PRODUCT_ID_48 = 0x4036, + MONITOR_PRODUCT_ID_49 = 0x4065, + MONITOR_PRODUCT_ID_50 = 0xA02A, + MONITOR_PRODUCT_ID_51 = 0xA02C, + MONITOR_PRODUCT_ID_46_HDMI = 0xF016, + MONITOR_PRODUCT_ID_53 = 0xF048, + MONITOR_PRODUCT_ID_54 = 0xA0A2, + MONITOR_PRODUCT_ID_55 = 0x4083, + MONITOR_PRODUCT_ID_56 = 0x0E74, + MONITOR_PRODUCT_ID_57 = 0x2771, + MONITOR_PRODUCT_ID_58 = 0x0814, + MONITOR_PRODUCT_ID_59 = 0xffff, + MONITOR_PRODUCT_ID_60 = 0x3339, + MONITOR_PRODUCT_ID_61 = 0x01F5, + MONITOR_PRODUCT_ID_62 = 0x02A5, + MONITOR_PRODUCT_ID_63 = 0x06AC, + MONITOR_PRODUCT_ID_64 = 0x04D5, + MONITOR_PRODUCT_ID_65 = 0x079D, + MONITOR_PRODUCT_ID_66 = 0x079F, + MONITOR_PRODUCT_ID_67 = 0x0797, + MONITOR_PRODUCT_ID_68 = 0x0B80, + MONITOR_PRODUCT_ID_69 = 0x7D06, + MONITOR_PRODUCT_ID_70 = 0x0131, + MONITOR_PRODUCT_ID_71 = 0x8545, + MONITOR_PRODUCT_ID_72 = 0x0002, + MONITOR_PRODUCT_ID_73 = 0x0125, + MONITOR_PRODUCT_ID_74 = 0x00D0, + MONITOR_PRODUCT_ID_75 = 0x26F7, + MONITOR_PRODUCT_ID_76 = 0x26F9, + MONITOR_PRODUCT_ID_77 = 0x2807, + MONITOR_PRODUCT_ID_78 = 0x26F3, + MONITOR_PRODUCT_ID_79 = 0x2676, + MONITOR_PRODUCT_ID_80 = 0x0A72, + MONITOR_PRODUCT_ID_81 = 0x2693, + MONITOR_PRODUCT_ID_82 = 0x2615, + MONITOR_PRODUCT_ID_83 = 0x2613, + MONITOR_PRODUCT_ID_84 = 0x262D, + MONITOR_PRODUCT_ID_85 = 0x264B, + MONITOR_PRODUCT_ID_86 = 0x2869, + MONITOR_PRODUCT_ID_87 = 0x286C, + MONITOR_PRODUCT_ID_88 = 0x288F, + MONITOR_PRODUCT_ID_89 = 0x2954, + MONITOR_PRODUCT_ID_90 = 0x6522, + MONITOR_PRODUCT_ID_91 = 0x0FAE, + MONITOR_PRODUCT_ID_92 = 0x0A0C, + MONITOR_PRODUCT_ID_93 = 0x00BF, + MONITOR_PRODUCT_ID_94 = 0x0, +}; + +enum monitor_patch_type { + MONITOR_PATCH_TYPE_NONE, + MONITOR_PATCH_TYPE_ERROR_CHECKSUM, + MONITOR_PATCH_TYPE_HDTV_WITH_PURE_DFP_EDID, + MONITOR_PATCH_TYPE_DO_NOT_USE_DETAILED_TIMING, + MONITOR_PATCH_TYPE_DO_NOT_USE_RANGE_LIMITATION, + MONITOR_PATCH_TYPE_EDID_EXTENTION_ERROR_CHECK_SUM, + MONITOR_PATCH_TYPE_TURN_OFF_DISPLAY_BEFORE_MODE_CHANGE, + MONITOR_PATCH_TYPE_RESTRICT_VESA_MODE_TIMING, + MONITOR_PATCH_TYPE_DO_NOT_USE_EDID_MAX_PIX_CLK, + MONITOR_PATCH_TYPE_VENDOR_0, + MONITOR_PATCH_TYPE_RANDOM_CRT, + MONITOR_PATCH_TYPE_VENDOR_1, + MONITOR_PATCH_TYPE_LIMIT_PANEL_SUPPORT_RGB_ONLY, + MONITOR_PATCH_TYPE_PACKED_PIXEL_FORMAT, + MONITOR_PATCH_TYPE_LARGE_PANEL, + MONITOR_PATCH_TYPE_STEREO_SUPPORT, + /* 0 (default) - mean patch will not be applied, however it can be + * explicitly set to 1 + */ + MONITOR_PATCH_TYPE_DUAL_EDID_PANEL, + MONITOR_PATCH_TYPE_IGNORE_19X12_STD_TIMING, + MONITOR_PATCH_TYPE_MULTIPLE_PACKED_TYPE, + MONITOR_PATCH_TYPE_RESET_TX_ON_DISPLAY_POWER_ON, + MONITOR_PATCH_TYPE_VENDOR_2, + MONITOR_PATCH_TYPE_RESTRICT_PROT_DUAL_LINK_DVI, + MONITOR_PATCH_TYPE_FORCE_LINK_RATE, + MONITOR_PATCH_TYPE_DELAY_AFTER_DP_RECEIVER_POWER_UP, + MONITOR_PATCH_TYPE_KEEP_DP_RECEIVER_POWERED, + MONITOR_PATCH_TYPE_DELAY_BEFORE_READ_EDID, + MONITOR_PATCH_TYPE_DELAY_AFTER_PIXEL_FORMAT_CHANGE, + MONITOR_PATCH_TYPE_INCREASE_DEFER_WRITE_RETRY_I2C_OVER_AUX, + MONITOR_PATCH_TYPE_NO_DEFAULT_TIMINGS, + MONITOR_PATCH_TYPE_ADD_CEA861_DETAILED_TIMING_VIC16, + MONITOR_PATCH_TYPE_ADD_CEA861_DETAILED_TIMING_VIC31, + MONITOR_PATCH_TYPE_DELAY_BEFORE_UNMUTE, + MONITOR_PATCH_TYPE_RETRY_LINK_TRAINING_ON_FAILURE, + MONITOR_PATCH_TYPE_ALLOW_AUX_WHEN_HPD_LOW, + MONITOR_PATCH_TYPE_TILED_DISPLAY, + MONITOR_PATCH_TYPE_DISABLE_PSR_ENTRY_ABORT, + MONITOR_PATCH_TYPE_EDID_EXTENTION_ERROR_CHECKSUM, + MONITOR_PATCH_TYPE_ALLOW_ONLY_CE_MODE, + MONITOR_PATCH_TYPE_VID_STREAM_DIFFER_TO_SYNC, + MONITOR_PATCH_TYPE_EXTRA_DELAY_ON_DISCONNECT, + MONITOR_PATCH_TYPE_DELAY_AFTER_DISABLE_BACKLIGHT_DFS_BYPASS, + MONITOR_PATCH_TYPE_SINGLE_MODE_PACKED_PIXEL +}; + +/* Single entry in the monitor table */ +struct monitor_patch_info { + enum monitor_manufacturer_id manufacturer_id; + enum monitor_product_id product_id; + enum monitor_patch_type type; + uint32_t param; +}; + +union dcs_monitor_patch_flags { + struct { + bool ERROR_CHECKSUM:1; + bool HDTV_WITH_PURE_DFP_EDID:1; + bool DO_NOT_USE_DETAILED_TIMING:1; + bool DO_NOT_USE_RANGE_LIMITATION:1; + bool EDID_EXTENTION_ERROR_CHECKSUM:1; + bool TURN_OFF_DISPLAY_BEFORE_MODE_CHANGE:1; + bool RESTRICT_VESA_MODE_TIMING:1; + bool DO_NOT_USE_EDID_MAX_PIX_CLK:1; + bool VENDOR_0:1; + bool RANDOM_CRT:1;/* 10 bits used including this one-*/ + bool VENDOR_1:1; + bool LIMIT_PANEL_SUPPORT_RGB_ONLY:1; + bool PACKED_PIXEL_FORMAT:1; + bool LARGE_PANEL:1; + bool STEREO_SUPPORT:1; + bool DUAL_EDID_PANEL:1; + bool IGNORE_19X12_STD_TIMING:1; + bool MULTIPLE_PACKED_TYPE:1; + bool RESET_TX_ON_DISPLAY_POWER_ON:1; + bool ALLOW_ONLY_CE_MODE:1;/* 20 bits used including this one*/ + bool RESTRICT_PROT_DUAL_LINK_DVI:1; + bool FORCE_LINK_RATE:1; + bool DELAY_AFTER_DP_RECEIVER_POWER_UP:1; + bool KEEP_DP_RECEIVER_POWERED:1; + bool DELAY_BEFORE_READ_EDID:1; + bool DELAY_AFTER_PIXEL_FORMAT_CHANGE:1; + bool INCREASE_DEFER_WRITE_RETRY_I2C_OVER_AUX:1; + bool NO_DEFAULT_TIMINGS:1; + bool ADD_CEA861_DETAILED_TIMING_VIC16:1; + bool ADD_CEA861_DETAILED_TIMING_VIC31:1; /* 30 bits*/ + bool DELAY_BEFORE_UNMUTE:1; + bool RETRY_LINK_TRAINING_ON_FAILURE:1; + bool ALLOW_AUX_WHEN_HPD_LOW:1; + bool TILED_DISPLAY:1; + bool DISABLE_PSR_ENTRY_ABORT:1; + bool INTERMITTENT_EDID_ERROR:1;/* 36 bits total*/ + bool VID_STREAM_DIFFER_TO_SYNC:1;/* 37 bits total*/ + bool EXTRA_DELAY_ON_DISCONNECT:1;/* 38 bits total*/ + bool DELAY_AFTER_DISABLE_BACKLIGHT_DFS_BYPASS:1;/* 39 bits total*/ + } flags; + uint64_t raw; +}; + +struct dcs_edid_supported_max_bw { + uint32_t pix_clock_khz; + uint32_t bits_per_pixel; +}; + +struct dcs_stereo_3d_features { + struct { +/* 3D Format supported by monitor (implies supported by driver)*/ + uint32_t SUPPORTED:1; +/* 3D Format supported on all timings +(no need to check every timing for 3D support)*/ + uint32_t ALL_TIMINGS:1; +/* 3D Format supported in clone mode*/ + uint32_t CLONE_MODE:1; +/* Scaling allowed when driving 3D Format*/ + uint32_t SCALING:1; +/* Left and right images packed by SW within single frame*/ + uint32_t SINGLE_FRAME_SW_PACKED:1; + } flags; +}; + +struct dcs_container_id { + /*128bit GUID in binary form*/ + uint8_t guid[16]; + /* 8 byte port ID -> ELD.PortID*/ + uint32_t port_id[2]; + /* 2 byte manufacturer name -> ELD.ManufacturerName*/ + uint16_t manufacturer_name; + /* 2 byte product code -> ELD.ProductCode*/ + uint16_t product_code; +}; + +struct dcs_display_tile { +/*unique Id of Tiled Display. 0 - means display is not part in Tiled Display*/ + uint64_t id; + uint32_t rows;/* size of Tiled Display in tiles*/ + uint32_t cols;/* size of Tiled Display in tiles*/ + uint32_t width;/* size of current Tile in pixels*/ + uint32_t height;/* size of current Tile in pixels*/ + uint32_t row;/* location of current Tile*/ + uint32_t col;/* location of current Tile*/ + struct { + /*in pixels*/ + uint32_t left; + uint32_t right; + uint32_t top; + uint32_t bottom; + } bezel;/* bezel information of current tile*/ + + struct { + uint32_t SEPARATE_ENCLOSURE:1; + uint32_t BEZEL_INFO_PRESENT:1; + uint32_t CAN_SCALE:1; + } flags; + + struct { + uint32_t manufacturer_id; + uint32_t product_id; + uint32_t serial_id; + } topology_id; +}; + +#endif /* __DAL_DCS_TYPES_H__ */ diff --git a/drivers/gpu/drm/amd/dal/include/ddc_service_interface.h b/drivers/gpu/drm/amd/dal/include/ddc_service_interface.h new file mode 100644 index 000000000000..f7805227bd9b --- /dev/null +++ b/drivers/gpu/drm/amd/dal/include/ddc_service_interface.h @@ -0,0 +1,97 @@ +/* + * Copyright 2012-15 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 + * + */ +#ifndef __DAL_DDC_SERVICE_INTERFACE_H__ +#define __DAL_DDC_SERVICE_INTERFACE_H__ + +#include "ddc_service_types.h" + +struct ddc_service; +struct adapter_service; +struct graphics_object_id; +enum ddc_result; +struct av_sync_data; +struct dp_receiver_id_info; + +struct ddc_service_init_data { + struct adapter_service *as; + struct graphics_object_id id; + struct dal_context *ctx; +}; +struct ddc_service *dal_ddc_service_create( + struct ddc_service_init_data *ddc_init_data); + +void dal_ddc_service_destroy(struct ddc_service **ddc); + +enum ddc_service_type dal_ddc_service_get_type(struct ddc_service *ddc); + +void dal_ddc_service_set_transaction_type( + struct ddc_service *ddc, + enum ddc_transaction_type type); + +bool dal_ddc_service_is_in_aux_transaction_mode(struct ddc_service *ddc); + +void dal_ddc_service_optimized_edid_query(struct ddc_service *ddc); + +uint32_t dal_ddc_service_get_edid_buf_len(struct ddc_service *ddc); + +const uint8_t *dal_ddc_service_get_edid_buf(struct ddc_service *ddc); + +bool dal_ddc_service_i2c_query_dp_dual_mode_adaptor( + struct ddc_service *ddc, + struct display_sink_capability *sink_cap); + +void dal_ddc_service_retrieve_dpcd_data( + struct ddc_service *ddc, + struct av_sync_data *av_sync_data); + +bool dal_ddc_service_aux_query_dp_sink_capability( + struct ddc_service *ddc, + struct display_sink_capability *sink_cap); + +bool dal_ddc_service_query_ddc_data( + struct ddc_service *ddc, + uint32_t address, + uint8_t *write_buf, + uint32_t write_size, + uint8_t *read_buf, + uint32_t read_size); + +bool dal_ddc_service_get_dp_receiver_id_info( + struct ddc_service *ddc, + struct dp_receiver_id_info *info); + +enum ddc_result dal_ddc_service_read_dpcd_data( + struct ddc_service *ddc, + uint32_t address, + uint8_t *data, + uint32_t len); + +enum ddc_result dal_ddc_service_write_dpcd_data( + struct ddc_service *ddc, + uint32_t address, + const uint8_t *data, + uint32_t len); + +#endif /* __DAL_DDC_SERVICE_INTERFACE_H__ */ diff --git a/drivers/gpu/drm/amd/dal/include/ddc_service_types.h b/drivers/gpu/drm/amd/dal/include/ddc_service_types.h new file mode 100644 index 000000000000..eba2a9b381c9 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/include/ddc_service_types.h @@ -0,0 +1,220 @@ +/* + * Copyright 2012-15 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 + * + */ +#ifndef __DAL_DDC_SERVICE_TYPES_H__ +#define __DAL_DDC_SERVICE_TYPES_H__ + +#include "include/hw_sequencer_types.h" + +#define DP_BRANCH_DEVICE_ID_1 0x0010FA +#define DP_BRANCH_DEVICE_ID_2 0x0022B9 +#define DP_SINK_DEVICE_ID_1 0x4CE000 +#define DP_BRANCH_DEVICE_ID_3 0x00001A +#define DP_BRANCH_DEVICE_ID_4 0x0080e1 +#define DP_BRANCH_DEVICE_ID_5 0x006037 +#define DP_SINK_DEVICE_ID_2 0x001CF8 + + +enum ddc_result { + DDC_RESULT_UNKNOWN = 0, + DDC_RESULT_SUCESSFULL, + DDC_RESULT_FAILED_CHANNEL_BUSY, + DDC_RESULT_FAILED_TIMEOUT, + DDC_RESULT_FAILED_PROTOCOL_ERROR, + DDC_RESULT_FAILED_NACK, + DDC_RESULT_FAILED_INCOMPLETE, + DDC_RESULT_FAILED_OPERATION, + DDC_RESULT_FAILED_INVALID_OPERATION, + DDC_RESULT_FAILED_BUFFER_OVERFLOW +}; + +enum ddc_service_type { + DDC_SERVICE_TYPE_CONNECTOR, + DDC_SERVICE_TYPE_DISPLAY_PORT_MST, +}; + +enum ddc_transaction_type { + DDC_TRANSACTION_TYPE_NONE = 0, + DDC_TRANSACTION_TYPE_I2C, + DDC_TRANSACTION_TYPE_I2C_OVER_AUX, + DDC_TRANSACTION_TYPE_I2C_OVER_AUX_WITH_DEFER, + DDC_TRANSACTION_TYPE_I2C_OVER_AUX_RETRY_DEFER +}; + +enum display_dongle_type { + DISPLAY_DONGLE_NONE = 0, + /* Active converter types*/ + DISPLAY_DONGLE_DP_VGA_CONVERTER, + DISPLAY_DONGLE_DP_DVI_CONVERTER, + DISPLAY_DONGLE_DP_HDMI_CONVERTER, + /* DP-HDMI/DVI passive dongles (Type 1 and Type 2)*/ + DISPLAY_DONGLE_DP_DVI_DONGLE, + DISPLAY_DONGLE_DP_HDMI_DONGLE, + /* Other types of dongle*/ + DISPLAY_DONGLE_DP_HDMI_MISMATCHED_DONGLE, +}; + +enum dcs_dpcd_revision { + DCS_DPCD_REV_10 = 0x10, + DCS_DPCD_REV_11 = 0x11, + DCS_DPCD_REV_12 = 0x12 +}; + +/** + * display sink capability + */ +struct display_sink_capability { + /* dongle type (DP converter, CV smart dongle) */ + enum display_dongle_type dongle_type; + + /********************************************************** + capabilities going INTO SINK DEVICE (stream capabilities) + **********************************************************/ + /* Dongle's downstream count. */ + uint32_t downstrm_sink_count; + /* Is dongle's downstream count info field (downstrm_sink_count) + * valid. */ + bool downstrm_sink_count_valid; + + /* Maximum additional audio delay in microsecond (us) */ + uint32_t additional_audio_delay; + /* Audio latency value in microsecond (us) */ + uint32_t audio_latency; + /* Interlace video latency value in microsecond (us) */ + uint32_t video_latency_interlace; + /* Progressive video latency value in microsecond (us) */ + uint32_t video_latency_progressive; + /* Dongle caps: Maximum pixel clock supported over dongle for HDMI */ + uint32_t max_hdmi_pixel_clock; + /* Dongle caps: Maximum deep color supported over dongle for HDMI */ + enum hw_color_depth max_hdmi_deep_color; + + /************************************************************ + capabilities going OUT OF SOURCE DEVICE (link capabilities) + ************************************************************/ + /* support for Spread Spectrum(SS) */ + bool ss_supported; + /* DP link settings (laneCount, linkRate, Spread) */ + uint32_t dp_link_lane_count; + uint32_t dp_link_rate; + uint32_t dp_link_spead; + + enum dcs_dpcd_revision dpcd_revision; + /* If dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER, + indicates 'Frame Sequential-to-lllFrame Pack' conversion capability.*/ + bool is_dp_hdmi_s3d_converter; + /* to check if we have queried the display capability + * for eDP panel already. */ + bool is_edp_sink_cap_valid; +}; + +struct dp_receiver_id_info { + uint32_t dpcd_rev; + uint32_t sink_id; + int8_t sink_id_str[6]; + int8_t sink_hw_revision; + int8_t sink_fw_revision[2]; + uint32_t branch_id; + int8_t branch_name[6]; + enum display_dongle_type dongle_type; +}; + +struct av_sync_data { + uint8_t av_granularity;/* DPCD 00023h */ + uint8_t aud_dec_lat1;/* DPCD 00024h */ + uint8_t aud_dec_lat2;/* DPCD 00025h */ + uint8_t aud_pp_lat1;/* DPCD 00026h */ + uint8_t aud_pp_lat2;/* DPCD 00027h */ + uint8_t vid_inter_lat;/* DPCD 00028h */ + uint8_t vid_prog_lat;/* DPCD 00029h */ + uint8_t aud_del_ins1;/* DPCD 0002Bh */ + uint8_t aud_del_ins2;/* DPCD 0002Ch */ + uint8_t aud_del_ins3;/* DPCD 0002Dh */ +}; + +/** EDID retrieval related constants, also used by MstMgr **/ + +#define DDC_EDID_SEGMENT_SIZE 256 +#define DDC_EDID_BLOCK_SIZE 128 +#define DDC_EDID_BLOCKS_PER_SEGMENT \ + (DDC_EDID_SEGMENT_SIZE / DDC_EDID_BLOCK_SIZE) + +#define DDC_EDID_EXT_COUNT_OFFSET 0x7E + +#define DDC_EDID_ADDRESS_START 0x50 +#define DDC_EDID_ADDRESS_END 0x52 +#define DDC_EDID_SEGMENT_ADDRESS 0x30 + +/* signatures for Edid 1x */ +#define DDC_EDID1X_VENDORID_SIGNATURE_OFFSET 8 +#define DDC_EDID1X_VENDORID_SIGNATURE_LEN 4 +#define DDC_EDID1X_EXT_CNT_AND_CHECKSUM_OFFSET 126 +#define DDC_EDID1X_EXT_CNT_AND_CHECKSUM_LEN 2 +#define DDC_EDID1X_CHECKSUM_OFFSET 127 +/* signatures for Edid 20*/ +#define DDC_EDID_20_SIGNATURE_OFFSET 0 +#define DDC_EDID_20_SIGNATURE 0x20 + +#define DDC_EDID20_VENDORID_SIGNATURE_OFFSET 1 +#define DDC_EDID20_VENDORID_SIGNATURE_LEN 4 +#define DDC_EDID20_CHECKSUM_OFFSET 255 +#define DDC_EDID20_CHECKSUM_LEN 1 + +/*DP to VGA converter*/ +static const uint8_t DP_VGA_CONVERTER_ID_1[] = "mVGAa"; +/*DP to Dual link DVI converter*/ +static const uint8_t DP_DVI_CONVERTER_ID_1[] = "m2DVIa"; +/*Travis*/ +static const uint8_t DP_VGA_LVDS_CONVERTER_ID_2[] = "sivarT"; +/*Nutmeg*/ +static const uint8_t DP_VGA_LVDS_CONVERTER_ID_3[] = "dnomlA"; +/*DP to VGA converter*/ +static const uint8_t DP_VGA_CONVERTER_ID_4[] = "DpVga"; +/*DP to Dual link DVI converter*/ +static const uint8_t DP_DVI_CONVERTER_ID_4[] = "m2DVIa"; +/*DP to Dual link DVI converter 2*/ +static const uint8_t DP_DVI_CONVERTER_ID_42[] = "v2DVIa"; + +static const uint8_t DP_SINK_DEV_STRING_ID2_REV0[] = "\0\0\0\0\0\0"; + +/* Identifies second generation PSR TCON from Parade: Device ID string: + * yy-xx-**-**-**-** + */ +/* xx - Hw ID high byte */ +static const uint32_t DP_SINK_DEV_STRING_ID2_REV1_HW_ID_HIGH_BYTE = + 0x06; + +/* yy - HW ID low byte, the same silicon has several package/feature flavors */ +static const uint32_t DP_SINK_DEV_STRING_ID2_REV1_HW_ID_LOW_BYTE1 = + 0x61; +static const uint32_t DP_SINK_DEV_STRING_ID2_REV1_HW_ID_LOW_BYTE2 = + 0x62; +static const uint32_t DP_SINK_DEV_STRING_ID2_REV1_HW_ID_LOW_BYTE3 = + 0x63; +static const uint32_t DP_SINK_DEV_STRING_ID2_REV1_HW_ID_LOW_BYTE4 = + 0x72; +static const uint32_t DP_SINK_DEV_STRING_ID2_REV1_HW_ID_LOW_BYTE5 = + 0x73; + +#endif /* __DAL_DDC_SERVICE_TYPES_H__ */ diff --git a/drivers/gpu/drm/amd/dal/include/default_mode_list_interface.h b/drivers/gpu/drm/amd/dal/include/default_mode_list_interface.h new file mode 100644 index 000000000000..9dcd0353dac1 --- /dev/null +++ b/drivers/gpu/drm/amd/dal/include/default_mode_list_interface.h @@ -0,0 +1,37 @@ +/* + * Copyright 2012-15 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 + * + */ + +#ifndef __DAL_DEFAULT_MODE_LIST_INTERFACE_H__ +#define __DAL_DEFAULT_MODE_LIST_INTERFACE_H__ + +struct default_mode_list; + +uint32_t dal_default_mode_list_get_count(const struct default_mode_list *dml); + +struct mode_info *dal_default_mode_list_get_mode_info_at_index( + const struct default_mode_list *dml, + uint32_t index); + +#endif /*__DAL_DEFAULT_MODE_LIST_INTERFACE_H__*/ |