summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/gpu/drm/amd/dal/Makefile2
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/Makefile16
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/dcs.c3142
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/dcs_list.c180
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/ddc_i2caux_helper.c158
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/ddc_i2caux_helper.h61
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/ddc_service.c1338
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/ddc_service.h38
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/default_modes_dco.c181
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/default_modes_dco.h44
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/edid/edid.c66
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/edid/edid.h35
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/edid/edid13.c858
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/edid/edid13.h117
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/edid/edid14.c842
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/edid/edid14.h45
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/edid/edid1x_data.h167
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/edid/edid20.c637
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/edid/edid20.h69
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/edid/edid_base.c817
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/edid/edid_base.h480
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/edid/edid_ext_cea.c1758
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/edid/edid_ext_cea.h41
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/edid/edid_ext_di.c327
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/edid/edid_ext_di.h37
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/edid/edid_ext_unknown.c135
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/edid/edid_ext_unknown.h36
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/edid/edid_ext_vtb.c439
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/edid/edid_ext_vtb.h38
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/edid_mgr.c488
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/edid_mgr.h98
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/edid_patch.c920
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/edid_patch.h83
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/hdtv_dco.c32
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/hdtv_dco.h49
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/monitor_tables.c288
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/monitor_tables.h34
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/remote_display_receiver_modes.c555
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/remote_display_receiver_modes.h62
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/vbios_dco.c327
-rw-r--r--drivers/gpu/drm/amd/dal/dcs/vbios_dco.h77
-rw-r--r--drivers/gpu/drm/amd/dal/include/dcs_interface.h376
-rw-r--r--drivers/gpu/drm/amd/dal/include/dcs_types.h752
-rw-r--r--drivers/gpu/drm/amd/dal/include/ddc_service_interface.h97
-rw-r--r--drivers/gpu/drm/amd/dal/include/ddc_service_types.h220
-rw-r--r--drivers/gpu/drm/amd/dal/include/default_mode_list_interface.h37
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__*/