diff options
author | Harry Wentland <harry.wentland@amd.com> | 2015-07-31 21:21:08 -0400 |
---|---|---|
committer | Alex Deucher <alexander.deucher@amd.com> | 2015-09-21 17:45:15 -0400 |
commit | 7be25cce3466809be0b38e62ed6105281cfd7445 (patch) | |
tree | f32533c7af1dea2a10c6f92135d485f4c273ef4a | |
parent | c3bee645b6c3858d755618b8ee709bf059a6331c (diff) |
amd/dal: Display Capability Service
Reads, parses, caches an enumerates display (sink) capabilities. Uses
I2cAux component to read capabilities from the receiver. While parsing
the EDID, DCS will call Timing Service to get timing parameters for
DMT and CVT EDID timings.
SW Layer
/===============================================================\
| Timing Asic |
| Service Capability |
| |
| Display Adapter |
| Capability Service |
| Service |
|---------------------------------------------------------------|
| GPIO IRQ I2cAux BIOS |
| Service Manager Parser |
| |
| Connector Encoder Audio GPU Controller |
\===============================================================/
HW Layer
Signed-off-by: Harry Wentland <harry.wentland@amd.com>
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__*/ |