aboutsummaryrefslogtreecommitdiffstats
path: root/common/recipes-kernel/linux/files/0511-drm-amd-dal-Adding-amdgpu_dm-for-dal-v2.patch
diff options
context:
space:
mode:
Diffstat (limited to 'common/recipes-kernel/linux/files/0511-drm-amd-dal-Adding-amdgpu_dm-for-dal-v2.patch')
-rw-r--r--common/recipes-kernel/linux/files/0511-drm-amd-dal-Adding-amdgpu_dm-for-dal-v2.patch6016
1 files changed, 6016 insertions, 0 deletions
diff --git a/common/recipes-kernel/linux/files/0511-drm-amd-dal-Adding-amdgpu_dm-for-dal-v2.patch b/common/recipes-kernel/linux/files/0511-drm-amd-dal-Adding-amdgpu_dm-for-dal-v2.patch
new file mode 100644
index 00000000..c709f6e8
--- /dev/null
+++ b/common/recipes-kernel/linux/files/0511-drm-amd-dal-Adding-amdgpu_dm-for-dal-v2.patch
@@ -0,0 +1,6016 @@
+From a04ef2511da8e6d563253dd70f193979ca0ebb81 Mon Sep 17 00:00:00 2001
+From: Harry Wentland <harry.wentland@amd.com>
+Date: Wed, 25 Nov 2015 14:48:16 -0500
+Subject: [PATCH 0511/1110] drm/amd/dal: Adding amdgpu_dm for dal (v2)
+
+v2: agd: fix for API changes in kernel 4.6
+
+Signed-off-by: Harry Wentland <harry.wentland@amd.com>
+Acked-by: Alex Deucher <alexander.deucher@amd.com>
+---
+ drivers/gpu/drm/amd/dal/amdgpu_dm/Makefile | 17 +
+ .../drm/amd/dal/amdgpu_dm/amdgpu_dal_services.c | 251 ++
+ .../gpu/drm/amd/dal/amdgpu_dm/amdgpu_dc_helpers.c | 350 +++
+ drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm.c | 1318 +++++++++++
+ drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm.h | 166 ++
+ drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_irq.c | 814 +++++++
+ drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_irq.h | 122 +
+ .../drm/amd/dal/amdgpu_dm/amdgpu_dm_mst_types.c | 353 +++
+ .../drm/amd/dal/amdgpu_dm/amdgpu_dm_mst_types.h | 36 +
+ .../gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_types.c | 2390 ++++++++++++++++++++
+ .../gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_types.h | 96 +
+ 11 files changed, 5913 insertions(+)
+ create mode 100644 drivers/gpu/drm/amd/dal/amdgpu_dm/Makefile
+ create mode 100644 drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dal_services.c
+ create mode 100644 drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dc_helpers.c
+ create mode 100644 drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm.c
+ create mode 100644 drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm.h
+ create mode 100644 drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_irq.c
+ create mode 100644 drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_irq.h
+ create mode 100644 drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_mst_types.c
+ create mode 100644 drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_mst_types.h
+ create mode 100644 drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_types.c
+ create mode 100644 drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_types.h
+
+diff --git a/drivers/gpu/drm/amd/dal/amdgpu_dm/Makefile b/drivers/gpu/drm/amd/dal/amdgpu_dm/Makefile
+new file mode 100644
+index 0000000..65ad370
+--- /dev/null
++++ b/drivers/gpu/drm/amd/dal/amdgpu_dm/Makefile
+@@ -0,0 +1,17 @@
++#
++# Makefile for the 'dm' sub-component of DAL.
++# It provides the control and status of dm blocks.
++
++
++
++AMDGPUDM = amdgpu_dm_types.o amdgpu_dm.o amdgpu_dm_irq.o amdgpu_dm_mst_types.o
++
++ifneq ($(CONFIG_DRM_AMD_DAL),)
++AMDGPUDM += amdgpu_dal_services.o amdgpu_dc_helpers.o
++endif
++
++subdir-ccflags-y += -I$(FULL_AMD_DAL_PATH)/dc
++
++AMDGPU_DM = $(addprefix $(AMDDALPATH)/amdgpu_dm/,$(AMDGPUDM))
++
++AMD_DAL_FILES += $(AMDGPU_DM)
+diff --git a/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dal_services.c b/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dal_services.c
+new file mode 100644
+index 0000000..a497093
+--- /dev/null
++++ b/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dal_services.c
+@@ -0,0 +1,251 @@
++/*
++ * Copyright 2015 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 <linux/string.h>
++#include <linux/acpi.h>
++
++#include <drm/drmP.h>
++#include <drm/drm_crtc_helper.h>
++#include <drm/amdgpu_drm.h>
++
++#include "amdgpu.h"
++#include "dal_services.h"
++#include "amdgpu_dm.h"
++#include "amdgpu_dm_irq.h"
++#include "amdgpu_dm_types.h"
++#include "amdgpu_pm.h"
++
++/*
++#include "logger_interface.h"
++#include "acpimethod_atif.h"
++#include "amdgpu_powerplay.h"
++#include "amdgpu_notifications.h"
++*/
++
++/* if the pointer is not NULL, the allocated memory is zeroed */
++void *dc_service_alloc(struct dc_context *ctx, uint32_t size)
++{
++ return kzalloc(size, GFP_KERNEL);
++}
++
++/* Reallocate memory. The contents will remain unchanged.*/
++void *dc_service_realloc(struct dc_context *ctx, const void *ptr, uint32_t size)
++{
++ return krealloc(ptr, size, GFP_KERNEL);
++}
++
++void dc_service_memmove(void *dst, const void *src, uint32_t size)
++{
++ memmove(dst, src, size);
++}
++
++void dc_service_free(struct dc_context *ctx, void *p)
++{
++ kfree(p);
++}
++
++void dc_service_memset(void *p, int32_t c, uint32_t count)
++{
++ memset(p, c, count);
++}
++
++int32_t dal_memcmp(const void *p1, const void *p2, uint32_t count)
++{
++ return memcmp(p1, p2, count);
++}
++
++int32_t dal_strncmp(const int8_t *p1, const int8_t *p2, uint32_t count)
++{
++ return strncmp(p1, p2, count);
++}
++
++void dc_service_sleep_in_milliseconds(struct dc_context *ctx, uint32_t milliseconds)
++{
++ if (milliseconds >= 20)
++ msleep(milliseconds);
++ else
++ usleep_range(milliseconds*1000, milliseconds*1000+1);
++}
++
++void dal_delay_in_nanoseconds(uint32_t nanoseconds)
++{
++ ndelay(nanoseconds);
++}
++
++void dc_service_delay_in_microseconds(struct dc_context *ctx, uint32_t microseconds)
++{
++ udelay(microseconds);
++}
++
++/******************************************************************************
++ * IRQ Interfaces.
++ *****************************************************************************/
++
++void dal_register_timer_interrupt(
++ struct dc_context *ctx,
++ struct dc_timer_interrupt_params *int_params,
++ interrupt_handler ih,
++ void *args)
++{
++ struct amdgpu_device *adev = ctx->driver_context;
++
++ if (!adev || !int_params) {
++ DRM_ERROR("DM_IRQ: invalid input!\n");
++ return;
++ }
++
++ if (int_params->int_context != INTERRUPT_LOW_IRQ_CONTEXT) {
++ /* only low irq ctx is supported. */
++ DRM_ERROR("DM_IRQ: invalid context: %d!\n",
++ int_params->int_context);
++ return;
++ }
++
++ amdgpu_dm_irq_register_timer(adev, int_params, ih, args);
++}
++
++void dal_isr_acquire_lock(struct dc_context *ctx)
++{
++ /*TODO*/
++}
++
++void dal_isr_release_lock(struct dc_context *ctx)
++{
++ /*TODO*/
++}
++
++/******************************************************************************
++ * End-of-IRQ Interfaces.
++ *****************************************************************************/
++
++bool dal_get_platform_info(struct dc_context *ctx,
++ struct platform_info_params *params)
++{
++ /*TODO*/
++ return false;
++}
++
++/* Next calls are to power component */
++bool dc_service_pp_pre_dce_clock_change(struct dc_context *ctx,
++ struct dal_to_power_info *input,
++ struct power_to_dal_info *output)
++{
++ /*TODO*/
++ return false;
++}
++
++bool dc_service_pp_post_dce_clock_change(struct dc_context *ctx,
++ const struct dc_pp_display_configuration *pp_display_cfg)
++{
++#ifdef CONFIG_DRM_AMD_POWERPLAY
++ struct amdgpu_device *adev = ctx->driver_context;
++
++ if (adev->pm.dpm_enabled) {
++
++ memset(&adev->pm.pm_display_cfg, 0,
++ sizeof(adev->pm.pm_display_cfg));
++
++ adev->pm.pm_display_cfg.cpu_cc6_disable =
++ pp_display_cfg->cpu_cc6_disable;
++
++ adev->pm.pm_display_cfg.cpu_pstate_disable =
++ pp_display_cfg->cpu_pstate_disable;
++
++ adev->pm.pm_display_cfg.cpu_pstate_separation_time =
++ pp_display_cfg->cpu_pstate_separation_time;
++
++ adev->pm.pm_display_cfg.nb_pstate_switch_disable =
++ pp_display_cfg->nb_pstate_switch_disable;
++
++ amd_powerplay_display_configuration_change(
++ adev->powerplay.pp_handle,
++ &adev->pm.pm_display_cfg);
++
++ /* TODO: replace by a separate call to 'apply display cfg'? */
++ amdgpu_pm_compute_clocks(adev);
++ }
++ return true;
++#else
++ return false;
++#endif
++}
++
++bool dc_service_get_system_clocks_range(struct dc_context *ctx,
++ struct dal_system_clock_range *sys_clks)
++{
++ struct amdgpu_device *adev = ctx->driver_context;
++
++ /* Default values, in case PPLib is not compiled-in. */
++ sys_clks->max_mclk = 80000;
++ sys_clks->min_mclk = 80000;
++
++ sys_clks->max_sclk = 60000;
++ sys_clks->min_sclk = 30000;
++
++#ifdef CONFIG_DRM_AMD_POWERPLAY
++ if (adev->pm.dpm_enabled) {
++ sys_clks->max_mclk = amdgpu_dpm_get_mclk(adev, false);
++ sys_clks->min_mclk = amdgpu_dpm_get_mclk(adev, true);
++
++ sys_clks->max_sclk = amdgpu_dpm_get_sclk(adev, false);
++ sys_clks->min_sclk = amdgpu_dpm_get_sclk(adev, true);
++ }
++#endif
++
++ return true;
++}
++
++
++bool dc_service_pp_set_display_clock(struct dc_context *ctx,
++ struct dal_to_power_dclk *dclk)
++{
++ /* TODO: need power component to provide appropriate interface */
++ return false;
++}
++
++/* end of calls to power component */
++
++/* Calls to notification */
++
++void dal_notify_setmode_complete(struct dc_context *ctx,
++ uint32_t h_total,
++ uint32_t v_total,
++ uint32_t h_active,
++ uint32_t v_active,
++ uint32_t pix_clk_in_khz)
++{
++ /*TODO*/
++}
++/* End of calls to notification */
++
++long dal_get_pid(void)
++{
++ return current->pid;
++}
++
++long dal_get_tgid(void)
++{
++ return current->tgid;
++}
+diff --git a/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dc_helpers.c b/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dc_helpers.c
+new file mode 100644
+index 0000000..beaef70
+--- /dev/null
++++ b/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dc_helpers.c
+@@ -0,0 +1,350 @@
++/*
++ * Copyright 2015 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 <linux/string.h>
++#include <linux/acpi.h>
++
++#include <drm/drmP.h>
++#include <drm/drm_crtc_helper.h>
++#include <drm/amdgpu_drm.h>
++#include <drm/drm_edid.h>
++
++#include "dc_types.h"
++#include "amdgpu.h"
++#include "dc.h"
++#include "dc_services.h"
++
++#include "amdgpu_dm.h"
++#include "amdgpu_dm_irq.h"
++#include "amdgpu_dm_types.h"
++
++/* dc_helpers_parse_edid_caps
++ *
++ * Parse edid caps
++ *
++ * @edid: [in] pointer to edid
++ * edid_caps: [in] pointer to edid caps
++ * @return
++ * void
++ * */
++enum dc_edid_status dc_helpers_parse_edid_caps(
++ struct dc_context *ctx,
++ const struct dc_edid *edid,
++ struct dc_edid_caps *edid_caps)
++{
++ struct edid *edid_buf = (struct edid *) edid->raw_edid;
++ struct cea_sad *sads;
++ int sad_count = -1;
++ int sadb_count = -1;
++ int i = 0;
++ int j = 0;
++ uint8_t *sadb = NULL;
++
++ enum dc_edid_status result = EDID_OK;
++
++ if (!edid_caps || !edid)
++ return EDID_BAD_INPUT;
++
++ if (!drm_edid_is_valid(edid_buf))
++ result = EDID_BAD_CHECKSUM;
++
++ edid_caps->manufacturer_id = (uint16_t) edid_buf->mfg_id[0] |
++ ((uint16_t) edid_buf->mfg_id[1])<<8;
++ edid_caps->product_id = (uint16_t) edid_buf->prod_code[0] |
++ ((uint16_t) edid_buf->prod_code[1])<<8;
++ edid_caps->serial_number = edid_buf->serial;
++ edid_caps->manufacture_week = edid_buf->mfg_week;
++ edid_caps->manufacture_year = edid_buf->mfg_year;
++
++ /* One of the four detailed_timings stores the monitor name. It's
++ * stored in an array of length 13. */
++ for (i = 0; i < 4; i++) {
++ if (edid_buf->detailed_timings[i].data.other_data.type == 0xfc) {
++ while (edid_buf->detailed_timings[i].data.other_data.data.str.str[j] && j < 13) {
++ if (edid_buf->detailed_timings[i].data.other_data.data.str.str[j] == '\n')
++ break;
++
++ edid_caps->display_name[j] =
++ edid_buf->detailed_timings[i].data.other_data.data.str.str[j];
++ j++;
++ }
++ }
++ }
++
++ sad_count = drm_edid_to_sad((struct edid *) edid->raw_edid, &sads);
++ if (sad_count <= 0) {
++ DRM_INFO("SADs count is: %d, don't need to read it\n",
++ sad_count);
++ return result;
++ }
++
++ edid_caps->audio_mode_count = sad_count < DC_MAX_AUDIO_DESC_COUNT ? sad_count : DC_MAX_AUDIO_DESC_COUNT;
++ for (i = 0; i < edid_caps->audio_mode_count; ++i) {
++ struct cea_sad *sad = &sads[i];
++
++ edid_caps->audio_modes[i].format_code = sad->format;
++ edid_caps->audio_modes[i].channel_count = sad->channels;
++ edid_caps->audio_modes[i].sample_rate = sad->freq;
++ edid_caps->audio_modes[i].sample_size = sad->byte2;
++ }
++
++ sadb_count = drm_edid_to_speaker_allocation((struct edid *) edid->raw_edid, &sadb);
++
++ if (sadb_count < 0) {
++ DRM_ERROR("Couldn't read Speaker Allocation Data Block: %d\n", sadb_count);
++ sadb_count = 0;
++ }
++
++ if (sadb_count)
++ edid_caps->speaker_flags = sadb[0];
++ else
++ edid_caps->speaker_flags = DEFAULT_SPEAKER_LOCATION;
++
++ kfree(sads);
++ kfree(sadb);
++
++ return result;
++}
++
++
++static struct amdgpu_connector *get_connector_for_sink(
++ struct drm_device *dev,
++ const struct dc_sink *sink)
++{
++ struct drm_connector *connector;
++ struct amdgpu_connector *aconnector = NULL;
++
++ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
++ aconnector = to_amdgpu_connector(connector);
++ if (aconnector->dc_sink == sink)
++ break;
++ }
++
++ return aconnector;
++}
++
++static struct amdgpu_connector *get_connector_for_link(
++ struct drm_device *dev,
++ const struct dc_link *link)
++{
++ struct drm_connector *connector;
++ struct amdgpu_connector *aconnector = NULL;
++
++ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
++ aconnector = to_amdgpu_connector(connector);
++ if (aconnector->dc_link == link)
++ break;
++ }
++
++ return aconnector;
++}
++
++/*
++ * Writes payload allocation table in immediate downstream device.
++ */
++bool dc_helpers_dp_mst_write_payload_allocation_table(
++ struct dc_context *ctx,
++ const struct dc_sink *sink,
++ struct dp_mst_stream_allocation *alloc_entity,
++ bool enable)
++{
++ struct amdgpu_device *adev = ctx->driver_context;
++ struct drm_device *dev = adev->ddev;
++ struct amdgpu_connector *aconnector;
++ struct drm_crtc *crtc;
++ struct drm_dp_mst_topology_mgr *mst_mgr;
++ struct drm_dp_mst_port *mst_port;
++ int slots = 0;
++ bool ret;
++ int clock;
++ int bpp;
++ int pbn = 0;
++
++ aconnector = get_connector_for_sink(dev, sink);
++ crtc = aconnector->base.state->crtc;
++
++ if (!aconnector->mst_port)
++ return false;
++
++ mst_mgr = &aconnector->mst_port->mst_mgr;
++ mst_port = aconnector->port;
++
++ if (enable) {
++ clock = crtc->state->mode.clock;
++ /* TODO remove following hardcode value */
++ bpp = 30;
++
++ /* TODO need to know link rate */
++
++ pbn = drm_dp_calc_pbn_mode(clock, bpp);
++
++ ret = drm_dp_mst_allocate_vcpi(mst_mgr, mst_port, pbn, &slots);
++
++ if (!ret)
++ return false;
++
++ } else {
++ drm_dp_mst_reset_vcpi_slots(mst_mgr, mst_port);
++ }
++
++ alloc_entity->slot_count = slots;
++ alloc_entity->pbn = pbn;
++ alloc_entity->pbn_per_slot = mst_mgr->pbn_div;
++
++ ret = drm_dp_update_payload_part1(mst_mgr);
++ if (ret)
++ return false;
++
++ return true;
++}
++
++/*
++ * Polls for ACT (allocation change trigger) handled and sends
++ * ALLOCATE_PAYLOAD message.
++ */
++bool dc_helpers_dp_mst_poll_for_allocation_change_trigger(
++ struct dc_context *ctx,
++ const struct dc_sink *sink)
++{
++ struct amdgpu_device *adev = ctx->driver_context;
++ struct drm_device *dev = adev->ddev;
++ struct amdgpu_connector *aconnector;
++ struct drm_dp_mst_topology_mgr *mst_mgr;
++ int ret;
++
++ aconnector = get_connector_for_sink(dev, sink);
++
++ if (!aconnector->mst_port)
++ return false;
++
++ mst_mgr = &aconnector->mst_port->mst_mgr;
++
++ ret = drm_dp_check_act_status(mst_mgr);
++
++ if (ret)
++ return false;
++
++ return true;
++}
++
++bool dc_helpers_dp_mst_send_payload_allocation(
++ struct dc_context *ctx,
++ const struct dc_sink *sink,
++ bool enable)
++{
++ struct amdgpu_device *adev = ctx->driver_context;
++ struct drm_device *dev = adev->ddev;
++ struct amdgpu_connector *aconnector;
++ struct drm_dp_mst_topology_mgr *mst_mgr;
++ struct drm_dp_mst_port *mst_port;
++ int ret;
++
++ aconnector = get_connector_for_sink(dev, sink);
++
++ mst_port = aconnector->port;
++
++ if (!aconnector->mst_port)
++ return false;
++
++ mst_mgr = &aconnector->mst_port->mst_mgr;
++
++ ret = drm_dp_update_payload_part2(mst_mgr);
++
++ if (ret)
++ return false;
++
++ if (!enable)
++ drm_dp_mst_deallocate_vcpi(mst_mgr, mst_port);
++
++ return true;
++}
++
++void dc_helpers_dp_mst_handle_mst_hpd_rx_irq(void *param)
++{
++ uint8_t esi[8] = { 0 };
++ uint8_t dret;
++ bool new_irq_handled = true;
++ struct amdgpu_connector *aconnector = (struct amdgpu_connector *)param;
++
++ /* DPCD 0x2002 - 0x2008 for down stream IRQ from MST, eDP etc. */
++ dret = drm_dp_dpcd_read(
++ &aconnector->dm_dp_aux.aux,
++ DP_SINK_COUNT_ESI, esi, 8);
++
++ while ((dret == 8) && new_irq_handled) {
++ uint8_t retry;
++
++ DRM_DEBUG_KMS("ESI %02x %02x %02x\n", esi[0], esi[1], esi[2]);
++
++ /* handle HPD short pulse irq */
++ drm_dp_mst_hpd_irq(&aconnector->mst_mgr, esi, &new_irq_handled);
++
++ if (new_irq_handled) {
++ /* ACK at DPCD to notify down stream */
++ for (retry = 0; retry < 3; retry++) {
++ uint8_t wret;
++
++ wret = drm_dp_dpcd_write(
++ &aconnector->dm_dp_aux.aux,
++ DP_SINK_COUNT_ESI + 1,
++ &esi[1],
++ 3);
++ if (wret == 3)
++ break;
++ }
++
++ /* check if there is new irq to be handle */
++ dret = drm_dp_dpcd_read(
++ &aconnector->dm_dp_aux.aux,
++ DP_SINK_COUNT_ESI, esi, 8);
++ }
++ }
++}
++
++bool dc_helpers_dp_mst_start_top_mgr(
++ struct dc_context *ctx,
++ const struct dc_link *link)
++{
++ struct amdgpu_device *adev = ctx->driver_context;
++ struct drm_device *dev = adev->ddev;
++ struct amdgpu_connector *aconnector = get_connector_for_link(dev, link);
++
++ if (aconnector)
++ drm_dp_mst_topology_mgr_set_mst(&aconnector->mst_mgr, true);
++
++ return true;
++}
++
++void dc_helpers_dp_mst_stop_top_mgr(
++ struct dc_context *ctx,
++ const struct dc_link *link)
++{
++ struct amdgpu_device *adev = ctx->driver_context;
++ struct drm_device *dev = adev->ddev;
++ struct amdgpu_connector *aconnector = get_connector_for_link(dev, link);
++
++ if (aconnector)
++ drm_dp_mst_topology_mgr_set_mst(&aconnector->mst_mgr, false);
++}
+diff --git a/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm.c
+new file mode 100644
+index 0000000..37810ff
+--- /dev/null
++++ b/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm.c
+@@ -0,0 +1,1318 @@
++/*
++ * Copyright 2015 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_types.h"
++#include "dc.h"
++
++#include "vid.h"
++#include "amdgpu.h"
++#include "atom.h"
++#include "amdgpu_dm.h"
++#include "amdgpu_dm_types.h"
++
++#include "amd_shared.h"
++#include "amdgpu_dm_irq.h"
++#include "dc_helpers.h"
++
++#include "dce/dce_11_0_d.h"
++#include "dce/dce_11_0_sh_mask.h"
++#include "dce/dce_11_0_enum.h"
++#include "ivsrcid/ivsrcid_vislands30.h"
++
++#include "oss/oss_3_0_d.h"
++#include "oss/oss_3_0_sh_mask.h"
++#include "gmc/gmc_8_1_d.h"
++#include "gmc/gmc_8_1_sh_mask.h"
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++
++#include <drm/drm_atomic.h>
++#include <drm/drm_atomic_helper.h>
++#include <drm/drm_dp_mst_helper.h>
++
++/* TODO: Remove when mc access work around is removed */
++static const u32 crtc_offsets[] =
++{
++ CRTC0_REGISTER_OFFSET,
++ CRTC1_REGISTER_OFFSET,
++ CRTC2_REGISTER_OFFSET,
++ CRTC3_REGISTER_OFFSET,
++ CRTC4_REGISTER_OFFSET,
++ CRTC5_REGISTER_OFFSET,
++ CRTC6_REGISTER_OFFSET
++};
++/* TODO: End of when Remove mc access work around is removed */
++
++/* Define variables here
++ * These values will be passed to DAL for feature enable purpose
++ * Disable ALL for HDMI light up
++ * TODO: follow up if need this mechanism*/
++struct dal_override_parameters display_param = {
++ .bool_param_enable_mask = 0,
++ .bool_param_values = 0,
++ .int_param_values[DAL_PARAM_MAX_COFUNC_NON_DP_DISPLAYS] = DAL_PARAM_INVALID_INT,
++ .int_param_values[DAL_PARAM_DRR_SUPPORT] = DAL_PARAM_INVALID_INT,
++};
++
++/* Debug facilities */
++#define AMDGPU_DM_NOT_IMPL(fmt, ...) \
++ DRM_INFO("DM_NOT_IMPL: " fmt, ##__VA_ARGS__)
++
++/*
++ * dm_vblank_get_counter
++ *
++ * @brief
++ * Get counter for number of vertical blanks
++ *
++ * @param
++ * struct amdgpu_device *adev - [in] desired amdgpu device
++ * int disp_idx - [in] which CRTC to get the counter from
++ *
++ * @return
++ * Counter for vertical blanks
++ */
++static u32 dm_vblank_get_counter(struct amdgpu_device *adev, int crtc)
++{
++ if (crtc >= adev->mode_info.num_crtc)
++ return 0;
++ else {
++ struct amdgpu_crtc *acrtc = adev->mode_info.crtcs[crtc];
++
++ if (NULL == acrtc->target) {
++ DRM_ERROR("dc_target is NULL for crtc '%d'!\n", crtc);
++ return 0;
++ }
++
++ return dc_target_get_vblank_counter(acrtc->target);
++ }
++}
++
++static int dm_crtc_get_scanoutpos(struct amdgpu_device *adev, int crtc,
++ u32 *vbl, u32 *position)
++{
++ if ((crtc < 0) || (crtc >= adev->mode_info.num_crtc))
++ return -EINVAL;
++
++/* TODO: #DAL3 Implement scanoutpos
++ dal_get_crtc_scanoutpos(adev->dm.dal, crtc, vbl, position);
++*/
++ return 0;
++}
++
++static u32 dm_hpd_get_gpio_reg(struct amdgpu_device *adev)
++{
++ return mmDC_GPIO_HPD_A;
++}
++
++
++static bool dm_is_display_hung(struct amdgpu_device *adev)
++{
++ /* TODO: #DAL3 need to replace
++ u32 crtc_hung = 0;
++ u32 i, j, tmp;
++
++ crtc_hung = dal_get_connected_targets_vector(adev->dm.dal);
++
++ for (j = 0; j < 10; j++) {
++ for (i = 0; i < adev->mode_info.num_crtc; i++) {
++ if (crtc_hung & (1 << i)) {
++ int32_t vpos1, hpos1;
++ int32_t vpos2, hpos2;
++
++ tmp = dal_get_crtc_scanoutpos(
++ adev->dm.dal,
++ i,
++ &vpos1,
++ &hpos1);
++ udelay(10);
++ tmp = dal_get_crtc_scanoutpos(
++ adev->dm.dal,
++ i,
++ &vpos2,
++ &hpos2);
++
++ if (hpos1 != hpos2 && vpos1 != vpos2)
++ crtc_hung &= ~(1 << i);
++ }
++ }
++
++ if (crtc_hung == 0)
++ return false;
++ }
++*/
++ return true;
++}
++
++/* TODO: Remove mc access work around*/
++static void dm_stop_mc_access(struct amdgpu_device *adev,
++ struct amdgpu_mode_mc_save *save)
++{
++
++ u32 crtc_enabled, tmp;
++ int i;
++
++ save->vga_render_control = RREG32(mmVGA_RENDER_CONTROL);
++ save->vga_hdp_control = RREG32(mmVGA_HDP_CONTROL);
++
++ /* disable VGA render */
++ tmp = RREG32(mmVGA_RENDER_CONTROL);
++ tmp = REG_SET_FIELD(tmp, VGA_RENDER_CONTROL, VGA_VSTATUS_CNTL, 0);
++ WREG32(mmVGA_RENDER_CONTROL, tmp);
++
++ /* blank the display controllers */
++ for (i = 0; i < adev->mode_info.num_crtc; i++) {
++ crtc_enabled = REG_GET_FIELD(RREG32(mmCRTC_CONTROL + crtc_offsets[i]),
++ CRTC_CONTROL, CRTC_MASTER_EN);
++ if (crtc_enabled) {
++#if 0
++ u32 frame_count;
++ int j;
++
++ save->crtc_enabled[i] = true;
++ tmp = RREG32(mmCRTC_BLANK_CONTROL + crtc_offsets[i]);
++ if (REG_GET_FIELD(tmp, CRTC_BLANK_CONTROL, CRTC_BLANK_DATA_EN) == 0) {
++ amdgpu_display_vblank_wait(adev, i);
++ WREG32(mmCRTC_UPDATE_LOCK + crtc_offsets[i], 1);
++ tmp = REG_SET_FIELD(tmp, CRTC_BLANK_CONTROL, CRTC_BLANK_DATA_EN, 1);
++ WREG32(mmCRTC_BLANK_CONTROL + crtc_offsets[i], tmp);
++ WREG32(mmCRTC_UPDATE_LOCK + crtc_offsets[i], 0);
++ }
++ /* wait for the next frame */
++ frame_count = amdgpu_display_vblank_get_counter(adev, i);
++ for (j = 0; j < adev->usec_timeout; j++) {
++ if (amdgpu_display_vblank_get_counter(adev, i) != frame_count)
++ break;
++ udelay(1);
++ }
++ tmp = RREG32(mmGRPH_UPDATE + crtc_offsets[i]);
++ if (REG_GET_FIELD(tmp, GRPH_UPDATE, GRPH_UPDATE_LOCK) == 0) {
++ tmp = REG_SET_FIELD(tmp, GRPH_UPDATE, GRPH_UPDATE_LOCK, 1);
++ WREG32(mmGRPH_UPDATE + crtc_offsets[i], tmp);
++ }
++ tmp = RREG32(mmCRTC_MASTER_UPDATE_LOCK + crtc_offsets[i]);
++ if (REG_GET_FIELD(tmp, CRTC_MASTER_UPDATE_LOCK, MASTER_UPDATE_LOCK) == 0) {
++ tmp = REG_SET_FIELD(tmp, CRTC_MASTER_UPDATE_LOCK, MASTER_UPDATE_LOCK, 1);
++ WREG32(mmCRTC_MASTER_UPDATE_LOCK + crtc_offsets[i], tmp);
++ }
++#else
++ /* XXX this is a hack to avoid strange behavior with EFI on certain systems */
++ WREG32(mmCRTC_UPDATE_LOCK + crtc_offsets[i], 1);
++ tmp = RREG32(mmCRTC_CONTROL + crtc_offsets[i]);
++ tmp = REG_SET_FIELD(tmp, CRTC_CONTROL, CRTC_MASTER_EN, 0);
++ WREG32(mmCRTC_CONTROL + crtc_offsets[i], tmp);
++ WREG32(mmCRTC_UPDATE_LOCK + crtc_offsets[i], 0);
++ save->crtc_enabled[i] = false;
++ /* ***** */
++#endif
++ } else {
++ save->crtc_enabled[i] = false;
++ }
++ }
++}
++
++
++static void dm_resume_mc_access(struct amdgpu_device *adev,
++ struct amdgpu_mode_mc_save *save)
++{
++
++ u32 tmp, frame_count;
++ int i, j;
++
++ /* update crtc base addresses */
++ for (i = 0; i < adev->mode_info.num_crtc; i++) {
++ WREG32(mmGRPH_PRIMARY_SURFACE_ADDRESS_HIGH + crtc_offsets[i],
++ upper_32_bits(adev->mc.vram_start));
++ WREG32(mmGRPH_SECONDARY_SURFACE_ADDRESS_HIGH + crtc_offsets[i],
++ upper_32_bits(adev->mc.vram_start));
++ WREG32(mmGRPH_PRIMARY_SURFACE_ADDRESS + crtc_offsets[i],
++ (u32)adev->mc.vram_start);
++ WREG32(mmGRPH_SECONDARY_SURFACE_ADDRESS + crtc_offsets[i],
++ (u32)adev->mc.vram_start);
++
++ if (save->crtc_enabled[i]) {
++ tmp = RREG32(mmCRTC_MASTER_UPDATE_MODE + crtc_offsets[i]);
++ if (REG_GET_FIELD(tmp, CRTC_MASTER_UPDATE_MODE, MASTER_UPDATE_MODE) != 3) {
++ tmp = REG_SET_FIELD(tmp, CRTC_MASTER_UPDATE_MODE, MASTER_UPDATE_MODE, 3);
++ WREG32(mmCRTC_MASTER_UPDATE_MODE + crtc_offsets[i], tmp);
++ }
++ tmp = RREG32(mmGRPH_UPDATE + crtc_offsets[i]);
++ if (REG_GET_FIELD(tmp, GRPH_UPDATE, GRPH_UPDATE_LOCK)) {
++ tmp = REG_SET_FIELD(tmp, GRPH_UPDATE, GRPH_UPDATE_LOCK, 0);
++ WREG32(mmGRPH_UPDATE + crtc_offsets[i], tmp);
++ }
++ tmp = RREG32(mmCRTC_MASTER_UPDATE_LOCK + crtc_offsets[i]);
++ if (REG_GET_FIELD(tmp, CRTC_MASTER_UPDATE_LOCK, MASTER_UPDATE_LOCK)) {
++ tmp = REG_SET_FIELD(tmp, CRTC_MASTER_UPDATE_LOCK, MASTER_UPDATE_LOCK, 0);
++ WREG32(mmCRTC_MASTER_UPDATE_LOCK + crtc_offsets[i], tmp);
++ }
++ for (j = 0; j < adev->usec_timeout; j++) {
++ tmp = RREG32(mmGRPH_UPDATE + crtc_offsets[i]);
++ if (REG_GET_FIELD(tmp, GRPH_UPDATE, GRPH_SURFACE_UPDATE_PENDING) == 0)
++ break;
++ udelay(1);
++ }
++ tmp = RREG32(mmCRTC_BLANK_CONTROL + crtc_offsets[i]);
++ tmp = REG_SET_FIELD(tmp, CRTC_BLANK_CONTROL, CRTC_BLANK_DATA_EN, 0);
++ WREG32(mmCRTC_UPDATE_LOCK + crtc_offsets[i], 1);
++ WREG32(mmCRTC_BLANK_CONTROL + crtc_offsets[i], tmp);
++ WREG32(mmCRTC_UPDATE_LOCK + crtc_offsets[i], 0);
++ /* wait for the next frame */
++ frame_count = amdgpu_display_vblank_get_counter(adev, i);
++ for (j = 0; j < adev->usec_timeout; j++) {
++ if (amdgpu_display_vblank_get_counter(adev, i) != frame_count)
++ break;
++ udelay(1);
++ }
++ }
++ }
++
++ WREG32(mmVGA_MEMORY_BASE_ADDRESS_HIGH, upper_32_bits(adev->mc.vram_start));
++ WREG32(mmVGA_MEMORY_BASE_ADDRESS, lower_32_bits(adev->mc.vram_start));
++
++ /* Unlock vga access */
++ WREG32(mmVGA_HDP_CONTROL, save->vga_hdp_control);
++ mdelay(1);
++ WREG32(mmVGA_RENDER_CONTROL, save->vga_render_control);
++}
++
++/* End of TODO: Remove mc access work around*/
++
++static bool dm_is_idle(void *handle)
++{
++ /* XXX todo */
++ return true;
++}
++
++static int dm_wait_for_idle(void *handle)
++{
++ /* XXX todo */
++ return 0;
++}
++
++static void dm_print_status(void *handle)
++{
++ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
++ dev_info(adev->dev, "DCE 10.x registers\n");
++ /* XXX todo */
++}
++
++static int dm_soft_reset(void *handle)
++{
++ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
++ u32 srbm_soft_reset = 0, tmp;
++
++ if (dm_is_display_hung(adev))
++ srbm_soft_reset |= SRBM_SOFT_RESET__SOFT_RESET_DC_MASK;
++
++ if (srbm_soft_reset) {
++ dm_print_status(adev);
++
++ tmp = RREG32(mmSRBM_SOFT_RESET);
++ tmp |= srbm_soft_reset;
++ dev_info(adev->dev, "SRBM_SOFT_RESET=0x%08X\n", tmp);
++ WREG32(mmSRBM_SOFT_RESET, tmp);
++ tmp = RREG32(mmSRBM_SOFT_RESET);
++
++ udelay(50);
++
++ tmp &= ~srbm_soft_reset;
++ WREG32(mmSRBM_SOFT_RESET, tmp);
++ tmp = RREG32(mmSRBM_SOFT_RESET);
++
++ /* Wait a little for things to settle down */
++ udelay(50);
++ dm_print_status(adev);
++ }
++ return 0;
++}
++
++
++
++static void dm_pflip_high_irq(void *interrupt_params)
++{
++ struct amdgpu_flip_work *works;
++ struct amdgpu_crtc *amdgpu_crtc;
++ struct common_irq_params *irq_params = interrupt_params;
++ struct amdgpu_device *adev = irq_params->adev;
++ unsigned long flags;
++ const struct dc *dc = irq_params->adev->dm.dc;
++ const struct dc_target *dc_target =
++ dc_get_target_on_irq_source(dc, irq_params->irq_src);
++ uint8_t link_index = 0;
++
++ /* TODO: #flip address all tags together*/
++ if (dc_target != NULL)
++ link_index = dc_target_get_link_index(dc_target);
++
++ amdgpu_crtc= adev->mode_info.crtcs[link_index];
++
++ /* IRQ could occur when in initial stage */
++ if(amdgpu_crtc == NULL)
++ return;
++
++ spin_lock_irqsave(&adev->ddev->event_lock, flags);
++ works = amdgpu_crtc->pflip_works;
++ if (amdgpu_crtc->pflip_status != AMDGPU_FLIP_SUBMITTED){
++ DRM_DEBUG_DRIVER("amdgpu_crtc->pflip_status = %d != "
++ "AMDGPU_FLIP_SUBMITTED(%d)\n",
++ amdgpu_crtc->pflip_status,
++ AMDGPU_FLIP_SUBMITTED);
++ spin_unlock_irqrestore(&adev->ddev->event_lock, flags);
++ return;
++ }
++
++ /* page flip completed. clean up */
++ amdgpu_crtc->pflip_status = AMDGPU_FLIP_NONE;
++ amdgpu_crtc->pflip_works = NULL;
++
++ /* wakeup usersapce */
++ if(works->event)
++ drm_send_vblank_event(
++ adev->ddev,
++ amdgpu_crtc->crtc_id,
++ works->event);
++
++ spin_unlock_irqrestore(&adev->ddev->event_lock, flags);
++
++ drm_crtc_vblank_put(&amdgpu_crtc->base);
++ schedule_work(&works->unpin_work);
++}
++
++static void dm_crtc_high_irq(void *interrupt_params)
++{
++ struct common_irq_params *irq_params = interrupt_params;
++ struct amdgpu_device *adev = irq_params->adev;
++ const struct dc *dc = irq_params->adev->dm.dc;
++ const struct dc_target *dc_target =
++ dc_get_target_on_irq_source(dc, irq_params->irq_src);
++ uint8_t link_index = 0;
++
++ /* TODO: #flip fix all tags together*/
++ if (dc_target != NULL)
++ link_index = dc_target_get_link_index(dc_target);
++
++ drm_handle_vblank(adev->ddev, link_index);
++
++}
++
++static int dm_set_clockgating_state(void *handle,
++ enum amd_clockgating_state state)
++{
++ return 0;
++}
++
++static int dm_set_powergating_state(void *handle,
++ enum amd_powergating_state state)
++{
++ return 0;
++}
++
++/* Prototypes of private functions */
++static int dm_early_init(void* handle);
++
++static void detect_on_all_dc_links(struct amdgpu_display_manager *dm)
++{
++ uint32_t i;
++ const struct dc_link *dc_link;
++ struct dc_caps caps = { 0 };
++
++ dc_get_caps(dm->dc, &caps);
++
++ for (i = 0; i < caps.max_links; i++) {
++ dc_link = dc_get_link_at_index(dm->dc, i);
++ dc_link_detect(dc_link);
++ }
++}
++
++/* Init display KMS
++ *
++ * Returns 0 on success
++ */
++int amdgpu_dm_init(struct amdgpu_device *adev)
++{
++ struct dal_init_data init_data;
++ struct drm_device *ddev = adev->ddev;
++ adev->dm.ddev = adev->ddev;
++ adev->dm.adev = adev;
++
++ /* Zero all the fields */
++ memset(&init_data, 0, sizeof(init_data));
++
++ /* initialize DAL's lock (for SYNC context use) */
++ spin_lock_init(&adev->dm.dal_lock);
++
++ /* initialize DAL's mutex */
++ mutex_init(&adev->dm.dal_mutex);
++
++ if(amdgpu_dm_irq_init(adev)) {
++ DRM_ERROR("amdgpu: failed to initialize DM IRQ support.\n");
++ goto error;
++ }
++
++ if (ddev->pdev) {
++ init_data.bdf_info.DEVICE_NUMBER = PCI_SLOT(ddev->pdev->devfn);
++ init_data.bdf_info.FUNCTION_NUMBER =
++ PCI_FUNC(ddev->pdev->devfn);
++ if (ddev->pdev->bus)
++ init_data.bdf_info.BUS_NUMBER = ddev->pdev->bus->number;
++ }
++
++ init_data.display_param = display_param;
++
++ init_data.asic_id.chip_family = adev->family;
++
++ init_data.asic_id.pci_revision_id = adev->rev_id;
++ init_data.asic_id.hw_internal_rev = adev->external_rev_id;
++
++ init_data.asic_id.vram_width = adev->mc.vram_width;
++ /* TODO: initialize init_data.asic_id.vram_type here!!!! */
++ init_data.asic_id.atombios_base_address =
++ adev->mode_info.atom_context->bios;
++ init_data.asic_id.runtime_flags.flags.bits.SKIP_POWER_DOWN_ON_RESUME = 1;
++
++ if (adev->asic_type == CHIP_CARRIZO)
++ init_data.asic_id.runtime_flags.flags.bits.GNB_WAKEUP_SUPPORTED = 1;
++
++ init_data.driver = adev;
++
++ adev->dm.cgs_device = amdgpu_cgs_create_device(adev);
++
++ if (!adev->dm.cgs_device) {
++ DRM_ERROR("amdgpu: failed to create cgs device.\n");
++ goto error;
++ }
++
++ init_data.cgs_device = adev->dm.cgs_device;
++
++ adev->dm.dal = NULL;
++
++ /* enable gpu scaling in DAL */
++ init_data.display_param.bool_param_enable_mask |=
++ 1 << DAL_PARAM_ENABLE_GPU_SCALING;
++ init_data.display_param.bool_param_values |=
++ 1 << DAL_PARAM_ENABLE_GPU_SCALING;
++
++ /* Display Core create. */
++ adev->dm.dc = dc_create(&init_data);
++
++ if (amdgpu_dm_initialize_drm_device(adev)) {
++ DRM_ERROR(
++ "amdgpu: failed to initialize sw for display support.\n");
++ goto error;
++ }
++
++ /* Update the actual used number of crtc */
++ adev->mode_info.num_crtc = adev->dm.display_indexes_num;
++
++ /* TODO: Add_display_info? */
++
++ /* TODO use dynamic cursor width */
++ adev->ddev->mode_config.cursor_width = 128;
++ adev->ddev->mode_config.cursor_height = 128;
++
++ if (drm_vblank_init(adev->ddev, adev->dm.display_indexes_num)) {
++ DRM_ERROR(
++ "amdgpu: failed to initialize sw for display support.\n");
++ goto error;
++ }
++
++ DRM_INFO("KMS initialized.\n");
++
++ return 0;
++error:
++ amdgpu_dm_fini(adev);
++
++ return -1;
++}
++
++void amdgpu_dm_fini(struct amdgpu_device *adev)
++{
++ /*
++ * TODO: pageflip, vlank interrupt
++ *
++ * amdgpu_dm_destroy_drm_device(&adev->dm);
++ * amdgpu_dm_irq_fini(adev);
++ */
++
++ if (adev->dm.cgs_device) {
++ amdgpu_cgs_destroy_device(adev->dm.cgs_device);
++ adev->dm.cgs_device = NULL;
++ }
++
++ /* DC Destroy TODO: Replace destroy DAL */
++ {
++ dc_destroy(&adev->dm.dc);
++ }
++ return;
++}
++
++/* moved from amdgpu_dm_kms.c */
++void amdgpu_dm_destroy()
++{
++}
++
++static int dm_sw_init(void *handle)
++{
++ return 0;
++}
++
++static int dm_sw_fini(void *handle)
++{
++ return 0;
++}
++
++static int dm_hw_init(void *handle)
++{
++ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
++ /* Create DAL display manager */
++ amdgpu_dm_init(adev);
++
++ amdgpu_dm_hpd_init(adev);
++
++ return 0;
++}
++
++static int dm_hw_fini(void *handle)
++{
++ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
++
++ amdgpu_dm_hpd_fini(adev);
++
++ amdgpu_dm_irq_fini(adev);
++
++ return 0;
++}
++
++static int dm_suspend(void *handle)
++{
++ struct amdgpu_device *adev = handle;
++ struct amdgpu_display_manager *dm = &adev->dm;
++ struct drm_crtc *crtc;
++
++ dc_set_power_state(
++ dm->dc,
++ DC_ACPI_CM_POWER_STATE_D3,
++ DC_VIDEO_POWER_SUSPEND);
++
++ amdgpu_dm_irq_suspend(adev);
++
++ list_for_each_entry(crtc, &dm->ddev->mode_config.crtc_list, head) {
++ crtc->mode.clock = 0;
++ }
++
++ return 0;
++}
++
++static int dm_resume(void *handle)
++{
++ int ret = 0;
++ struct amdgpu_device *adev = handle;
++ struct amdgpu_display_manager *dm = &adev->dm;
++
++ dc_set_power_state(
++ dm->dc,
++ DC_ACPI_CM_POWER_STATE_D0,
++ DC_VIDEO_POWER_ON);
++
++ amdgpu_dm_irq_resume(adev);
++
++ dc_resume(dm->dc);
++
++ detect_on_all_dc_links(dm);
++
++ drm_mode_config_reset(adev->ddev);
++
++ return ret;
++}
++const struct amd_ip_funcs amdgpu_dm_funcs = {
++ .early_init = dm_early_init,
++ .late_init = NULL,
++ .sw_init = dm_sw_init,
++ .sw_fini = dm_sw_fini,
++ .hw_init = dm_hw_init,
++ .hw_fini = dm_hw_fini,
++ .suspend = dm_suspend,
++ .resume = dm_resume,
++ .is_idle = dm_is_idle,
++ .wait_for_idle = dm_wait_for_idle,
++ .soft_reset = dm_soft_reset,
++ .print_status = dm_print_status,
++ .set_clockgating_state = dm_set_clockgating_state,
++ .set_powergating_state = dm_set_powergating_state,
++};
++
++/* TODO: it is temporary non-const, should fixed later */
++static struct drm_mode_config_funcs amdgpu_dm_mode_funcs = {
++ .atomic_check = amdgpu_dm_atomic_check,
++ .atomic_commit = amdgpu_dm_atomic_commit
++};
++
++static bool dm_get_sink_from_link(const struct dc_link *link,
++ struct amdgpu_connector *aconnector,
++ const struct dc_sink **sink)
++{
++ int i;
++ *sink = NULL;
++
++ if (!link->sink_count) {
++ DRM_INFO("No sinks on link!\n");
++ return true;
++ } else if (link->sink_count > 1 && !aconnector) {
++ DRM_ERROR("Multi sink link but no connector given!\n");
++ return false;
++ }
++
++ if (link->sink_count == 1) {
++ *sink = link->sink[0];
++ return true;
++ }
++
++ for (i = 0; i < link->sink_count; i++)
++ if (aconnector->dc_sink == link->sink[i])
++ *sink = aconnector->dc_sink;
++
++ return true;
++}
++
++void amdgpu_dm_update_connector_after_detect(
++ struct amdgpu_connector *aconnector)
++{
++ struct drm_connector *connector = &aconnector->base;
++ struct drm_device *dev = connector->dev;
++ const struct dc_link *dc_link = aconnector->dc_link;
++ const struct dc_sink *sink;
++
++ /* MST handled by drm_mst framework */
++ if (aconnector->mst_mgr.mst_state)
++ return;
++ if (!dm_get_sink_from_link(dc_link, aconnector, &sink)) {
++ return;
++ }
++
++ if (aconnector->dc_sink == sink) {
++ /* We got a DP short pulse (Link Loss, DP CTS, etc...).
++ * Do nothing!! */
++ DRM_INFO("DCHPD: connector_id=%d: dc_sink didn't change.\n",
++ aconnector->connector_id);
++ return;
++ }
++
++ DRM_INFO("DCHPD: connector_id=%d: Old sink=%p New sink=%p\n",
++ aconnector->connector_id, aconnector->dc_sink, sink);
++
++ mutex_lock(&dev->mode_config.mutex);
++
++ /* 1. Update status of the drm connector
++ * 2. Send an event and let userspace tell us what to do */
++ if (sink) {
++ /* TODO: check if we still need the S3 mode update workaround.
++ * If yes, put it here. */
++
++ aconnector->dc_sink = sink;
++ if (sink->dc_edid.length == 0)
++ aconnector->edid = NULL;
++ else {
++ aconnector->edid =
++ (struct edid *) sink->dc_edid.raw_edid;
++ drm_mode_connector_update_edid_property(connector,
++ aconnector->edid);
++ amdgpu_dm_connector_get_modes(&aconnector->base);
++ }
++ } else {
++ drm_mode_connector_update_edid_property(connector, NULL);
++ aconnector->num_modes = 0;
++ aconnector->dc_sink = NULL;
++ }
++
++ mutex_unlock(&dev->mode_config.mutex);
++}
++
++static void handle_hpd_irq(void *param)
++{
++ struct amdgpu_connector *aconnector = (struct amdgpu_connector *)param;
++ struct drm_connector *connector = &aconnector->base;
++ struct drm_device *dev = connector->dev;
++
++ dc_link_detect(aconnector->dc_link);
++ amdgpu_dm_update_connector_after_detect(aconnector);
++ drm_helper_hpd_irq_event(dev);
++}
++
++static void handle_hpd_rx_irq(void *param)
++{
++ struct amdgpu_connector *aconnector = (struct amdgpu_connector *)param;
++ struct drm_connector *connector = &aconnector->base;
++ struct drm_device *dev = connector->dev;
++
++ if (dc_link_handle_hpd_rx_irq(aconnector->dc_link) &&
++ !aconnector->mst_mgr.mst_state) {
++ /* Downstream Port status changed. */
++ dc_link_detect(aconnector->dc_link);
++ amdgpu_dm_update_connector_after_detect(aconnector);
++ drm_helper_hpd_irq_event(dev);
++ }
++
++ if (aconnector->mst_mgr.mst_state)
++ dc_helpers_dp_mst_handle_mst_hpd_rx_irq(param);
++}
++
++static void register_hpd_handlers(struct amdgpu_device *adev)
++{
++ struct drm_device *dev = adev->ddev;
++ struct drm_connector *connector;
++ struct amdgpu_connector *aconnector;
++ const struct dc_link *dc_link;
++ struct dc_interrupt_params int_params = {0};
++
++ int_params.requested_polarity = INTERRUPT_POLARITY_DEFAULT;
++ int_params.current_polarity = INTERRUPT_POLARITY_DEFAULT;
++
++ list_for_each_entry(connector,
++ &dev->mode_config.connector_list, head) {
++
++ aconnector = to_amdgpu_connector(connector);
++ dc_link = aconnector->dc_link;
++
++ int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT;
++ int_params.irq_source = dc_link->irq_source_hpd;
++
++ amdgpu_dm_irq_register_interrupt(adev, &int_params,
++ handle_hpd_irq,
++ (void *) aconnector);
++
++ if (DC_IRQ_SOURCE_INVALID != dc_link->irq_source_hpd_rx) {
++
++ /* Also register for DP short pulse (hpd_rx). */
++ int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT;
++ int_params.irq_source = dc_link->irq_source_hpd_rx;
++
++ amdgpu_dm_irq_register_interrupt(adev, &int_params,
++ handle_hpd_rx_irq,
++ (void *) aconnector);
++ }
++ }
++}
++
++/* Register IRQ sources and initialize IRQ callbacks */
++static int dce110_register_irq_handlers(struct amdgpu_device *adev)
++{
++ struct dc *dc = adev->dm.dc;
++ struct common_irq_params *c_irq_params;
++ struct dc_interrupt_params int_params = {0};
++ int r;
++ int i;
++ struct dc_caps caps = { 0 };
++
++ dc_get_caps(dc, &caps);
++
++ int_params.requested_polarity = INTERRUPT_POLARITY_DEFAULT;
++ int_params.current_polarity = INTERRUPT_POLARITY_DEFAULT;
++
++ /* Actions of amdgpu_irq_add_id():
++ * 1. Register a set() function with base driver.
++ * Base driver will call set() function to enable/disable an
++ * interrupt in DC hardware.
++ * 2. Register amdgpu_dm_irq_handler().
++ * Base driver will call amdgpu_dm_irq_handler() for ALL interrupts
++ * coming from DC hardware.
++ * amdgpu_dm_irq_handler() will re-direct the interrupt to DC
++ * for acknowledging and handling. */
++
++ for (i = VISLANDS30_IV_SRCID_D1_V_UPDATE_INT;
++ i <= VISLANDS30_IV_SRCID_D6_V_UPDATE_INT; i += 2) {
++ r = amdgpu_irq_add_id(adev, i, &adev->crtc_irq);
++ if (r) {
++ DRM_ERROR("Failed to add crtc irq id!\n");
++ return r;
++ }
++
++ int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
++ int_params.irq_source =
++ dc_interrupt_to_irq_source(dc, i, 0);
++
++ c_irq_params = &adev->dm.vupdate_params[int_params.irq_source - DC_IRQ_SOURCE_VUPDATE1];
++
++ c_irq_params->adev = adev;
++ c_irq_params->irq_src = int_params.irq_source;
++
++ amdgpu_dm_irq_register_interrupt(adev, &int_params,
++ dm_crtc_high_irq, c_irq_params);
++ }
++
++ for (i = VISLANDS30_IV_SRCID_D1_GRPH_PFLIP;
++ i <= VISLANDS30_IV_SRCID_D6_GRPH_PFLIP; i += 2) {
++ r = amdgpu_irq_add_id(adev, i, &adev->pageflip_irq);
++ if (r) {
++ DRM_ERROR("Failed to add page flip irq id!\n");
++ return r;
++ }
++
++ int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
++ int_params.irq_source =
++ dc_interrupt_to_irq_source(dc, i, 0);
++
++ c_irq_params = &adev->dm.pflip_params[int_params.irq_source - DC_IRQ_SOURCE_PFLIP_FIRST];
++
++ c_irq_params->adev = adev;
++ c_irq_params->irq_src = int_params.irq_source;
++
++ amdgpu_dm_irq_register_interrupt(adev, &int_params,
++ dm_pflip_high_irq, c_irq_params);
++
++ }
++
++ /* HPD */
++ r = amdgpu_irq_add_id(adev, VISLANDS30_IV_SRCID_HOTPLUG_DETECT_A,
++ &adev->hpd_irq);
++ if (r) {
++ DRM_ERROR("Failed to add hpd irq id!\n");
++ return r;
++ }
++
++ register_hpd_handlers(adev);
++
++ /* This is a part of HPD initialization. */
++ drm_kms_helper_poll_init(adev->ddev);
++
++ return 0;
++}
++
++static int amdgpu_dm_mode_config_init(struct amdgpu_device *adev)
++{
++ int r;
++
++ adev->mode_info.mode_config_initialized = true;
++
++ amdgpu_dm_mode_funcs.fb_create =
++ amdgpu_mode_funcs.fb_create;
++ amdgpu_dm_mode_funcs.output_poll_changed =
++ amdgpu_mode_funcs.output_poll_changed;
++
++ adev->ddev->mode_config.funcs = (void *)&amdgpu_dm_mode_funcs;
++
++ adev->ddev->mode_config.max_width = 16384;
++ adev->ddev->mode_config.max_height = 16384;
++
++ adev->ddev->mode_config.preferred_depth = 24;
++ adev->ddev->mode_config.prefer_shadow = 1;
++
++ adev->ddev->mode_config.fb_base = adev->mc.aper_base;
++
++ r = amdgpu_modeset_create_props(adev);
++ if (r)
++ return r;
++
++ return 0;
++}
++
++#if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) ||\
++ defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE)
++
++static int amdgpu_dm_backlight_update_status(struct backlight_device *bd)
++{
++ struct amdgpu_display_manager *dm = bl_get_data(bd);
++
++ if (dc_link_set_backlight_level(dm->backlight_link,
++ bd->props.brightness))
++ return 0;
++ else
++ return 1;
++}
++
++static int amdgpu_dm_backlight_get_brightness(struct backlight_device *bd)
++{
++ return bd->props.brightness;
++}
++
++static const struct backlight_ops amdgpu_dm_backlight_ops = {
++ .get_brightness = amdgpu_dm_backlight_get_brightness,
++ .update_status = amdgpu_dm_backlight_update_status,
++};
++
++void amdgpu_dm_register_backlight_device(struct amdgpu_display_manager *dm)
++{
++ char bl_name[16];
++ struct backlight_properties props = { 0 };
++
++ props.max_brightness = AMDGPU_MAX_BL_LEVEL;
++ props.type = BACKLIGHT_RAW;
++
++ snprintf(bl_name, sizeof(bl_name), "amdgpu_bl%d",
++ dm->adev->ddev->primary->index);
++
++ dm->backlight_dev = backlight_device_register(bl_name,
++ dm->adev->ddev->dev,
++ dm,
++ &amdgpu_dm_backlight_ops,
++ &props);
++
++ if (NULL == dm->backlight_dev)
++ DRM_ERROR("DM: Backlight registration failed!\n");
++ else
++ DRM_INFO("DM: Registered Backlight device: %s\n", bl_name);
++}
++
++#endif
++
++/* In this architecture, the association
++ * connector -> encoder -> crtc
++ * id not really requried. The crtc and connector will hold the
++ * display_index as an abstraction to use with DAL component
++ *
++ * Returns 0 on success
++ */
++int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev)
++{
++ struct amdgpu_display_manager *dm = &adev->dm;
++ uint32_t link_index;
++ struct drm_connector *connector;
++ struct amdgpu_connector *aconnector;
++ struct amdgpu_encoder *aencoder;
++ struct amdgpu_crtc *acrtc;
++ struct dc_caps caps = { 0 };
++ uint32_t link_cnt;
++
++ dc_get_caps(dm->dc, &caps);
++ link_cnt = caps.max_links;
++
++ if (amdgpu_dm_mode_config_init(dm->adev)) {
++ DRM_ERROR("DM: Failed to initialize mode config\n");
++ return -1;
++ }
++
++ /* loops over all connectors on the board */
++ for (link_index = 0; link_index < link_cnt; link_index++) {
++
++ if (link_index > AMDGPU_DM_MAX_DISPLAY_INDEX) {
++ DRM_ERROR(
++ "KMS: Cannot support more than %d display indeces\n",
++ AMDGPU_DM_MAX_DISPLAY_INDEX);
++ continue;
++ }
++
++ aconnector = kzalloc(sizeof(*aconnector), GFP_KERNEL);
++ if (!aconnector)
++ goto fail_connector;
++
++ aencoder = kzalloc(sizeof(*aencoder), GFP_KERNEL);
++ if (!aencoder)
++ goto fail_encoder;
++
++ acrtc = kzalloc(sizeof(struct amdgpu_crtc), GFP_KERNEL);
++ if (!acrtc)
++ goto fail_crtc;
++
++ if (amdgpu_dm_crtc_init(
++ dm,
++ acrtc,
++ link_index)) {
++ DRM_ERROR("KMS: Failed to initialize crtc\n");
++ goto fail;
++ }
++
++ if (amdgpu_dm_encoder_init(
++ dm->ddev,
++ aencoder,
++ link_index,
++ acrtc)) {
++ DRM_ERROR("KMS: Failed to initialize encoder\n");
++ goto fail;
++ }
++
++ if (amdgpu_dm_connector_init(
++ dm,
++ aconnector,
++ link_index,
++ aencoder)) {
++ DRM_ERROR("KMS: Failed to initialize connector\n");
++ goto fail;
++ }
++ }
++
++ dm->display_indexes_num = link_cnt;
++
++ detect_on_all_dc_links(&adev->dm);
++ list_for_each_entry(connector, &adev->ddev->mode_config.connector_list, head)
++ amdgpu_dm_update_connector_after_detect(to_amdgpu_connector(connector));
++
++ /* Software is initialized. Now we can register interrupt handlers. */
++ switch (adev->asic_type) {
++ case CHIP_CARRIZO:
++ if (dce110_register_irq_handlers(dm->adev)) {
++ DRM_ERROR("DM: Failed to initialize IRQ\n");
++ return -1;
++ }
++ break;
++ default:
++ DRM_ERROR("Usupported ASIC type: 0x%X\n", adev->asic_type);
++ return -1;
++ }
++
++ drm_mode_config_reset(dm->ddev);
++
++ return 0;
++
++fail:
++ /* clean any dongling drm structure for the last (corrupted)
++ display target */
++ amdgpu_dm_crtc_destroy(&acrtc->base);
++fail_crtc:
++ amdgpu_dm_encoder_destroy(&aencoder->base);
++fail_encoder:
++ amdgpu_dm_connector_destroy(&aconnector->base);
++fail_connector:
++ if (dm->backlight_dev) {
++ backlight_device_unregister(dm->backlight_dev);
++ dm->backlight_dev = NULL;
++ }
++ return -1;
++}
++
++void amdgpu_dm_destroy_drm_device(
++ struct amdgpu_display_manager *dm)
++{
++ drm_mode_config_cleanup(dm->ddev);
++ /*switch_dev_unregister(&dm->hdmi_audio_dev);*/
++ return;
++}
++
++/******************************************************************************
++ * amdgpu_display_funcs functions
++ *****************************************************************************/
++
++
++static void dm_set_vga_render_state(struct amdgpu_device *adev,
++ bool render)
++{
++ u32 tmp;
++
++ /* Lockout access through VGA aperture*/
++ tmp = RREG32(mmVGA_HDP_CONTROL);
++ if (render)
++ tmp = REG_SET_FIELD(tmp, VGA_HDP_CONTROL, VGA_MEMORY_DISABLE, 0);
++ else
++ tmp = REG_SET_FIELD(tmp, VGA_HDP_CONTROL, VGA_MEMORY_DISABLE, 1);
++ WREG32(mmVGA_HDP_CONTROL, tmp);
++
++ /* disable VGA render */
++ tmp = RREG32(mmVGA_RENDER_CONTROL);
++ if (render)
++ tmp = REG_SET_FIELD(tmp, VGA_RENDER_CONTROL, VGA_VSTATUS_CNTL, 1);
++ else
++ tmp = REG_SET_FIELD(tmp, VGA_RENDER_CONTROL, VGA_VSTATUS_CNTL, 0);
++ WREG32(mmVGA_RENDER_CONTROL, tmp);
++}
++
++/**
++ * dm_bandwidth_update - program display watermarks
++ *
++ * @adev: amdgpu_device pointer
++ *
++ * Calculate and program the display watermarks and line buffer allocation.
++ */
++static void dm_bandwidth_update(struct amdgpu_device *adev)
++{
++ AMDGPU_DM_NOT_IMPL("%s\n", __func__);
++}
++
++static void dm_set_backlight_level(struct amdgpu_encoder *amdgpu_encoder,
++ u8 level)
++{
++ /* TODO: translate amdgpu_encoder to display_index and call DAL */
++ AMDGPU_DM_NOT_IMPL("%s\n", __func__);
++}
++
++static u8 dm_get_backlight_level(struct amdgpu_encoder *amdgpu_encoder)
++{
++ /* TODO: translate amdgpu_encoder to display_index and call DAL */
++ AMDGPU_DM_NOT_IMPL("%s\n", __func__);
++ return 0;
++}
++
++/******************************************************************************
++ * Page Flip functions
++ ******************************************************************************/
++
++void amdgpu_dm_flip_cleanup(
++ struct amdgpu_device *adev,
++ struct amdgpu_crtc *acrtc)
++{
++ int r;
++ struct amdgpu_flip_work *works = acrtc->pflip_works;
++
++ acrtc->pflip_works = NULL;
++ acrtc->pflip_status = AMDGPU_FLIP_NONE;
++
++ if (works) {
++ if(works->event)
++ drm_send_vblank_event(
++ adev->ddev,
++ acrtc->crtc_id,
++ works->event);
++
++ r = amdgpu_bo_reserve(works->old_rbo, false);
++ if (likely(r == 0)) {
++ r = amdgpu_bo_unpin(works->old_rbo);
++ if (unlikely(r != 0)) {
++ DRM_ERROR("failed to unpin buffer after flip\n");
++ }
++ amdgpu_bo_unreserve(works->old_rbo);
++ } else
++ DRM_ERROR("failed to reserve buffer after flip\n");
++
++ drm_gem_object_unreference_unlocked(&works->old_rbo->gem_base);
++ kfree(works->shared);
++ kfree(works);
++ }
++}
++
++/**
++ * dm_page_flip - called by amdgpu_flip_work_func(), which is triggered
++ * via DRM IOCTL, by user mode.
++ *
++ * @adev: amdgpu_device pointer
++ * @crtc_id: crtc to cleanup pageflip on
++ * @crtc_base: new address of the crtc (GPU MC address)
++ *
++ * Does the actual pageflip (surface address update).
++ */
++static void dm_page_flip(struct amdgpu_device *adev,
++ int crtc_id, u64 crtc_base)
++{
++ struct amdgpu_crtc *acrtc;
++ struct dc_target *target;
++ struct dc_flip_addrs addr = { {0} };
++
++ /*
++ * TODO risk of concurrency issues
++ *
++ * This should guarded by the dal_mutex but we can't do this since the
++ * caller uses a spin_lock on event_lock.
++ *
++ * If we wait on the dal_mutex a second page flip interrupt might come,
++ * spin on the event_lock, disabling interrupts while it does so. At
++ * this point the core can no longer be pre-empted and return to the
++ * thread that waited on the dal_mutex and we're deadlocked.
++ *
++ * With multiple cores the same essentially happens but might just take
++ * a little longer to lock up all cores.
++ *
++ * The reason we should lock on dal_mutex is so that we can be sure
++ * nobody messes with acrtc->target after we read and check its value.
++ *
++ * We might be able to fix our concurrency issues with a work queue
++ * where we schedule all work items (mode_set, page_flip, etc.) and
++ * execute them one by one. Care needs to be taken to still deal with
++ * any potential concurrency issues arising from interrupt calls.
++ */
++
++ acrtc = adev->mode_info.crtcs[crtc_id];
++ target = acrtc->target;
++
++ /*
++ * Received a page flip call after the display has been reset. Make sure
++ * we return the buffers.
++ */
++ if (!target) {
++ amdgpu_dm_flip_cleanup(adev, acrtc);
++ return;
++ }
++
++ addr.address.grph.addr.low_part = lower_32_bits(crtc_base);
++ addr.address.grph.addr.high_part = upper_32_bits(crtc_base);
++
++ dc_flip_surface_addrs(
++ adev->dm.dc,
++ dc_target_get_status(target)->surfaces,
++ &addr, 1);
++}
++
++static const struct amdgpu_display_funcs display_funcs = {
++ .set_vga_render_state = dm_set_vga_render_state,
++ .bandwidth_update = dm_bandwidth_update, /* called unconditionally */
++ .vblank_get_counter = dm_vblank_get_counter,/* called unconditionally */
++ .vblank_wait = NULL, /* not called anywhere */
++ .is_display_hung = dm_is_display_hung,/* called unconditionally */
++ .backlight_set_level =
++ dm_set_backlight_level,/* called unconditionally */
++ .backlight_get_level =
++ dm_get_backlight_level,/* called unconditionally */
++ .hpd_sense = NULL,/* called unconditionally */
++ .hpd_set_polarity = NULL, /* called unconditionally */
++ .hpd_get_gpio_reg = dm_hpd_get_gpio_reg,/* called unconditionally */
++ .page_flip = dm_page_flip, /* called unconditionally */
++ .page_flip_get_scanoutpos =
++ dm_crtc_get_scanoutpos,/* called unconditionally */
++ .add_encoder = NULL, /* VBIOS parsing. DAL does it. */
++ .add_connector = NULL, /* VBIOS parsing. DAL does it. */
++ .stop_mc_access = dm_stop_mc_access, /* called unconditionally */
++ .resume_mc_access = dm_resume_mc_access, /* called unconditionally */
++};
++
++static void set_display_funcs(struct amdgpu_device *adev)
++{
++ if (adev->mode_info.funcs == NULL)
++ adev->mode_info.funcs = &display_funcs;
++}
++
++static int dm_early_init(void *handle)
++{
++ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
++ set_display_funcs(adev);
++ amdgpu_dm_set_irq_funcs(adev);
++
++ switch (adev->asic_type) {
++ case CHIP_CARRIZO:
++ adev->mode_info.num_crtc = 3;
++ adev->mode_info.num_hpd = 6;
++ adev->mode_info.num_dig = 9;
++ break;
++ default:
++ DRM_ERROR("Usupported ASIC type: 0x%X\n", adev->asic_type);
++ return -EINVAL;
++ }
++
++ /* Note: Do NOT change adev->audio_endpt_rreg and
++ * adev->audio_endpt_wreg because they are initialised in
++ * amdgpu_device_init() */
++
++
++
++ return 0;
++}
++
++
++bool amdgpu_dm_acquire_dal_lock(struct amdgpu_display_manager *dm)
++{
++ /* TODO */
++ return true;
++}
++
++bool amdgpu_dm_release_dal_lock(struct amdgpu_display_manager *dm)
++{
++ /* TODO */
++ return true;
++}
+diff --git a/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm.h b/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm.h
+new file mode 100644
+index 0000000..57e9c45
+--- /dev/null
++++ b/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm.h
+@@ -0,0 +1,166 @@
++/*
++ * Copyright 2015 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 __AMDGPU_DM_H__
++#define __AMDGPU_DM_H__
++
++/*
++#include "linux/switch.h"
++*/
++
++/*
++ * This file contains the definition for amdgpu_display_manager
++ * and its API for amdgpu driver's use.
++ * This component provides all the display related functionality
++ * and this is the only component that calls DAL API.
++ * The API contained here intended for amdgpu driver use.
++ * The API that is called directly from KMS framework is located
++ * in amdgpu_dm_kms.h file
++ */
++
++#define AMDGPU_DM_MAX_DISPLAY_INDEX 31
++/*
++#include "include/amdgpu_dal_power_if.h"
++#include "amdgpu_dm_irq.h"
++*/
++
++#include "irq_types.h"
++
++/* Forward declarations */
++struct amdgpu_device;
++struct drm_device;
++struct amdgpu_dm_irq_handler_data;
++
++struct amdgpu_dm_prev_state {
++ struct drm_framebuffer *fb;
++ int32_t x;
++ int32_t y;
++ struct drm_display_mode mode;
++};
++
++struct common_irq_params {
++ struct amdgpu_device *adev;
++ enum dc_irq_source irq_src;
++};
++
++struct irq_list_head {
++ struct list_head head;
++ /* In case this interrupt needs post-processing, 'work' will be queued*/
++ struct work_struct work;
++};
++
++struct amdgpu_display_manager {
++ struct dal *dal;
++ struct dc *dc;
++ void *cgs_device;
++ /* lock to be used when DAL is called from SYNC IRQ context */
++ spinlock_t dal_lock;
++
++ struct amdgpu_device *adev; /*AMD base driver*/
++ struct drm_device *ddev; /*DRM base driver*/
++ u16 display_indexes_num;
++
++ struct amdgpu_dm_prev_state prev_state;
++
++ /*
++ * 'irq_source_handler_table' holds a list of handlers
++ * per (DAL) IRQ source.
++ *
++ * Each IRQ source may need to be handled at different contexts.
++ * By 'context' we mean, for example:
++ * - The ISR context, which is the direct interrupt handler.
++ * - The 'deferred' context - this is the post-processing of the
++ * interrupt, but at a lower priority.
++ *
++ * Note that handlers are called in the same order as they were
++ * registered (FIFO).
++ */
++ struct irq_list_head irq_handler_list_low_tab[DAL_IRQ_SOURCES_NUMBER];
++ struct list_head irq_handler_list_high_tab[DAL_IRQ_SOURCES_NUMBER];
++
++ struct common_irq_params
++ pflip_params[DC_IRQ_SOURCE_PFLIP_LAST - DC_IRQ_SOURCE_PFLIP_FIRST + 1];
++
++ struct common_irq_params
++ vupdate_params[DC_IRQ_SOURCE_VUPDATE6 - DC_IRQ_SOURCE_VUPDATE1 + 1];
++
++ /* this spin lock synchronizes access to 'irq_handler_list_table' */
++ spinlock_t irq_handler_list_table_lock;
++
++ /* Timer-related data. */
++ struct list_head timer_handler_list;
++ struct workqueue_struct *timer_workqueue;
++
++ /* Use dal_mutex for any activity which is NOT syncronized by
++ * DRM mode setting locks.
++ * For example: amdgpu_dm_hpd_low_irq() calls into DAL *without*
++ * DRM mode setting locks being acquired. This is where dal_mutex
++ * is acquired before calling into DAL. */
++ struct mutex dal_mutex;
++
++ struct backlight_device *backlight_dev;
++
++ const struct dc_link *backlight_link;
++};
++
++
++/* basic init/fini API */
++int amdgpu_dm_init(struct amdgpu_device *adev);
++
++void amdgpu_dm_fini(struct amdgpu_device *adev);
++
++void amdgpu_dm_destroy(void);
++
++/* initializes drm_device display related structures, based on the information
++ * provided by DAL. The drm strcutures are: drm_crtc, drm_connector,
++ * drm_encoder, drm_mode_config
++ *
++ * Returns 0 on success
++ */
++int amdgpu_dm_initialize_drm_device(
++ struct amdgpu_device *adev);
++
++/* removes and deallocates the drm structures, created by the above function */
++void amdgpu_dm_destroy_drm_device(
++ struct amdgpu_display_manager *dm);
++
++/* Locking/Mutex */
++bool amdgpu_dm_acquire_dal_lock(struct amdgpu_display_manager *dm);
++
++bool amdgpu_dm_release_dal_lock(struct amdgpu_display_manager *dm);
++
++/* Register "Backlight device" accessible by user-mode. */
++void amdgpu_dm_register_backlight_device(struct amdgpu_display_manager *dm);
++
++void amdgpu_dm_flip_cleanup(
++ struct amdgpu_device *adev,
++ struct amdgpu_crtc *acrtc);
++
++extern const struct amd_ip_funcs amdgpu_dm_funcs;
++
++void amdgpu_dm_update_connector_after_detect(
++ struct amdgpu_connector *aconnector);
++
++#endif /* __AMDGPU_DM_H__ */
+diff --git a/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_irq.c b/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_irq.c
+new file mode 100644
+index 0000000..9491fd0
+--- /dev/null
++++ b/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_irq.c
+@@ -0,0 +1,814 @@
++/*
++ * Copyright 2015 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 <drm/drmP.h>
++
++#include "dal_services_types.h"
++#include "dc.h"
++
++#include "amdgpu.h"
++#include "amdgpu_dm.h"
++#include "amdgpu_dm_irq.h"
++
++
++/******************************************************************************
++ * Private declarations.
++ *****************************************************************************/
++
++struct handler_common_data {
++ struct list_head list;
++ interrupt_handler handler;
++ void *handler_arg;
++
++ /* DM which this handler belongs to */
++ struct amdgpu_display_manager *dm;
++};
++
++struct amdgpu_dm_irq_handler_data {
++ struct handler_common_data hcd;
++ /* DAL irq source which registered for this interrupt. */
++ enum dc_irq_source irq_source;
++};
++
++struct amdgpu_dm_timer_handler_data {
++ struct handler_common_data hcd;
++ struct delayed_work d_work;
++};
++
++
++#define DM_IRQ_TABLE_LOCK(adev, flags) \
++ spin_lock_irqsave(&adev->dm.irq_handler_list_table_lock, flags)
++
++#define DM_IRQ_TABLE_UNLOCK(adev, flags) \
++ spin_unlock_irqrestore(&adev->dm.irq_handler_list_table_lock, flags)
++
++/******************************************************************************
++ * Private functions.
++ *****************************************************************************/
++
++static void init_handler_common_data(
++ struct handler_common_data *hcd,
++ void (*ih)(void *),
++ void *args,
++ struct amdgpu_display_manager *dm)
++{
++ hcd->handler = ih;
++ hcd->handler_arg = args;
++ hcd->dm = dm;
++}
++
++/**
++ * dm_irq_work_func - Handle an IRQ outside of the interrupt handler proper.
++ *
++ * @work: work struct
++ */
++static void dm_irq_work_func(struct work_struct *work)
++{
++ struct list_head *entry;
++ struct irq_list_head *irq_list_head =
++ container_of(work, struct irq_list_head, work);
++ struct list_head *handler_list = &irq_list_head->head;
++ struct amdgpu_dm_irq_handler_data *handler_data;
++
++ list_for_each(entry, handler_list) {
++ handler_data =
++ list_entry(
++ entry,
++ struct amdgpu_dm_irq_handler_data,
++ hcd.list);
++
++ DRM_DEBUG_KMS("DM_IRQ: work_func: for dal_src=%d\n",
++ handler_data->irq_source);
++
++ DRM_DEBUG_KMS("DM_IRQ: schedule_work: for dal_src=%d\n",
++ handler_data->irq_source);
++
++ handler_data->hcd.handler(handler_data->hcd.handler_arg);
++ }
++
++ /* Call a DAL subcomponent which registered for interrupt notification
++ * at INTERRUPT_LOW_IRQ_CONTEXT.
++ * (The most common use is HPD interrupt) */
++}
++
++/**
++ * Remove a handler and return a pointer to hander list from which the
++ * handler was removed.
++ */
++static struct list_head *remove_irq_handler(
++ struct amdgpu_device *adev,
++ void *ih,
++ const struct dc_interrupt_params *int_params)
++{
++ struct list_head *hnd_list;
++ struct list_head *entry, *tmp;
++ struct amdgpu_dm_irq_handler_data *handler;
++ unsigned long irq_table_flags;
++ bool handler_removed = false;
++ enum dc_irq_source irq_source;
++
++ DM_IRQ_TABLE_LOCK(adev, irq_table_flags);
++
++ irq_source = int_params->irq_source;
++
++ switch (int_params->int_context) {
++ case INTERRUPT_HIGH_IRQ_CONTEXT:
++ hnd_list = &adev->dm.irq_handler_list_high_tab[irq_source];
++ break;
++ case INTERRUPT_LOW_IRQ_CONTEXT:
++ default:
++ hnd_list = &adev->dm.irq_handler_list_low_tab[irq_source].head;
++ break;
++ }
++
++ list_for_each_safe(entry, tmp, hnd_list) {
++
++ handler = list_entry(entry, struct amdgpu_dm_irq_handler_data,
++ hcd.list);
++
++ if (ih == handler) {
++ /* Found our handler. Remove it from the list. */
++ list_del(&handler->hcd.list);
++ handler_removed = true;
++ break;
++ }
++ }
++
++ DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags);
++
++ if (handler_removed == false) {
++ /* Not necessarily an error - caller may not
++ * know the context. */
++ return NULL;
++ }
++
++ kfree(handler);
++
++ DRM_DEBUG_KMS(
++ "DM_IRQ: removed irq handler: %p for: dal_src=%d, irq context=%d\n",
++ ih, int_params->irq_source, int_params->int_context);
++
++ return hnd_list;
++}
++
++/* If 'handler_in == NULL' then remove ALL handlers. */
++static void remove_timer_handler(
++ struct amdgpu_device *adev,
++ struct amdgpu_dm_timer_handler_data *handler_in)
++{
++ struct amdgpu_dm_timer_handler_data *handler_temp;
++ struct list_head *handler_list;
++ struct list_head *entry, *tmp;
++ unsigned long irq_table_flags;
++ bool handler_removed = false;
++
++ DM_IRQ_TABLE_LOCK(adev, irq_table_flags);
++
++ handler_list = &adev->dm.timer_handler_list;
++
++ list_for_each_safe(entry, tmp, handler_list) {
++ /* Note that list_for_each_safe() guarantees that
++ * handler_temp is NOT null. */
++ handler_temp = list_entry(entry,
++ struct amdgpu_dm_timer_handler_data, hcd.list);
++
++ if (handler_in == NULL || handler_in == handler_temp) {
++ list_del(&handler_temp->hcd.list);
++ DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags);
++
++ DRM_DEBUG_KMS("DM_IRQ: removing timer handler: %p\n",
++ handler_temp);
++
++ if (handler_in == NULL) {
++ /* Since it is still in the queue, it must
++ * be cancelled. */
++ cancel_delayed_work_sync(&handler_temp->d_work);
++ }
++
++ kfree(handler_temp);
++ handler_removed = true;
++
++ DM_IRQ_TABLE_LOCK(adev, irq_table_flags);
++ }
++
++ if (handler_in == NULL) {
++ /* Remove ALL handlers. */
++ continue;
++ }
++
++ if (handler_in == handler_temp) {
++ /* Remove a SPECIFIC handler.
++ * Found our handler - we can stop here. */
++ break;
++ }
++ }
++
++ DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags);
++
++ if (handler_in != NULL && handler_removed == false) {
++ DRM_ERROR("DM_IRQ: handler: %p is not in the list!\n",
++ handler_in);
++ }
++}
++
++/**
++ * dm_timer_work_func - Handle a timer.
++ *
++ * @work: work struct
++ */
++static void dm_timer_work_func(
++ struct work_struct *work)
++{
++ struct amdgpu_dm_timer_handler_data *handler_data =
++ container_of(work, struct amdgpu_dm_timer_handler_data,
++ d_work.work);
++
++ DRM_DEBUG_KMS("DM_IRQ: work_func: handler_data=%p\n", handler_data);
++
++ /* Call a DAL subcomponent which registered for timer notification. */
++ handler_data->hcd.handler(handler_data->hcd.handler_arg);
++
++ /* We support only "single shot" timers. That means we must delete
++ * the handler after it was called. */
++ remove_timer_handler(handler_data->hcd.dm->adev, handler_data);
++}
++
++static bool validate_irq_registration_params(
++ struct dc_interrupt_params *int_params,
++ void (*ih)(void *))
++{
++ if (NULL == int_params || NULL == ih) {
++ DRM_ERROR("DM_IRQ: invalid input!\n");
++ return false;
++ }
++
++ if (int_params->int_context >= INTERRUPT_CONTEXT_NUMBER) {
++ DRM_ERROR("DM_IRQ: invalid context: %d!\n",
++ int_params->int_context);
++ return false;
++ }
++
++ if (!DAL_VALID_IRQ_SRC_NUM(int_params->irq_source)) {
++ DRM_ERROR("DM_IRQ: invalid irq_source: %d!\n",
++ int_params->irq_source);
++ return false;
++ }
++
++ return true;
++}
++
++static bool validate_irq_unregistration_params(
++ enum dc_irq_source irq_source,
++ irq_handler_idx handler_idx)
++{
++ if (DAL_INVALID_IRQ_HANDLER_IDX == handler_idx) {
++ DRM_ERROR("DM_IRQ: invalid handler_idx==NULL!\n");
++ return false;
++ }
++
++ if (!DAL_VALID_IRQ_SRC_NUM(irq_source)) {
++ DRM_ERROR("DM_IRQ: invalid irq_source:%d!\n", irq_source);
++ return false;
++ }
++
++ return true;
++}
++/******************************************************************************
++ * Public functions.
++ *
++ * Note: caller is responsible for input validation.
++ *****************************************************************************/
++
++void *amdgpu_dm_irq_register_interrupt(
++ struct amdgpu_device *adev,
++ struct dc_interrupt_params *int_params,
++ void (*ih)(void *),
++ void *handler_args)
++{
++ struct list_head *hnd_list;
++ struct amdgpu_dm_irq_handler_data *handler_data;
++ unsigned long irq_table_flags;
++ enum dc_irq_source irq_source;
++
++ if (false == validate_irq_registration_params(int_params, ih))
++ return DAL_INVALID_IRQ_HANDLER_IDX;
++
++ handler_data = kzalloc(sizeof(*handler_data), GFP_KERNEL);
++ if (!handler_data) {
++ DRM_ERROR("DM_IRQ: failed to allocate irq handler!\n");
++ return DAL_INVALID_IRQ_HANDLER_IDX;
++ }
++
++ memset(handler_data, 0, sizeof(*handler_data));
++
++ init_handler_common_data(&handler_data->hcd, ih, handler_args,
++ &adev->dm);
++
++ irq_source = int_params->irq_source;
++
++ handler_data->irq_source = irq_source;
++
++ /* Lock the list, add the handler. */
++ DM_IRQ_TABLE_LOCK(adev, irq_table_flags);
++
++ switch (int_params->int_context) {
++ case INTERRUPT_HIGH_IRQ_CONTEXT:
++ hnd_list = &adev->dm.irq_handler_list_high_tab[irq_source];
++ break;
++ case INTERRUPT_LOW_IRQ_CONTEXT:
++ default:
++ hnd_list = &adev->dm.irq_handler_list_low_tab[irq_source].head;
++ break;
++ }
++
++ list_add_tail(&handler_data->hcd.list, hnd_list);
++
++ DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags);
++
++ /* This pointer will be stored by code which requested interrupt
++ * registration.
++ * The same pointer will be needed in order to unregister the
++ * interrupt. */
++
++ DRM_DEBUG_KMS(
++ "DM_IRQ: added irq handler: %p for: dal_src=%d, irq context=%d\n",
++ handler_data,
++ irq_source,
++ int_params->int_context);
++
++ return handler_data;
++}
++
++void amdgpu_dm_irq_unregister_interrupt(
++ struct amdgpu_device *adev,
++ enum dc_irq_source irq_source,
++ void *ih)
++{
++ struct list_head *handler_list;
++ struct dc_interrupt_params int_params;
++ int i;
++
++ if (false == validate_irq_unregistration_params(irq_source, ih))
++ return;
++
++ memset(&int_params, 0, sizeof(int_params));
++
++ int_params.irq_source = irq_source;
++
++ for (i = 0; i < INTERRUPT_CONTEXT_NUMBER; i++) {
++
++ int_params.int_context = i;
++
++ handler_list = remove_irq_handler(adev, ih, &int_params);
++
++ if (handler_list != NULL)
++ break;
++ }
++
++ if (handler_list == NULL) {
++ /* If we got here, it means we searched all irq contexts
++ * for this irq source, but the handler was not found. */
++ DRM_ERROR(
++ "DM_IRQ: failed to find irq handler:%p for irq_source:%d!\n",
++ ih, irq_source);
++ }
++}
++
++int amdgpu_dm_irq_init(
++ struct amdgpu_device *adev)
++{
++ int src;
++ struct irq_list_head *lh;
++
++ DRM_DEBUG_KMS("DM_IRQ\n");
++
++ spin_lock_init(&adev->dm.irq_handler_list_table_lock);
++
++ for (src = 0; src < DAL_IRQ_SOURCES_NUMBER; src++) {
++ /* low context handler list init */
++ lh = &adev->dm.irq_handler_list_low_tab[src];
++ INIT_LIST_HEAD(&lh->head);
++ INIT_WORK(&lh->work, dm_irq_work_func);
++
++ /* high context handler init */
++ INIT_LIST_HEAD(&adev->dm.irq_handler_list_high_tab[src]);
++ }
++
++ INIT_LIST_HEAD(&adev->dm.timer_handler_list);
++
++ /* allocate and initialize the workqueue for DM timer */
++ adev->dm.timer_workqueue = create_singlethread_workqueue(
++ "dm_timer_queue");
++ if (adev->dm.timer_workqueue == NULL) {
++ DRM_ERROR("DM_IRQ: unable to create timer queue!\n");
++ return -1;
++ }
++
++ return 0;
++}
++
++void amdgpu_dm_irq_register_timer(
++ struct amdgpu_device *adev,
++ struct dc_timer_interrupt_params *int_params,
++ interrupt_handler ih,
++ void *args)
++{
++ unsigned long jf_delay;
++ struct list_head *handler_list;
++ struct amdgpu_dm_timer_handler_data *handler_data;
++ unsigned long irq_table_flags;
++
++ handler_data = kzalloc(sizeof(*handler_data), GFP_KERNEL);
++ if (!handler_data) {
++ DRM_ERROR("DM_IRQ: failed to allocate timer handler!\n");
++ return;
++ }
++
++ memset(handler_data, 0, sizeof(*handler_data));
++
++ init_handler_common_data(&handler_data->hcd, ih, args, &adev->dm);
++
++ INIT_DELAYED_WORK(&handler_data->d_work, dm_timer_work_func);
++
++ /* Lock the list, add the handler. */
++ DM_IRQ_TABLE_LOCK(adev, irq_table_flags);
++
++ handler_list = &adev->dm.timer_handler_list;
++
++ list_add_tail(&handler_data->hcd.list, handler_list);
++
++ DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags);
++
++ jf_delay = usecs_to_jiffies(int_params->micro_sec_interval);
++
++ queue_delayed_work(adev->dm.timer_workqueue, &handler_data->d_work,
++ jf_delay);
++
++ DRM_DEBUG_KMS("DM_IRQ: added handler:%p with micro_sec_interval=%llu\n",
++ handler_data, int_params->micro_sec_interval);
++ return;
++}
++
++/* DM IRQ and timer resource release */
++void amdgpu_dm_irq_fini(
++ struct amdgpu_device *adev)
++{
++ int src;
++ struct irq_list_head *lh;
++ DRM_DEBUG_KMS("DM_IRQ: releasing resources.\n");
++
++ for (src = 0; src < DAL_IRQ_SOURCES_NUMBER; src++) {
++
++ /* The handler was removed from the table,
++ * it means it is safe to flush all the 'work'
++ * (because no code can schedule a new one). */
++ lh = &adev->dm.irq_handler_list_low_tab[src];
++ flush_work(&lh->work);
++ }
++
++ /* Cancel ALL timers and release handlers (if any). */
++ remove_timer_handler(adev, NULL);
++ /* Release the queue itself. */
++ destroy_workqueue(adev->dm.timer_workqueue);
++}
++
++int amdgpu_dm_irq_suspend(
++ struct amdgpu_device *adev)
++{
++ int src;
++ struct list_head *hnd_list_h;
++ struct list_head *hnd_list_l;
++ unsigned long irq_table_flags;
++
++ DM_IRQ_TABLE_LOCK(adev, irq_table_flags);
++
++ DRM_DEBUG_KMS("DM_IRQ: suspend\n");
++
++ /* disable HW interrupt */
++ for (src = DC_IRQ_SOURCE_HPD1; src < DAL_IRQ_SOURCES_NUMBER; src++) {
++ hnd_list_l = &adev->dm.irq_handler_list_low_tab[src].head;
++ hnd_list_h = &adev->dm.irq_handler_list_high_tab[src];
++ if (!list_empty(hnd_list_l) || !list_empty(hnd_list_h))
++ dc_interrupt_set(adev->dm.dc, src, false);
++
++ DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags);
++ flush_work(&adev->dm.irq_handler_list_low_tab[src].work);
++
++ DM_IRQ_TABLE_LOCK(adev, irq_table_flags);
++ }
++
++ DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags);
++
++ return 0;
++}
++
++int amdgpu_dm_irq_resume(
++ struct amdgpu_device *adev)
++{
++ int src;
++ struct list_head *hnd_list_h, *hnd_list_l;
++ unsigned long irq_table_flags;
++
++ DM_IRQ_TABLE_LOCK(adev, irq_table_flags);
++
++ DRM_DEBUG_KMS("DM_IRQ: resume\n");
++
++ /* re-enable HW interrupt */
++ for (src = DC_IRQ_SOURCE_HPD1; src < DAL_IRQ_SOURCES_NUMBER; src++) {
++ hnd_list_l = &adev->dm.irq_handler_list_low_tab[src].head;
++ hnd_list_h = &adev->dm.irq_handler_list_high_tab[src];
++ if (!list_empty(hnd_list_l) || !list_empty(hnd_list_h))
++ dc_interrupt_set(adev->dm.dc, src, true);
++ }
++
++ DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags);
++
++ return 0;
++}
++
++
++/**
++ * amdgpu_dm_irq_schedule_work - schedule all work items registered for the
++ * "irq_source".
++ */
++static void amdgpu_dm_irq_schedule_work(
++ struct amdgpu_device *adev,
++ enum dc_irq_source irq_source)
++{
++ unsigned long irq_table_flags;
++
++ DM_IRQ_TABLE_LOCK(adev, irq_table_flags);
++
++ /* Since the caller is interested in 'work_struct' then
++ * the irq will be post-processed at "INTERRUPT_LOW_IRQ_CONTEXT". */
++
++ schedule_work(&adev->dm.irq_handler_list_low_tab[irq_source].work);
++
++ DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags);
++}
++
++/** amdgpu_dm_irq_immediate_work
++ * Callback high irq work immediately, don't send to work queue
++ */
++static void amdgpu_dm_irq_immediate_work(
++ struct amdgpu_device *adev,
++ enum dc_irq_source irq_source)
++{
++ struct amdgpu_dm_irq_handler_data *handler_data;
++ struct list_head *entry;
++ unsigned long irq_table_flags;
++
++ DM_IRQ_TABLE_LOCK(adev, irq_table_flags);
++
++ list_for_each(
++ entry,
++ &adev->dm.irq_handler_list_high_tab[irq_source]) {
++
++ handler_data =
++ list_entry(
++ entry,
++ struct amdgpu_dm_irq_handler_data,
++ hcd.list);
++
++ /* Call a subcomponent which registered for immediate
++ * interrupt notification */
++ handler_data->hcd.handler(handler_data->hcd.handler_arg);
++ }
++
++ DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags);
++}
++
++/*
++ * amdgpu_dm_irq_handler
++ *
++ * Generic IRQ handler, calls all registered high irq work immediately, and
++ * schedules work for low irq
++ */
++int amdgpu_dm_irq_handler(
++ struct amdgpu_device *adev,
++ struct amdgpu_irq_src *source,
++ struct amdgpu_iv_entry *entry)
++{
++
++ enum dc_irq_source src =
++ dc_interrupt_to_irq_source(
++ adev->dm.dc,
++ entry->src_id,
++ entry->src_data);
++
++ dc_interrupt_ack(adev->dm.dc, src);
++
++ /* Call high irq work immediately */
++ amdgpu_dm_irq_immediate_work(adev, src);
++ /*Schedule low_irq work */
++ amdgpu_dm_irq_schedule_work(adev, src);
++
++ return 0;
++}
++
++static enum dc_irq_source amdgpu_dm_hpd_to_dal_irq_source(unsigned type)
++{
++ switch (type) {
++ case AMDGPU_HPD_1:
++ return DC_IRQ_SOURCE_HPD1;
++ case AMDGPU_HPD_2:
++ return DC_IRQ_SOURCE_HPD2;
++ case AMDGPU_HPD_3:
++ return DC_IRQ_SOURCE_HPD3;
++ case AMDGPU_HPD_4:
++ return DC_IRQ_SOURCE_HPD4;
++ case AMDGPU_HPD_5:
++ return DC_IRQ_SOURCE_HPD5;
++ case AMDGPU_HPD_6:
++ return DC_IRQ_SOURCE_HPD6;
++ default:
++ return DC_IRQ_SOURCE_INVALID;
++ }
++}
++
++static int amdgpu_dm_set_hpd_irq_state(struct amdgpu_device *adev,
++ struct amdgpu_irq_src *source,
++ unsigned type,
++ enum amdgpu_interrupt_state state)
++{
++ enum dc_irq_source src = amdgpu_dm_hpd_to_dal_irq_source(type);
++ bool st = (state == AMDGPU_IRQ_STATE_ENABLE);
++
++ dc_interrupt_set(adev->dm.dc, src, st);
++ return 0;
++}
++
++static inline int dm_irq_state(
++ struct amdgpu_device *adev,
++ struct amdgpu_irq_src *source,
++ unsigned crtc_id,
++ enum amdgpu_interrupt_state state,
++ const enum irq_type dal_irq_type,
++ const char *func)
++{
++ bool st;
++ enum dc_irq_source irq_source;
++
++ struct amdgpu_crtc *acrtc = adev->mode_info.crtcs[crtc_id];
++
++ if (!acrtc->target) {
++ DRM_INFO(
++ "%s: target is null for crtc %d, talk to David R\n",
++ func,
++ crtc_id);
++ WARN_ON(true);
++ return 0;
++ }
++
++ irq_source = dc_target_get_irq_src(acrtc->target, dal_irq_type);
++
++ st = (state == AMDGPU_IRQ_STATE_ENABLE);
++
++ dc_interrupt_set(adev->dm.dc, irq_source, st);
++ return 0;
++}
++
++static int amdgpu_dm_set_pflip_irq_state(struct amdgpu_device *adev,
++ struct amdgpu_irq_src *source,
++ unsigned crtc_id,
++ enum amdgpu_interrupt_state state)
++{
++ return dm_irq_state(
++ adev,
++ source,
++ crtc_id,
++ state,
++ IRQ_TYPE_PFLIP,
++ __func__);
++}
++
++static int amdgpu_dm_set_crtc_irq_state(struct amdgpu_device *adev,
++ struct amdgpu_irq_src *source,
++ unsigned crtc_id,
++ enum amdgpu_interrupt_state state)
++{
++ return dm_irq_state(
++ adev,
++ source,
++ crtc_id,
++ state,
++ IRQ_TYPE_VUPDATE,
++ __func__);
++}
++
++static const struct amdgpu_irq_src_funcs dm_crtc_irq_funcs = {
++ .set = amdgpu_dm_set_crtc_irq_state,
++ .process = amdgpu_dm_irq_handler,
++};
++
++static const struct amdgpu_irq_src_funcs dm_pageflip_irq_funcs = {
++ .set = amdgpu_dm_set_pflip_irq_state,
++ .process = amdgpu_dm_irq_handler,
++};
++
++static const struct amdgpu_irq_src_funcs dm_hpd_irq_funcs = {
++ .set = amdgpu_dm_set_hpd_irq_state,
++ .process = amdgpu_dm_irq_handler,
++};
++
++void amdgpu_dm_set_irq_funcs(struct amdgpu_device *adev)
++{
++ adev->crtc_irq.num_types = AMDGPU_CRTC_IRQ_LAST;
++ adev->crtc_irq.funcs = &dm_crtc_irq_funcs;
++
++ adev->pageflip_irq.num_types = AMDGPU_PAGEFLIP_IRQ_LAST;
++ adev->pageflip_irq.funcs = &dm_pageflip_irq_funcs;
++
++ adev->hpd_irq.num_types = AMDGPU_HPD_LAST;
++ adev->hpd_irq.funcs = &dm_hpd_irq_funcs;
++}
++
++/*
++ * amdgpu_dm_hpd_init - hpd setup callback.
++ *
++ * @adev: amdgpu_device pointer
++ *
++ * Setup the hpd pins used by the card (evergreen+).
++ * Enable the pin, set the polarity, and enable the hpd interrupts.
++ */
++void amdgpu_dm_hpd_init(struct amdgpu_device *adev)
++{
++ struct drm_device *dev = adev->ddev;
++ struct drm_connector *connector;
++
++ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
++ struct amdgpu_connector *amdgpu_connector =
++ to_amdgpu_connector(connector);
++ enum dc_irq_source src =
++ amdgpu_dm_hpd_to_dal_irq_source(
++ amdgpu_connector->hpd.hpd);
++ const struct dc_link *dc_link = amdgpu_connector->dc_link;
++
++ if (connector->connector_type == DRM_MODE_CONNECTOR_eDP ||
++ connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
++ /* don't try to enable hpd on eDP or LVDS avoid breaking
++ * the aux dp channel on imac and help (but not
++ * completely fix)
++ * https://bugzilla.redhat.com/show_bug.cgi?id=726143
++ * also avoid interrupt storms during dpms.
++ */
++ continue;
++ }
++
++ dc_interrupt_set(adev->dm.dc, src, true);
++ amdgpu_irq_get(adev, &adev->hpd_irq, amdgpu_connector->hpd.hpd);
++
++ if (DC_IRQ_SOURCE_INVALID != dc_link->irq_source_hpd_rx) {
++ dc_interrupt_set(adev->dm.dc,
++ dc_link->irq_source_hpd_rx,
++ true);
++ }
++ }
++}
++
++/**
++ * amdgpu_dm_hpd_fini - hpd tear down callback.
++ *
++ * @adev: amdgpu_device pointer
++ *
++ * Tear down the hpd pins used by the card (evergreen+).
++ * Disable the hpd interrupts.
++ */
++void amdgpu_dm_hpd_fini(struct amdgpu_device *adev)
++{
++ struct drm_device *dev = adev->ddev;
++ struct drm_connector *connector;
++
++ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
++ struct amdgpu_connector *amdgpu_connector =
++ to_amdgpu_connector(connector);
++ enum dc_irq_source src =
++ amdgpu_dm_hpd_to_dal_irq_source(
++ amdgpu_connector->hpd.hpd);
++
++ dc_interrupt_set(adev->dm.dc, src, false);
++ amdgpu_irq_put(adev, &adev->hpd_irq, amdgpu_connector->hpd.hpd);
++ }
++}
+diff --git a/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_irq.h b/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_irq.h
+new file mode 100644
+index 0000000..afedb50
+--- /dev/null
++++ b/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_irq.h
+@@ -0,0 +1,122 @@
++/*
++ * Copyright 2015 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.
++ *
++ */
++
++#ifndef __AMDGPU_DM_IRQ_H__
++#define __AMDGPU_DM_IRQ_H__
++
++#include "irq_types.h" /* DAL irq definitions */
++
++/*
++ * Display Manager IRQ-related interfaces (for use by DAL).
++ */
++
++/**
++ * amdgpu_dm_irq_init - Initialize internal structures of 'amdgpu_dm_irq'.
++ *
++ * This function should be called exactly once - during DM initialization.
++ *
++ * Returns:
++ * 0 - success
++ * non-zero - error
++ */
++int amdgpu_dm_irq_init(
++ struct amdgpu_device *adev);
++
++/**
++ * amdgpu_dm_irq_fini - deallocate internal structures of 'amdgpu_dm_irq'.
++ *
++ * This function should be called exactly once - during DM destruction.
++ *
++ */
++void amdgpu_dm_irq_fini(
++ struct amdgpu_device *adev);
++
++/**
++ * amdgpu_dm_irq_register_interrupt - register irq handler for Display block.
++ *
++ * @adev: AMD DRM device
++ * @int_params: parameters for the irq
++ * @ih: pointer to the irq hander function
++ * @handler_args: arguments which will be passed to ih
++ *
++ * Returns:
++ * IRQ Handler Index on success.
++ * NULL on failure.
++ *
++ * Cannot be called from an interrupt handler.
++ */
++void *amdgpu_dm_irq_register_interrupt(
++ struct amdgpu_device *adev,
++ struct dc_interrupt_params *int_params,
++ void (*ih)(void *),
++ void *handler_args);
++
++/**
++ * amdgpu_dm_irq_unregister_interrupt - unregister handler which was registered
++ * by amdgpu_dm_irq_register_interrupt().
++ *
++ * @adev: AMD DRM device.
++ * @ih_index: irq handler index which was returned by
++ * amdgpu_dm_irq_register_interrupt
++ */
++void amdgpu_dm_irq_unregister_interrupt(
++ struct amdgpu_device *adev,
++ enum dc_irq_source irq_source,
++ void *ih_index);
++
++void amdgpu_dm_irq_register_timer(
++ struct amdgpu_device *adev,
++ struct dc_timer_interrupt_params *int_params,
++ interrupt_handler ih,
++ void *args);
++
++/**
++ * amdgpu_dm_irq_handler
++ * Generic IRQ handler, calls all registered high irq work immediately, and
++ * schedules work for low irq
++ */
++int amdgpu_dm_irq_handler(
++ struct amdgpu_device *adev,
++ struct amdgpu_irq_src *source,
++ struct amdgpu_iv_entry *entry);
++
++void amdgpu_dm_set_irq_funcs(struct amdgpu_device *adev);
++
++void amdgpu_dm_hpd_init(struct amdgpu_device *adev);
++void amdgpu_dm_hpd_fini(struct amdgpu_device *adev);
++
++/**
++ * amdgpu_dm_irq_suspend - disable ASIC interrupt during suspend.
++ *
++ */
++int amdgpu_dm_irq_suspend(
++ struct amdgpu_device *adev);
++
++/**
++ * amdgpu_dm_irq_resume - enable ASIC interrupt during resume.
++ *
++ */
++int amdgpu_dm_irq_resume(
++ struct amdgpu_device *adev);
++
++#endif /* __AMDGPU_DM_IRQ_H__ */
+diff --git a/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_mst_types.c
+new file mode 100644
+index 0000000..6d9ee15
+--- /dev/null
++++ b/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_mst_types.c
+@@ -0,0 +1,353 @@
++/*
++ * 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 "amdgpu.h"
++
++#include "amdgpu_dm_types.h"
++
++#include "amdgpu_dm_mst_types.h"
++
++#include "dc.h"
++
++static ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
++{
++ struct pci_dev *pdev = to_pci_dev(aux->dev);
++ struct drm_device *drm_dev = pci_get_drvdata(pdev);
++ struct amdgpu_device *adev = drm_dev->dev_private;
++ struct dc *dc = adev->dm.dc;
++
++ switch (msg->request) {
++ case DP_AUX_NATIVE_READ:
++ dc_read_dpcd(
++ dc,
++ TO_DM_AUX(aux)->link_index,
++ msg->address,
++ msg->buffer,
++ msg->size);
++ break;
++ case DP_AUX_NATIVE_WRITE:
++ dc_write_dpcd(
++ dc,
++ TO_DM_AUX(aux)->link_index,
++ msg->address,
++ msg->buffer,
++ msg->size);
++ break;
++ default:
++ return 0;
++ }
++
++ return msg->size;
++}
++
++static enum drm_connector_status
++dm_dp_mst_detect(struct drm_connector *connector, bool force)
++{
++ struct amdgpu_connector *aconnector = to_amdgpu_connector(connector);
++ struct amdgpu_connector *master = aconnector->mst_port;
++
++ return drm_dp_mst_detect_port(connector, &master->mst_mgr, aconnector->port);
++}
++
++static void
++dm_dp_mst_connector_destroy(struct drm_connector *connector)
++{
++ struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
++ struct amdgpu_encoder *amdgpu_encoder = amdgpu_connector->mst_encoder;
++
++ drm_encoder_cleanup(&amdgpu_encoder->base);
++ kfree(amdgpu_encoder);
++ drm_connector_cleanup(connector);
++ kfree(amdgpu_connector);
++}
++
++static int dm_dp_mst_connector_dpms(struct drm_connector *connector, int mode)
++{
++ DRM_DEBUG_KMS("\n");
++ return 0;
++}
++
++static const struct drm_connector_funcs dm_dp_mst_connector_funcs = {
++ .dpms = dm_dp_mst_connector_dpms,
++ .detect = dm_dp_mst_detect,
++ .fill_modes = drm_helper_probe_single_connector_modes,
++ .destroy = dm_dp_mst_connector_destroy,
++ .reset = amdgpu_dm_connector_funcs_reset,
++ .atomic_duplicate_state = amdgpu_dm_connector_atomic_duplicate_state,
++ .atomic_destroy_state = amdgpu_dm_connector_atomic_destroy_state,
++ .atomic_set_property = amdgpu_dm_connector_atomic_set_property
++};
++
++static const struct dc_sink *dm_dp_mst_add_mst_sink(
++ struct dc_link *dc_link,
++ uint8_t *edid,
++ uint16_t len)
++{
++ struct dc_sink *dc_sink;
++ struct sink_init_data init_params = {
++ .link = dc_link,
++ .sink_signal = SIGNAL_TYPE_DISPLAY_PORT_MST};
++
++ if (len > MAX_EDID_BUFFER_SIZE) {
++ DRM_ERROR("Max EDID buffer size breached!\n");
++ return NULL;
++ }
++
++ /*
++ * TODO make dynamic-ish?
++ * dc_link->connector_signal;
++ */
++
++ dc_sink = sink_create(&init_params);
++
++ if (!dc_sink)
++ return NULL;
++
++ dc_service_memmove(dc_sink->dc_edid.raw_edid, edid, len);
++ dc_sink->dc_edid.length = len;
++
++ if (!dc_link_add_sink(
++ dc_link,
++ dc_sink))
++ goto fail;
++
++ /* dc_sink_retain(&core_sink->public); */
++
++ return dc_sink;
++
++fail:
++ dc_link_remove_sink(dc_link, dc_sink);
++ return NULL;
++}
++
++static int dm_dp_mst_get_modes(struct drm_connector *connector)
++{
++ struct amdgpu_connector *aconnector = to_amdgpu_connector(connector);
++ struct amdgpu_connector *master = aconnector->mst_port;
++ struct edid *edid;
++ const struct dc_sink *sink;
++ int ret = 0;
++
++ edid = drm_dp_mst_get_edid(connector, &master->mst_mgr, aconnector->port);
++
++ if (!edid) {
++ drm_mode_connector_update_edid_property(
++ &aconnector->base,
++ NULL);
++
++ return ret;
++ }
++
++ aconnector->edid = edid;
++
++ if (!aconnector->dc_sink) {
++ sink = dm_dp_mst_add_mst_sink(
++ (struct dc_link *)aconnector->dc_link,
++ (uint8_t *)edid,
++ (edid->extensions + 1) * EDID_LENGTH);
++ aconnector->dc_sink = sink;
++ }
++
++ DRM_DEBUG_KMS("edid retrieved %p\n", edid);
++
++ drm_mode_connector_update_edid_property(
++ &aconnector->base,
++ aconnector->edid);
++
++ ret = drm_add_edid_modes(&aconnector->base, aconnector->edid);
++
++ drm_edid_to_eld(&aconnector->base, aconnector->edid);
++
++ return ret;
++}
++
++static enum drm_mode_status
++dm_dp_mst_mode_valid(struct drm_connector *connector,
++ struct drm_display_mode *mode)
++{
++ return MODE_OK;
++}
++
++static struct drm_encoder *dm_mst_best_encoder(struct drm_connector *connector)
++{
++ struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
++
++ return &amdgpu_connector->mst_encoder->base;
++}
++
++static const struct drm_connector_helper_funcs dm_dp_mst_connector_helper_funcs = {
++ .get_modes = dm_dp_mst_get_modes,
++ .mode_valid = dm_dp_mst_mode_valid,
++ .best_encoder = dm_mst_best_encoder,
++};
++
++static struct amdgpu_encoder *
++dm_dp_create_fake_mst_encoder(struct amdgpu_connector *connector)
++{
++ struct drm_device *dev = connector->base.dev;
++ struct amdgpu_device *adev = dev->dev_private;
++ struct amdgpu_encoder *amdgpu_encoder;
++ struct drm_encoder *encoder;
++ const struct drm_connector_helper_funcs *connector_funcs =
++ connector->base.helper_private;
++ struct drm_encoder *enc_master =
++ connector_funcs->best_encoder(&connector->base);
++
++ DRM_DEBUG_KMS("enc master is %p\n", enc_master);
++ amdgpu_encoder = kzalloc(sizeof(*amdgpu_encoder), GFP_KERNEL);
++ if (!amdgpu_encoder)
++ return NULL;
++
++ encoder = &amdgpu_encoder->base;
++ switch (adev->mode_info.num_crtc) {
++ case 1:
++ encoder->possible_crtcs = 0x1;
++ break;
++ case 2:
++ default:
++ encoder->possible_crtcs = 0x3;
++ break;
++ case 4:
++ encoder->possible_crtcs = 0xf;
++ break;
++ case 6:
++ encoder->possible_crtcs = 0x3f;
++ break;
++ }
++
++ encoder->possible_crtcs = 0x1;
++
++ drm_encoder_init(
++ dev,
++ &amdgpu_encoder->base,
++ NULL,
++ DRM_MODE_ENCODER_DPMST,
++ NULL);
++
++ drm_encoder_helper_add(encoder, &amdgpu_dm_encoder_helper_funcs);
++
++ return amdgpu_encoder;
++}
++
++static struct drm_connector *dm_dp_add_mst_connector(struct drm_dp_mst_topology_mgr *mgr,
++ struct drm_dp_mst_port *port,
++ const char *pathprop)
++{
++ struct amdgpu_connector *master = container_of(mgr, struct amdgpu_connector, mst_mgr);
++ struct drm_device *dev = master->base.dev;
++ struct amdgpu_device *adev = dev->dev_private;
++ struct amdgpu_connector *aconnector;
++ struct drm_connector *connector;
++
++ aconnector = kzalloc(sizeof(*aconnector), GFP_KERNEL);
++ if (!aconnector)
++ return NULL;
++
++ aconnector->is_mst_connector = true;
++ connector = &aconnector->base;
++ aconnector->port = port;
++ aconnector->mst_port = master;
++ aconnector->dc_link = master->dc_link;
++
++ /* Initialize connector state before adding the connectror to drm and framebuffer lists */
++ amdgpu_dm_connector_funcs_reset(connector);
++
++ drm_connector_init(dev, connector, &dm_dp_mst_connector_funcs, DRM_MODE_CONNECTOR_DisplayPort);
++ drm_connector_helper_add(connector, &dm_dp_mst_connector_helper_funcs);
++ aconnector->mst_encoder = dm_dp_create_fake_mst_encoder(master);
++
++ drm_object_attach_property(&connector->base, dev->mode_config.path_property, 0);
++ drm_mode_connector_set_path_property(connector, pathprop);
++
++ mutex_lock(&dev->mode_config.mutex);
++ drm_fb_helper_add_one_connector(&adev->mode_info.rfbdev->helper, connector);
++ mutex_unlock(&dev->mode_config.mutex);
++
++ drm_connector_register(connector);
++
++ DRM_DEBUG_KMS(":%d\n", connector->base.id);
++
++ return connector;
++}
++
++static void dm_dp_destroy_mst_connector(
++ struct drm_dp_mst_topology_mgr *mgr,
++ struct drm_connector *connector)
++{
++ struct amdgpu_connector *master =
++ container_of(mgr, struct amdgpu_connector, mst_mgr);
++ struct drm_device *dev = master->base.dev;
++ struct amdgpu_device *adev = dev->dev_private;
++ drm_connector_unregister(connector);
++ /* need to nuke the connector */
++ mutex_lock(&dev->mode_config.mutex);
++ /* dpms off */
++ drm_fb_helper_remove_one_connector(
++ &adev->mode_info.rfbdev->helper,
++ connector);
++
++ drm_connector_cleanup(connector);
++ mutex_unlock(&dev->mode_config.mutex);
++
++ kfree(connector);
++ DRM_DEBUG_KMS("\n");
++}
++
++static void dm_dp_mst_hotplug(struct drm_dp_mst_topology_mgr *mgr)
++{
++ struct amdgpu_connector *master = container_of(mgr, struct amdgpu_connector, mst_mgr);
++ struct drm_device *dev = master->base.dev;
++
++ drm_kms_helper_hotplug_event(dev);
++}
++
++struct drm_dp_mst_topology_cbs dm_mst_cbs = {
++ .add_connector = dm_dp_add_mst_connector,
++ .destroy_connector = dm_dp_destroy_mst_connector,
++ .hotplug = dm_dp_mst_hotplug,
++};
++
++void amdgpu_dm_initialize_mst_connector(
++ struct amdgpu_display_manager *dm,
++ struct amdgpu_connector *aconnector)
++{
++ aconnector->dm_dp_aux.aux.name = "dmdc";
++ aconnector->dm_dp_aux.aux.dev = dm->adev->dev;
++ aconnector->dm_dp_aux.aux.transfer = dm_dp_aux_transfer;
++ aconnector->dm_dp_aux.link_index = aconnector->connector_id;
++
++ drm_dp_aux_register(&aconnector->dm_dp_aux.aux);
++
++ aconnector->mst_mgr.cbs = &dm_mst_cbs;
++ drm_dp_mst_topology_mgr_init(
++ &aconnector->mst_mgr,
++ dm->adev->dev,
++ &aconnector->dm_dp_aux.aux,
++ 16,
++ 4,
++ aconnector->connector_id);
++}
+diff --git a/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_mst_types.h b/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_mst_types.h
+new file mode 100644
+index 0000000..6130d62
+--- /dev/null
++++ b/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_mst_types.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_AMDGPU_DM_MST_TYPES_H__
++#define __DAL_AMDGPU_DM_MST_TYPES_H__
++
++struct amdgpu_display_manager;
++struct amdgpu_connector;
++
++void amdgpu_dm_initialize_mst_connector(
++ struct amdgpu_display_manager *dm,
++ struct amdgpu_connector *aconnector);
++
++#endif
+diff --git a/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_types.c b/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_types.c
+new file mode 100644
+index 0000000..bfff48c
+--- /dev/null
++++ b/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_types.c
+@@ -0,0 +1,2390 @@
++/*
++ * Copyright 2012-13 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_types.h"
++
++#include <linux/types.h>
++#include <drm/drmP.h>
++#include <drm/drm_atomic_helper.h>
++#include <drm/drm_fb_helper.h>
++#include <drm/drm_atomic.h>
++#include "amdgpu.h"
++#include "amdgpu_pm.h"
++// We need to #undef FRAME_SIZE and DEPRECATED because they conflict
++// with ptrace-abi.h's #define's of them.
++#undef FRAME_SIZE
++#undef DEPRECATED
++
++#include "mode_query_interface.h"
++#include "dcs_types.h"
++#include "mode_manager_types.h"
++
++/*#include "amdgpu_buffer.h"*/
++
++#include "dce/dce_11_0_d.h"
++#include "dce/dce_11_0_sh_mask.h"
++#include "dce/dce_11_0_enum.h"
++
++#include "dc.h"
++
++#include "amdgpu_dm_types.h"
++#include "amdgpu_dm_mst_types.h"
++
++struct dm_connector_state {
++ struct drm_connector_state base;
++
++ enum amdgpu_rmx_type scaling;
++ uint8_t underscan_vborder;
++ uint8_t underscan_hborder;
++ bool underscan_enable;
++};
++
++#define to_dm_connector_state(x)\
++ container_of((x), struct dm_connector_state, base)
++
++#define AMDGPU_CRTC_MODE_PRIVATE_FLAGS_GAMMASET 1
++
++void amdgpu_dm_encoder_destroy(struct drm_encoder *encoder)
++{
++ drm_encoder_cleanup(encoder);
++ kfree(encoder);
++}
++
++static const struct drm_encoder_funcs amdgpu_dm_encoder_funcs = {
++ .destroy = amdgpu_dm_encoder_destroy,
++};
++
++static void dm_set_cursor(
++ struct amdgpu_crtc *amdgpu_crtc,
++ uint64_t gpu_addr,
++ uint32_t width,
++ uint32_t height)
++{
++ struct dc_cursor_attributes attributes;
++ amdgpu_crtc->cursor_width = width;
++ amdgpu_crtc->cursor_height = height;
++
++ attributes.address.high_part = upper_32_bits(gpu_addr);
++ attributes.address.low_part = lower_32_bits(gpu_addr);
++ attributes.width = width-1;
++ attributes.height = height-1;
++ attributes.x_hot = 0;
++ attributes.y_hot = 0;
++ attributes.color_format = CURSOR_MODE_COLOR_PRE_MULTIPLIED_ALPHA;
++ attributes.rotation_angle = 0;
++ attributes.attribute_flags.value = 0;
++
++ if (!dc_target_set_cursor_attributes(
++ amdgpu_crtc->target,
++ &attributes)) {
++ DRM_ERROR("DC failed to set cursor attributes\n");
++ }
++}
++
++static int dm_crtc_unpin_cursor_bo_old(
++ struct amdgpu_crtc *amdgpu_crtc)
++{
++ struct amdgpu_bo *robj;
++ int ret = 0;
++
++ if (NULL != amdgpu_crtc && NULL != amdgpu_crtc->cursor_bo) {
++ robj = gem_to_amdgpu_bo(amdgpu_crtc->cursor_bo);
++
++ ret = amdgpu_bo_reserve(robj, false);
++
++ if (likely(ret == 0)) {
++ amdgpu_bo_unpin(robj);
++ amdgpu_bo_unreserve(robj);
++ }
++ } else {
++ DRM_ERROR("dm_crtc_unpin_cursor_ob_old bo %x, leaked %p\n",
++ ret,
++ amdgpu_crtc->cursor_bo);
++ }
++
++ drm_gem_object_unreference_unlocked(amdgpu_crtc->cursor_bo);
++ amdgpu_crtc->cursor_bo = NULL;
++
++ return ret;
++}
++
++static int dm_crtc_pin_cursor_bo_new(
++ struct drm_crtc *crtc,
++ struct drm_file *file_priv,
++ uint32_t handle,
++ struct amdgpu_bo **ret_obj,
++ uint64_t *gpu_addr)
++{
++ struct amdgpu_crtc *amdgpu_crtc;
++ struct amdgpu_bo *robj;
++ struct drm_gem_object *obj;
++ int ret = EINVAL;
++
++ if (NULL != crtc) {
++ amdgpu_crtc = to_amdgpu_crtc(crtc);
++
++ obj = drm_gem_object_lookup(crtc->dev, file_priv, handle);
++
++ if (!obj) {
++ DRM_ERROR(
++ "Cannot find cursor object %x for crtc %d\n",
++ handle,
++ amdgpu_crtc->crtc_id);
++ goto release;
++ }
++ robj = gem_to_amdgpu_bo(obj);
++
++ ret = amdgpu_bo_reserve(robj, false);
++
++ if (unlikely(ret != 0)) {
++ drm_gem_object_unreference_unlocked(obj);
++ DRM_ERROR("dm_crtc_pin_cursor_bo_new ret %x, handle %x\n",
++ ret, handle);
++ goto release;
++ }
++
++ ret = amdgpu_bo_pin(robj, AMDGPU_GEM_DOMAIN_VRAM, NULL);
++
++ if (ret == 0) {
++ *gpu_addr = amdgpu_bo_gpu_offset(robj);
++ *ret_obj = robj;
++ }
++ amdgpu_bo_unreserve(robj);
++ if (ret)
++ drm_gem_object_unreference_unlocked(obj);
++
++ }
++release:
++
++ return ret;
++}
++
++static int dm_crtc_cursor_set(
++ struct drm_crtc *crtc,
++ struct drm_file *file_priv,
++ uint32_t handle,
++ uint32_t width,
++ uint32_t height)
++{
++ struct amdgpu_bo *new_cursor_bo;
++ uint64_t gpu_addr;
++ struct dc_cursor_position position;
++
++ int ret;
++
++ struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
++
++ ret = EINVAL;
++ new_cursor_bo = NULL;
++ gpu_addr = 0;
++
++ DRM_DEBUG_KMS(
++ "%s: crtc_id=%d with handle %d and size %d to %d, bo_object %p\n",
++ __func__,
++ amdgpu_crtc->crtc_id,
++ handle,
++ width,
++ height,
++ amdgpu_crtc->cursor_bo);
++
++ if (!handle) {
++ /* turn off cursor */
++ position.enable = false;
++ position.x = 0;
++ position.y = 0;
++ position.hot_spot_enable = false;
++
++ if (amdgpu_crtc->target) {
++ /*set cursor visible false*/
++ dc_target_set_cursor_position(
++ amdgpu_crtc->target,
++ &position);
++ }
++ /*unpin old cursor buffer and update cache*/
++ ret = dm_crtc_unpin_cursor_bo_old(amdgpu_crtc);
++ goto release;
++
++ }
++
++ if ((width > amdgpu_crtc->max_cursor_width) ||
++ (height > amdgpu_crtc->max_cursor_height)) {
++ DRM_ERROR(
++ "%s: bad cursor width or height %d x %d\n",
++ __func__,
++ width,
++ height);
++ goto release;
++ }
++ /*try to pin new cursor bo*/
++ ret = dm_crtc_pin_cursor_bo_new(crtc, file_priv, handle,
++ &new_cursor_bo, &gpu_addr);
++ /*if map not successful then return an error*/
++ if (ret)
++ goto release;
++
++ /*program new cursor bo to hardware*/
++ dm_set_cursor(amdgpu_crtc, gpu_addr, width, height);
++
++ /*un map old, not used anymore cursor bo ,
++ * return memory and mapping back */
++ dm_crtc_unpin_cursor_bo_old(amdgpu_crtc);
++
++ /*assign new cursor bo to our internal cache*/
++ amdgpu_crtc->cursor_bo = &new_cursor_bo->gem_base;
++
++release:
++ return ret;
++
++}
++
++static int dm_crtc_cursor_move(struct drm_crtc *crtc,
++ int x, int y)
++{
++ struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
++ int xorigin = 0, yorigin = 0;
++ struct dc_cursor_position position;
++
++ /* avivo cursor are offset into the total surface */
++ x += crtc->primary->state->src_x >> 16;
++ y += crtc->primary->state->src_y >> 16;
++
++ /*
++ * TODO: for cursor debugging unguard the following
++ */
++#if 0
++ DRM_DEBUG_KMS(
++ "%s: x %d y %d c->x %d c->y %d\n",
++ __func__,
++ x,
++ y,
++ crtc->x,
++ crtc->y);
++#endif
++
++ if (x < 0) {
++ xorigin = min(-x, amdgpu_crtc->max_cursor_width - 1);
++ x = 0;
++ }
++ if (y < 0) {
++ yorigin = min(-y, amdgpu_crtc->max_cursor_height - 1);
++ y = 0;
++ }
++
++ position.enable = true;
++ position.x = x;
++ position.y = y;
++
++ position.hot_spot_enable = true;
++ position.x_origin = xorigin;
++ position.y_origin = yorigin;
++
++ if (!dc_target_set_cursor_position(
++ amdgpu_crtc->target,
++ &position)) {
++ DRM_ERROR("DC failed to set cursor position\n");
++ return -EINVAL;
++ }
++
++#if BUILD_FEATURE_TIMING_SYNC
++ {
++ struct drm_device *dev = crtc->dev;
++ struct amdgpu_device *adev = dev->dev_private;
++ struct amdgpu_display_manager *dm = &adev->dm;
++
++ dc_print_sync_report(dm->dc);
++ }
++#endif
++ return 0;
++}
++
++static void dm_crtc_cursor_reset(struct drm_crtc *crtc)
++{
++ struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
++
++ DRM_DEBUG_KMS(
++ "%s: with cursor_bo %p\n",
++ __func__,
++ amdgpu_crtc->cursor_bo);
++
++ if (amdgpu_crtc->cursor_bo && amdgpu_crtc->target) {
++ dm_set_cursor(
++ amdgpu_crtc,
++ amdgpu_bo_gpu_offset(gem_to_amdgpu_bo(amdgpu_crtc->cursor_bo)),
++ amdgpu_crtc->cursor_width,
++ amdgpu_crtc->cursor_height);
++ }
++}
++static bool fill_rects_from_plane_state(
++ struct drm_plane_state *state,
++ struct dc_surface *surface)
++{
++ surface->src_rect.x = state->src_x >> 16;
++ surface->src_rect.y = state->src_y >> 16;
++ /*we ignore for now mantissa and do not to deal with floating pixels :(*/
++ surface->src_rect.width = state->src_w >> 16;
++
++ if (surface->src_rect.width == 0)
++ return false;
++
++ surface->src_rect.height = state->src_h >> 16;
++ if (surface->src_rect.height == 0)
++ return false;
++
++ surface->dst_rect.x = state->crtc_x;
++ surface->dst_rect.y = state->crtc_y;
++
++ if (state->crtc_w == 0)
++ return false;
++
++ surface->dst_rect.width = state->crtc_w;
++
++ if (state->crtc_h == 0)
++ return false;
++
++ surface->dst_rect.height = state->crtc_h;
++
++ surface->clip_rect = surface->dst_rect;
++
++ switch (state->rotation) {
++ case DRM_ROTATE_0:
++ surface->rotation = ROTATION_ANGLE_0;
++ break;
++ case DRM_ROTATE_90:
++ surface->rotation = ROTATION_ANGLE_90;
++ break;
++ case DRM_ROTATE_180:
++ surface->rotation = ROTATION_ANGLE_180;
++ break;
++ case DRM_ROTATE_270:
++ surface->rotation = ROTATION_ANGLE_270;
++ break;
++ default:
++ surface->rotation = ROTATION_ANGLE_0;
++ break;
++ }
++
++ return true;
++}
++static bool get_fb_info(
++ struct amdgpu_framebuffer *amdgpu_fb,
++ uint64_t *tiling_flags,
++ uint64_t *fb_location)
++{
++ struct amdgpu_bo *rbo = gem_to_amdgpu_bo(amdgpu_fb->obj);
++ int r = amdgpu_bo_reserve(rbo, false);
++ if (unlikely(r != 0)){
++ DRM_ERROR("Unable to reserve buffer\n");
++ return false;
++ }
++
++
++ if (fb_location)
++ *fb_location = amdgpu_bo_gpu_offset(rbo);
++
++ if (tiling_flags)
++ amdgpu_bo_get_tiling_flags(rbo, tiling_flags);
++
++ amdgpu_bo_unreserve(rbo);
++
++ return true;
++}
++static void fill_plane_attributes_from_fb(
++ struct dc_surface *surface,
++ struct amdgpu_framebuffer *amdgpu_fb)
++{
++ uint64_t tiling_flags;
++ uint64_t fb_location;
++ struct drm_framebuffer *fb = &amdgpu_fb->base;
++
++ get_fb_info(
++ amdgpu_fb,
++ &tiling_flags,
++ &fb_location);
++
++ surface->address.type = PLN_ADDR_TYPE_GRAPHICS;
++ surface->address.grph.addr.low_part = lower_32_bits(fb_location);
++ surface->address.grph.addr.high_part = upper_32_bits(fb_location);
++
++ switch (fb->pixel_format) {
++ case DRM_FORMAT_C8:
++ surface->format = SURFACE_PIXEL_FORMAT_GRPH_PALETA_256_COLORS;
++ break;
++ case DRM_FORMAT_RGB565:
++ surface->format = SURFACE_PIXEL_FORMAT_GRPH_RGB565;
++ break;
++ case DRM_FORMAT_XRGB8888:
++ case DRM_FORMAT_ARGB8888:
++ surface->format = SURFACE_PIXEL_FORMAT_GRPH_ARGB8888;
++ break;
++ default:
++ DRM_ERROR("Unsupported screen depth %d\n", fb->bits_per_pixel);
++ return;
++ }
++
++ surface->tiling_info.value = 0;
++
++ if (AMDGPU_TILING_GET(tiling_flags, ARRAY_MODE) == ARRAY_2D_TILED_THIN1)
++ {
++ unsigned bankw, bankh, mtaspect, tile_split, num_banks;
++
++ bankw = AMDGPU_TILING_GET(tiling_flags, BANK_WIDTH);
++ bankh = AMDGPU_TILING_GET(tiling_flags, BANK_HEIGHT);
++ mtaspect = AMDGPU_TILING_GET(tiling_flags, MACRO_TILE_ASPECT);
++ tile_split = AMDGPU_TILING_GET(tiling_flags, TILE_SPLIT);
++ num_banks = AMDGPU_TILING_GET(tiling_flags, NUM_BANKS);
++
++
++ /* XXX fix me for VI */
++ surface->tiling_info.grph.NUM_BANKS = num_banks;
++ surface->tiling_info.grph.ARRAY_MODE =
++ ARRAY_2D_TILED_THIN1;
++ surface->tiling_info.grph.TILE_SPLIT = tile_split;
++ surface->tiling_info.grph.BANK_WIDTH = bankw;
++ surface->tiling_info.grph.BANK_HEIGHT = bankh;
++ surface->tiling_info.grph.TILE_ASPECT = mtaspect;
++ surface->tiling_info.grph.TILE_MODE =
++ ADDR_SURF_MICRO_TILING_DISPLAY;
++ } else if (AMDGPU_TILING_GET(tiling_flags, ARRAY_MODE)
++ == ARRAY_1D_TILED_THIN1) {
++ surface->tiling_info.grph.ARRAY_MODE = ARRAY_1D_TILED_THIN1;
++ }
++
++ surface->tiling_info.grph.PIPE_CONFIG =
++ AMDGPU_TILING_GET(tiling_flags, PIPE_CONFIG);
++
++ surface->plane_size.grph.surface_size.x = 0;
++ surface->plane_size.grph.surface_size.y = 0;
++ surface->plane_size.grph.surface_size.width = fb->width;
++ surface->plane_size.grph.surface_size.height = fb->height;
++ surface->plane_size.grph.surface_pitch =
++ fb->pitches[0] / (fb->bits_per_pixel / 8);
++
++ surface->enabled = true;
++ surface->scaling_quality.h_taps_c = 2;
++ surface->scaling_quality.v_taps_c = 2;
++
++/* TODO: unhardcode */
++ surface->colorimetry.limited_range = false;
++ surface->colorimetry.color_space = SURFACE_COLOR_SPACE_SRGB;
++ surface->scaling_quality.h_taps = 4;
++ surface->scaling_quality.v_taps = 4;
++ surface->stereo_format = PLANE_STEREO_FORMAT_NONE;
++
++}
++
++static void fill_gamma_from_crtc(
++ struct drm_crtc *crtc,
++ struct dc_surface *dc_surface)
++{
++ int i;
++ struct gamma_ramp *gamma;
++ uint16_t *red, *green, *blue;
++ int end = (crtc->gamma_size > NUM_OF_RAW_GAMMA_RAMP_RGB_256) ?
++ NUM_OF_RAW_GAMMA_RAMP_RGB_256 : crtc->gamma_size;
++
++ red = crtc->gamma_store;
++ green = red + crtc->gamma_size;
++ blue = green + crtc->gamma_size;
++
++ gamma = &dc_surface->gamma_correction;
++
++ for (i = 0; i < end; i++) {
++ gamma->gamma_ramp_rgb256x3x16.red[i] =
++ (unsigned short) red[i];
++ gamma->gamma_ramp_rgb256x3x16.green[i] =
++ (unsigned short) green[i];
++ gamma->gamma_ramp_rgb256x3x16.blue[i] =
++ (unsigned short) blue[i];
++ }
++
++ gamma->type = GAMMA_RAMP_RBG256X3X16;
++ gamma->size = sizeof(gamma->gamma_ramp_rgb256x3x16);
++}
++
++static void fill_plane_attributes(
++ struct dc_surface *surface,
++ struct drm_crtc *crtc)
++{
++ struct amdgpu_framebuffer *amdgpu_fb =
++ to_amdgpu_framebuffer(crtc->primary->state->fb);
++ fill_rects_from_plane_state(crtc->primary->state, surface);
++ fill_plane_attributes_from_fb(
++ surface,
++ amdgpu_fb);
++
++ /* In case of gamma set, update gamma value */
++ if (crtc->mode.private_flags &
++ AMDGPU_CRTC_MODE_PRIVATE_FLAGS_GAMMASET) {
++ fill_gamma_from_crtc(crtc, surface);
++ /* reset trigger of gamma */
++ crtc->mode.private_flags &=
++ ~AMDGPU_CRTC_MODE_PRIVATE_FLAGS_GAMMASET;
++ }
++}
++
++/*****************************************************************************/
++
++struct amdgpu_connector *aconnector_from_drm_crtc(
++ struct drm_crtc *crtc,
++ struct drm_atomic_state *state)
++{
++ struct drm_connector *connector;
++ struct amdgpu_connector *aconnector;
++ struct drm_connector_state *conn_state;
++ uint8_t i;
++
++ for_each_connector_in_state(state, connector, conn_state, i) {
++ aconnector = to_amdgpu_connector(connector);
++ if (connector->state->crtc == crtc)
++ return aconnector;
++ }
++
++ /* If we get here, not found. */
++ return NULL;
++}
++
++struct amdgpu_connector *aconnector_from_drm_crtc_id(
++ const struct drm_crtc *crtc)
++{
++ struct drm_device *dev = crtc->dev;
++ struct drm_connector *connector;
++ struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
++ struct amdgpu_connector *aconnector;
++
++ list_for_each_entry(connector,
++ &dev->mode_config.connector_list, head) {
++
++ aconnector = to_amdgpu_connector(connector);
++
++ /* acrtc->crtc_id means display_index */
++ if (aconnector->connector_id != acrtc->crtc_id)
++ continue;
++
++ /* Found the connector */
++ return aconnector;
++ }
++
++ /* If we get here, not found. */
++ return NULL;
++}
++
++static void dm_dc_surface_commit(
++ struct dc *dc,
++ struct drm_crtc *crtc,
++ struct amdgpu_framebuffer *afb)
++{
++ struct dc_surface *dc_surface;
++ struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
++ struct dc_target *dc_target = acrtc->target;
++
++ if (!dc_target) {
++ dal_error(
++ "%s: Failed to obtain target on crtc (%d)!\n",
++ __func__,
++ acrtc->crtc_id);
++ goto fail;
++ }
++
++ dc_surface = dc_create_surface(dc);
++
++ if (!dc_surface) {
++ dal_error(
++ "%s: Failed to create a surface!\n",
++ __func__);
++ goto fail;
++ }
++
++ /* Surface programming */
++
++ fill_plane_attributes(dc_surface, crtc);
++
++ if (false == dc_commit_surfaces_to_target(
++ dc,
++ &dc_surface,
++ 1,
++ dc_target)) {
++ dal_error(
++ "%s: Failed to attach surface!\n",
++ __func__);
++ }
++
++ dc_surface_release(dc_surface);
++fail:
++ return;
++}
++
++static enum dc_color_depth convert_color_depth_from_display_info(
++ const struct drm_connector *connector)
++{
++ uint32_t bpc = connector->display_info.bpc;
++
++ /* Limited color depth to 8bit
++ * TODO: Still need to handle deep color*/
++ if (bpc > 8)
++ bpc = 8;
++
++ switch (bpc) {
++ case 0:
++ /* Temporary Work around, DRM don't parse color depth for
++ * EDID revision before 1.4
++ * TODO: Fix edid parsing
++ */
++ return COLOR_DEPTH_888;
++ case 6:
++ return COLOR_DEPTH_666;
++ case 8:
++ return COLOR_DEPTH_888;
++ case 10:
++ return COLOR_DEPTH_101010;
++ case 12:
++ return COLOR_DEPTH_121212;
++ case 14:
++ return COLOR_DEPTH_141414;
++ case 16:
++ return COLOR_DEPTH_161616;
++ default:
++ return COLOR_DEPTH_UNDEFINED;
++ }
++}
++
++static enum dc_aspect_ratio get_aspect_ratio(
++ const struct drm_display_mode *mode_in)
++{
++ int32_t width = mode_in->crtc_hdisplay * 9;
++ int32_t height = mode_in->crtc_vdisplay * 16;
++ if ((width - height) < 10 && (width - height) > -10)
++ return ASPECT_RATIO_16_9;
++ else
++ return ASPECT_RATIO_4_3;
++}
++
++/*****************************************************************************/
++
++static void dc_timing_from_drm_display_mode(
++ struct dc_crtc_timing *timing_out,
++ const struct drm_display_mode *mode_in,
++ const struct drm_connector *connector)
++{
++ memset(timing_out, 0, sizeof(struct dc_crtc_timing));
++
++ timing_out->h_border_left = 0;
++ timing_out->h_border_right = 0;
++ timing_out->v_border_top = 0;
++ timing_out->v_border_bottom = 0;
++ /* TODO: un-hardcode */
++ timing_out->pixel_encoding = PIXEL_ENCODING_RGB;
++ timing_out->timing_standard = TIMING_STANDARD_HDMI;
++ timing_out->timing_3d_format = TIMING_3D_FORMAT_NONE;
++ timing_out->display_color_depth = convert_color_depth_from_display_info(
++ connector);
++ timing_out->scan_type = SCANNING_TYPE_NODATA;
++ timing_out->hdmi_vic = 0;
++ timing_out->vic = drm_match_cea_mode(mode_in);
++
++ timing_out->h_addressable = mode_in->crtc_hdisplay;
++ timing_out->h_total = mode_in->crtc_htotal;
++ timing_out->h_sync_width =
++ mode_in->crtc_hsync_end - mode_in->crtc_hsync_start;
++ timing_out->h_front_porch =
++ mode_in->crtc_hsync_start - mode_in->crtc_hdisplay;
++ timing_out->v_total = mode_in->crtc_vtotal;
++ timing_out->v_addressable = mode_in->crtc_vdisplay;
++ timing_out->v_front_porch =
++ mode_in->crtc_vsync_start - mode_in->crtc_vdisplay;
++ timing_out->v_sync_width =
++ mode_in->crtc_vsync_end - mode_in->crtc_vsync_start;
++ timing_out->pix_clk_khz = mode_in->crtc_clock;
++ timing_out->aspect_ratio = get_aspect_ratio(mode_in);
++}
++
++static void fill_audio_info(
++ struct audio_info *audio_info,
++ const struct drm_connector *drm_connector,
++ const struct dc_sink *dc_sink)
++{
++ int i = 0;
++ int cea_revision = 0;
++ const struct dc_edid_caps *edid_caps = &dc_sink->edid_caps;
++
++ audio_info->manufacture_id = edid_caps->manufacturer_id;
++ audio_info->product_id = edid_caps->product_id;
++
++ cea_revision = drm_connector->display_info.cea_rev;
++
++ while (i < AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS &&
++ edid_caps->display_name[i]) {
++ audio_info->display_name[i] = edid_caps->display_name[i];
++ i++;
++ }
++
++ if(cea_revision >= 3) {
++ audio_info->mode_count = edid_caps->audio_mode_count;
++
++ for (i = 0; i < audio_info->mode_count; ++i) {
++ audio_info->modes[i].format_code =
++ (enum audio_format_code)
++ (edid_caps->audio_modes[i].format_code);
++ audio_info->modes[i].channel_count =
++ edid_caps->audio_modes[i].channel_count;
++ audio_info->modes[i].sample_rates.all =
++ edid_caps->audio_modes[i].sample_rate;
++ audio_info->modes[i].sample_size =
++ edid_caps->audio_modes[i].sample_size;
++ }
++ }
++
++ audio_info->flags.all = edid_caps->speaker_flags;
++
++ /* TODO: We only check for the progressive mode, check for interlace mode too */
++ if(drm_connector->latency_present[0]) {
++ audio_info->video_latency = drm_connector->video_latency[0];
++ audio_info->audio_latency = drm_connector->audio_latency[0];
++ }
++
++ /* TODO: For DP, video and audio latency should be calculated from DPCD caps */
++
++}
++
++/*TODO: move these defines elsewhere*/
++#define DAL_MAX_CONTROLLERS 4
++
++static void calculate_stream_scaling_settings(
++ const struct drm_display_mode *mode,
++ enum amdgpu_rmx_type rmx_type,
++ struct dc_stream *stream,
++ uint8_t underscan_vborder,
++ uint8_t underscan_hborder,
++ bool underscan_enable)
++{
++ /* Full screen scaling by default */
++ stream->src.width = mode->hdisplay;
++ stream->src.height = mode->vdisplay;
++ stream->dst.width = stream->timing.h_addressable;
++ stream->dst.height = stream->timing.v_addressable;
++
++ if (rmx_type == RMX_ASPECT || rmx_type == RMX_OFF) {
++ if (stream->src.width * stream->dst.height <
++ stream->src.height * stream->dst.width) {
++ /* height needs less upscaling/more downscaling */
++ stream->dst.width = stream->src.width *
++ stream->dst.height / stream->src.height;
++ } else {
++ /* width needs less upscaling/more downscaling */
++ stream->dst.height = stream->src.height *
++ stream->dst.width / stream->src.width;
++ }
++ } else if (rmx_type == RMX_CENTER) {
++ stream->dst = stream->src;
++ }
++
++ stream->dst.x = (stream->timing.h_addressable - stream->dst.width) / 2;
++ stream->dst.y = (stream->timing.v_addressable - stream->dst.height) / 2;
++
++ if (underscan_enable) {
++ stream->dst.x += underscan_hborder / 2;
++ stream->dst.y += underscan_vborder / 2;
++ stream->dst.width -= underscan_hborder;
++ stream->dst.height -= underscan_vborder;
++ }
++}
++
++
++static void copy_crtc_timing_for_drm_display_mode(
++ const struct drm_display_mode *src_mode,
++ struct drm_display_mode *dst_mode)
++{
++ dst_mode->crtc_hdisplay = src_mode->crtc_hdisplay;
++ dst_mode->crtc_vdisplay = src_mode->crtc_vdisplay;
++ dst_mode->crtc_clock = src_mode->crtc_clock;
++ dst_mode->crtc_hblank_start = src_mode->crtc_hblank_start;
++ dst_mode->crtc_hblank_end = src_mode->crtc_hblank_end;
++ dst_mode->crtc_hsync_start= src_mode->crtc_hsync_start;
++ dst_mode->crtc_hsync_end = src_mode->crtc_hsync_end;
++ dst_mode->crtc_htotal = src_mode->crtc_htotal;
++ dst_mode->crtc_hskew = src_mode->crtc_hskew;
++ dst_mode->crtc_vblank_start = src_mode->crtc_vblank_start;;
++ dst_mode->crtc_vblank_end = src_mode->crtc_vblank_end;;
++ dst_mode->crtc_vsync_start = src_mode->crtc_vsync_start;;
++ dst_mode->crtc_vsync_end = src_mode->crtc_vsync_end;;
++ dst_mode->crtc_vtotal = src_mode->crtc_vtotal;;
++}
++
++static void decide_crtc_timing_for_drm_display_mode(
++ struct drm_display_mode *drm_mode,
++ const struct drm_display_mode *native_mode,
++ bool scale_enabled)
++{
++ if (scale_enabled) {
++ copy_crtc_timing_for_drm_display_mode(native_mode, drm_mode);
++ } else if (native_mode->clock == drm_mode->clock &&
++ native_mode->htotal == drm_mode->htotal &&
++ native_mode->vtotal == drm_mode->vtotal) {
++ copy_crtc_timing_for_drm_display_mode(native_mode, drm_mode);
++ } else {
++ /* no scaling nor amdgpu inserted, no need to patch */
++ }
++}
++
++
++static struct dc_target *create_target_for_sink(
++ const struct amdgpu_connector *aconnector,
++ struct drm_display_mode *drm_mode)
++{
++ struct drm_display_mode *preferred_mode = NULL;
++ const struct drm_connector *drm_connector;
++ struct dm_connector_state *dm_state;
++ struct dc_target *target = NULL;
++ struct dc_stream *stream;
++ struct drm_display_mode mode = *drm_mode;
++ bool native_mode_found = false;
++
++ if (NULL == aconnector) {
++ DRM_ERROR("aconnector is NULL!\n");
++ goto drm_connector_null;
++ }
++
++ drm_connector = &aconnector->base;
++ dm_state = to_dm_connector_state(drm_connector->state);
++ stream = dc_create_stream_for_sink(aconnector->dc_sink);
++
++ if (NULL == stream) {
++ DRM_ERROR("Failed to create stream for sink!\n");
++ goto stream_create_fail;
++ }
++
++ list_for_each_entry(preferred_mode, &aconnector->base.modes, head) {
++ /* Search for preferred mode */
++ if (preferred_mode->type & DRM_MODE_TYPE_PREFERRED) {
++ native_mode_found = true;
++ break;
++ }
++ }
++ if (!native_mode_found)
++ preferred_mode = list_first_entry_or_null(
++ &aconnector->base.modes,
++ struct drm_display_mode,
++ head);
++
++ decide_crtc_timing_for_drm_display_mode(
++ &mode, preferred_mode,
++ dm_state->scaling != RMX_OFF);
++
++ dc_timing_from_drm_display_mode(&stream->timing,
++ &mode, &aconnector->base);
++
++ calculate_stream_scaling_settings(&mode, dm_state->scaling, stream,
++ dm_state->underscan_vborder,
++ dm_state->underscan_hborder,
++ dm_state->underscan_enable);
++
++
++ fill_audio_info(
++ &stream->audio_info,
++ drm_connector,
++ aconnector->dc_sink);
++
++ target = dc_create_target_for_streams(&stream, 1);
++ dc_stream_release(stream);
++
++ if (NULL == target) {
++ DRM_ERROR("Failed to create target with streams!\n");
++ goto target_create_fail;
++ }
++
++drm_connector_null:
++target_create_fail:
++stream_create_fail:
++ return target;
++}
++
++void amdgpu_dm_crtc_destroy(struct drm_crtc *crtc)
++{
++ drm_crtc_cleanup(crtc);
++ kfree(crtc);
++}
++
++static void amdgpu_dm_atomic_crtc_gamma_set(
++ struct drm_crtc *crtc,
++ u16 *red,
++ u16 *green,
++ u16 *blue,
++ uint32_t start,
++ uint32_t size)
++{
++ struct drm_device *dev = crtc->dev;
++ struct drm_property *prop = dev->mode_config.prop_crtc_id;
++
++ crtc->mode.private_flags |= AMDGPU_CRTC_MODE_PRIVATE_FLAGS_GAMMASET;
++
++ drm_atomic_helper_crtc_set_property(crtc, prop, 0);
++}
++
++static int dm_crtc_funcs_atomic_set_property(
++ struct drm_crtc *crtc,
++ struct drm_crtc_state *state,
++ struct drm_property *property,
++ uint64_t val)
++{
++ struct drm_crtc_state *new_crtc_state;
++ struct drm_crtc *new_crtc;
++ int i;
++
++ for_each_crtc_in_state(state->state, new_crtc, new_crtc_state, i) {
++ if (new_crtc == crtc) {
++ struct drm_plane_state *plane_state;
++
++ new_crtc_state->planes_changed = true;
++
++ /*
++ * Bit of magic done here. We need to ensure
++ * that planes get update after mode is set.
++ * So, we need to add primary plane to state,
++ * and this way atomic_update would be called
++ * for it
++ */
++ plane_state =
++ drm_atomic_get_plane_state(
++ state->state,
++ crtc->primary);
++
++ if (!plane_state)
++ return -EINVAL;
++ }
++ }
++
++ return 0;
++}
++
++/* Implemented only the options currently availible for the driver */
++static const struct drm_crtc_funcs amdgpu_dm_crtc_funcs = {
++ .reset = drm_atomic_helper_crtc_reset,
++ .cursor_set = dm_crtc_cursor_set,
++ .cursor_move = dm_crtc_cursor_move,
++ .destroy = amdgpu_dm_crtc_destroy,
++ .gamma_set = amdgpu_dm_atomic_crtc_gamma_set,
++ .set_config = drm_atomic_helper_set_config,
++ .page_flip = drm_atomic_helper_page_flip,
++ .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
++ .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
++ .atomic_set_property = dm_crtc_funcs_atomic_set_property
++};
++
++static enum drm_connector_status
++amdgpu_dm_connector_detect(struct drm_connector *connector, bool force)
++{
++ bool connected;
++ struct amdgpu_connector *aconnector =
++ to_amdgpu_connector(connector);
++
++ /*
++ * TODO: check whether we should lock here for mst_mgr.lock
++ */
++ /* set root connector to disconnected */
++ if (aconnector->mst_mgr.mst_state)
++ return connector_status_disconnected;
++
++ connected = (NULL != aconnector->dc_sink);
++ return (connected ? connector_status_connected :
++ connector_status_disconnected);
++}
++
++int amdgpu_dm_connector_atomic_set_property(
++ struct drm_connector *connector,
++ struct drm_connector_state *state,
++ struct drm_property *property,
++ uint64_t val)
++{
++ struct drm_device *dev = connector->dev;
++ struct amdgpu_device *adev = dev->dev_private;
++ struct dm_connector_state *dm_old_state =
++ to_dm_connector_state(connector->state);
++ struct dm_connector_state *dm_new_state =
++ to_dm_connector_state(state);
++
++ if (property == dev->mode_config.scaling_mode_property) {
++ struct drm_crtc_state *new_crtc_state;
++ struct drm_crtc *crtc;
++ int i;
++ enum amdgpu_rmx_type rmx_type;
++
++ switch (val) {
++ case DRM_MODE_SCALE_CENTER:
++ rmx_type = RMX_CENTER;
++ break;
++ case DRM_MODE_SCALE_ASPECT:
++ rmx_type = RMX_ASPECT;
++ break;
++ case DRM_MODE_SCALE_FULLSCREEN:
++ rmx_type = RMX_FULL;
++ break;
++ case DRM_MODE_SCALE_NONE:
++ default:
++ rmx_type = RMX_OFF;
++ break;
++ }
++
++ if (dm_old_state->scaling == rmx_type)
++ return 0;
++
++ dm_new_state->scaling = rmx_type;
++
++ for_each_crtc_in_state(state->state, crtc, new_crtc_state, i) {
++ if (crtc == state->crtc) {
++ struct drm_plane_state *plane_state;
++
++ new_crtc_state->mode_changed = true;
++
++ /*
++ * Bit of magic done here. We need to ensure
++ * that planes get update after mode is set.
++ * So, we need to add primary plane to state,
++ * and this way atomic_update would be called
++ * for it
++ */
++ plane_state =
++ drm_atomic_get_plane_state(
++ state->state,
++ crtc->primary);
++
++ if (!plane_state)
++ return -EINVAL;
++ }
++ }
++
++ return 0;
++ } else if (property == adev->mode_info.underscan_hborder_property) {
++ dm_new_state->underscan_hborder = val;
++ return 0;
++ } else if (property == adev->mode_info.underscan_vborder_property) {
++ dm_new_state->underscan_vborder = val;
++ return 0;
++ } else if (property == adev->mode_info.underscan_property) {
++ struct drm_crtc_state *new_crtc_state;
++ struct drm_crtc *crtc;
++ int i;
++
++ dm_new_state->underscan_enable = val;
++ for_each_crtc_in_state(state->state, crtc, new_crtc_state, i) {
++ if (crtc == state->crtc) {
++ struct drm_plane_state *plane_state;
++
++ new_crtc_state->mode_changed = true;
++
++ /*
++ * Bit of magic done here. We need to ensure
++ * that planes get update after mode is set.
++ * So, we need to add primary plane to state,
++ * and this way atomic_update would be called
++ * for it
++ */
++ plane_state =
++ drm_atomic_get_plane_state(
++ state->state,
++ crtc->primary);
++
++ if (!plane_state)
++ return -EINVAL;
++ }
++ }
++
++ return 0;
++ }
++
++ return -EINVAL;
++}
++
++void amdgpu_dm_connector_destroy(struct drm_connector *connector)
++{
++ /*drm_sysfs_connector_remove(connector);*/
++ drm_connector_cleanup(connector);
++ kfree(connector);
++}
++
++void amdgpu_dm_connector_funcs_reset(struct drm_connector *connector)
++{
++ struct dm_connector_state *state =
++ to_dm_connector_state(connector->state);
++
++ kfree(state);
++
++ state = kzalloc(sizeof(*state), GFP_KERNEL);
++
++ if (state) {
++ state->scaling = RMX_OFF;
++
++ connector->state = &state->base;
++ connector->state->connector = connector;
++ }
++}
++
++struct drm_connector_state *amdgpu_dm_connector_atomic_duplicate_state(
++ struct drm_connector *connector)
++{
++ struct dm_connector_state *state =
++ to_dm_connector_state(connector->state);
++
++ struct dm_connector_state *new_state =
++ kzalloc(sizeof(*new_state), GFP_KERNEL);
++
++ if (new_state) {
++ *new_state = *state;
++
++ return &new_state->base;
++ }
++
++ return NULL;
++}
++
++void amdgpu_dm_connector_atomic_destroy_state(
++ struct drm_connector *connector,
++ struct drm_connector_state *state)
++{
++ struct dm_connector_state *dm_state =
++ to_dm_connector_state(state);
++
++ __drm_atomic_helper_connector_destroy_state(connector, state);
++
++ kfree(dm_state);
++}
++
++static const struct drm_connector_funcs amdgpu_dm_connector_funcs = {
++ .dpms = drm_atomic_helper_connector_dpms,
++ .reset = amdgpu_dm_connector_funcs_reset,
++ .detect = amdgpu_dm_connector_detect,
++ .fill_modes = drm_helper_probe_single_connector_modes,
++ .set_property = drm_atomic_helper_connector_set_property,
++ .destroy = amdgpu_dm_connector_destroy,
++ .atomic_duplicate_state = amdgpu_dm_connector_atomic_duplicate_state,
++ .atomic_destroy_state = amdgpu_dm_connector_atomic_destroy_state,
++ .atomic_set_property = amdgpu_dm_connector_atomic_set_property
++};
++
++static struct drm_encoder *best_encoder(struct drm_connector *connector)
++{
++ int enc_id = connector->encoder_ids[0];
++ struct drm_mode_object *obj;
++ struct drm_encoder *encoder;
++
++ DRM_DEBUG_KMS("Finding the best encoder\n");
++
++ /* pick the encoder ids */
++ if (enc_id) {
++ obj = drm_mode_object_find(connector->dev, enc_id, DRM_MODE_OBJECT_ENCODER);
++ if (!obj) {
++ DRM_ERROR("Couldn't find a matching encoder for our connector\n");
++ return NULL;
++ }
++ encoder = obj_to_encoder(obj);
++ return encoder;
++ }
++ DRM_ERROR("No encoder id\n");
++ return NULL;
++}
++
++static int get_modes(struct drm_connector *connector)
++{
++ struct amdgpu_connector *amdgpu_connector =
++ to_amdgpu_connector(connector);
++ return amdgpu_connector->num_modes;
++}
++
++static int mode_valid(struct drm_connector *connector,
++ struct drm_display_mode *mode)
++{
++ int result = MODE_ERROR;
++ const struct dc_sink *dc_sink =
++ to_amdgpu_connector(connector)->dc_sink;
++ struct amdgpu_device *adev = connector->dev->dev_private;
++ struct dc_validation_set val_set = { 0 };
++ /* TODO: Unhardcode stream count */
++ struct dc_stream *streams[1];
++ struct dc_target *target;
++
++ if ((mode->flags & DRM_MODE_FLAG_INTERLACE) ||
++ (mode->flags & DRM_MODE_FLAG_DBLSCAN))
++ return result;
++
++ if (NULL == dc_sink) {
++ DRM_ERROR("dc_sink is NULL!\n");
++ goto stream_create_fail;
++ }
++
++ streams[0] = dc_create_stream_for_sink(dc_sink);
++
++ if (NULL == streams[0]) {
++ DRM_ERROR("Failed to create stream for sink!\n");
++ goto stream_create_fail;
++ }
++
++ drm_mode_set_crtcinfo(mode, 0);
++ dc_timing_from_drm_display_mode(&streams[0]->timing, mode, connector);
++
++ target = dc_create_target_for_streams(streams, 1);
++ val_set.target = target;
++
++ if (NULL == val_set.target) {
++ DRM_ERROR("Failed to create target with stream!\n");
++ goto target_create_fail;
++ }
++
++ val_set.surface_count = 0;
++ streams[0]->src.width = mode->hdisplay;
++ streams[0]->src.height = mode->vdisplay;
++ streams[0]->dst = streams[0]->src;
++
++ if (dc_validate_resources(adev->dm.dc, &val_set, 1))
++ result = MODE_OK;
++
++ dc_target_release(target);
++target_create_fail:
++ dc_stream_release(streams[0]);
++stream_create_fail:
++ /* TODO: error handling*/
++ return result;
++}
++
++
++static const struct drm_connector_helper_funcs
++amdgpu_dm_connector_helper_funcs = {
++ /*
++ * If hotplug a second bigger display in FB Con mode, bigger resolution
++ * modes will be filtered by drm_mode_validate_size(), and those modes
++ * is missing after user start lightdm. So we need to renew modes list.
++ * in get_modes call back, not just return the modes count
++ */
++ .get_modes = get_modes,
++ .mode_valid = mode_valid,
++ .best_encoder = best_encoder
++};
++
++static void dm_crtc_helper_disable(struct drm_crtc *crtc)
++{
++}
++
++static int dm_crtc_helper_atomic_check(
++ struct drm_crtc *crtc,
++ struct drm_crtc_state *state)
++{
++ return 0;
++}
++
++static bool dm_crtc_helper_mode_fixup(
++ struct drm_crtc *crtc,
++ const struct drm_display_mode *mode,
++ struct drm_display_mode *adjusted_mode)
++{
++ return true;
++}
++
++static const struct drm_crtc_helper_funcs amdgpu_dm_crtc_helper_funcs = {
++ .disable = dm_crtc_helper_disable,
++ .atomic_check = dm_crtc_helper_atomic_check,
++ .mode_fixup = dm_crtc_helper_mode_fixup
++};
++
++static void dm_encoder_helper_disable(struct drm_encoder *encoder)
++{
++
++}
++
++static int dm_encoder_helper_atomic_check(
++ struct drm_encoder *encoder,
++ struct drm_crtc_state *crtc_state,
++ struct drm_connector_state *conn_state)
++{
++ return 0;
++}
++
++const struct drm_encoder_helper_funcs amdgpu_dm_encoder_helper_funcs = {
++ .disable = dm_encoder_helper_disable,
++ .atomic_check = dm_encoder_helper_atomic_check
++};
++
++static const struct drm_plane_funcs dm_plane_funcs = {
++ .reset = drm_atomic_helper_plane_reset,
++ .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
++ .atomic_destroy_state = drm_atomic_helper_plane_destroy_state
++};
++
++static void clear_unrelated_fields(struct drm_plane_state *state)
++{
++ state->crtc = NULL;
++ state->fb = NULL;
++ state->state = NULL;
++ state->fence = NULL;
++}
++
++static bool page_flip_needed(
++ struct drm_plane_state *new_state,
++ struct drm_plane_state *old_state)
++{
++ struct drm_plane_state old_state_tmp;
++ struct drm_plane_state new_state_tmp;
++
++ struct amdgpu_framebuffer *amdgpu_fb_old;
++ struct amdgpu_framebuffer *amdgpu_fb_new;
++
++ uint64_t old_tiling_flags;
++ uint64_t new_tiling_flags;
++
++ if (!old_state)
++ return false;
++
++ if (!old_state->fb)
++ return false;
++
++ if (!new_state)
++ return false;
++
++ if (!new_state->fb)
++ return false;
++
++ old_state_tmp = *old_state;
++ new_state_tmp = *new_state;
++
++ if (!new_state->crtc->state->event)
++ return false;
++
++ amdgpu_fb_old = to_amdgpu_framebuffer(old_state->fb);
++ amdgpu_fb_new = to_amdgpu_framebuffer(new_state->fb);
++
++ if (!get_fb_info(amdgpu_fb_old, &old_tiling_flags, NULL))
++ return false;
++
++ if (!get_fb_info(amdgpu_fb_new, &new_tiling_flags, NULL))
++ return false;
++
++ if (old_tiling_flags != new_tiling_flags)
++ return false;
++
++ clear_unrelated_fields(&old_state_tmp);
++ clear_unrelated_fields(&new_state_tmp);
++
++ return memcmp(&old_state_tmp, &new_state_tmp, sizeof(old_state_tmp)) == 0;
++}
++
++static int dm_plane_helper_prepare_fb(
++ struct drm_plane *plane,
++ const struct drm_plane_state *new_state)
++{
++ struct amdgpu_framebuffer *afb;
++ struct drm_gem_object *obj;
++ struct amdgpu_bo *rbo;
++ int r;
++
++ if (!new_state->fb) {
++ DRM_DEBUG_KMS("No FB bound\n");
++ return 0;
++ }
++
++ afb = to_amdgpu_framebuffer(new_state->fb);
++
++ DRM_DEBUG_KMS("Pin new framebuffer: %p\n", afb);
++ obj = afb->obj;
++ rbo = gem_to_amdgpu_bo(obj);
++ r = amdgpu_bo_reserve(rbo, false);
++ if (unlikely(r != 0))
++ return r;
++
++ r = amdgpu_bo_pin(rbo, AMDGPU_GEM_DOMAIN_VRAM, NULL);
++
++ amdgpu_bo_unreserve(rbo);
++
++ if (unlikely(r != 0)) {
++ DRM_ERROR("Failed to pin framebuffer\n");
++ return r;
++ }
++
++ return 0;
++}
++
++static void dm_plane_helper_cleanup_fb(
++ struct drm_plane *plane,
++ const struct drm_plane_state *old_state)
++{
++ struct amdgpu_bo *rbo;
++ struct amdgpu_framebuffer *afb;
++ int r;
++
++ if (!old_state->fb)
++ return;
++
++ afb = to_amdgpu_framebuffer(old_state->fb);
++ DRM_DEBUG_KMS("Unpin old framebuffer: %p\n", afb);
++ rbo = gem_to_amdgpu_bo(afb->obj);
++ r = amdgpu_bo_reserve(rbo, false);
++ if (unlikely(r)) {
++ DRM_ERROR("failed to reserve rbo before unpin\n");
++ return;
++ } else {
++ amdgpu_bo_unpin(rbo);
++ amdgpu_bo_unreserve(rbo);
++ }
++}
++
++int dm_create_validation_set_for_target(struct drm_connector *connector,
++ struct drm_display_mode *mode, struct dc_validation_set *val_set)
++{
++ int result = MODE_ERROR;
++ const struct dc_sink *dc_sink =
++ to_amdgpu_connector(connector)->dc_sink;
++ /* TODO: Unhardcode stream count */
++ struct dc_stream *streams[1];
++ struct dc_target *target;
++
++ if ((mode->flags & DRM_MODE_FLAG_INTERLACE) ||
++ (mode->flags & DRM_MODE_FLAG_DBLSCAN))
++ return result;
++
++ if (NULL == dc_sink) {
++ DRM_ERROR("dc_sink is NULL!\n");
++ return result;
++ }
++
++ streams[0] = dc_create_stream_for_sink(dc_sink);
++
++ if (NULL == streams[0]) {
++ DRM_ERROR("Failed to create stream for sink!\n");
++ return result;
++ }
++
++ drm_mode_set_crtcinfo(mode, 0);
++ dc_timing_from_drm_display_mode(&streams[0]->timing, mode, connector);
++
++ target = dc_create_target_for_streams(streams, 1);
++ val_set->target = target;
++
++ if (NULL == val_set->target) {
++ DRM_ERROR("Failed to create target with stream!\n");
++ goto fail;
++ }
++
++ streams[0]->src.width = mode->hdisplay;
++ streams[0]->src.height = mode->vdisplay;
++ streams[0]->dst = streams[0]->src;
++
++ return MODE_OK;
++
++fail:
++ dc_stream_release(streams[0]);
++ return result;
++
++}
++
++int dm_add_surface_to_validation_set(struct drm_plane *plane,
++ struct drm_plane_state *state, struct dc_surface **surface)
++{
++ int res;
++
++ struct amdgpu_framebuffer *afb;
++ struct amdgpu_connector *aconnector;
++ struct drm_crtc *crtc;
++ struct drm_framebuffer *fb;
++
++ struct drm_device *dev;
++ struct amdgpu_device *adev;
++
++ res = -EINVAL;
++
++ if (NULL == plane || NULL == state) {
++ DRM_ERROR("invalid parameters dm_plane_atomic_check\n");
++ return res;
++ }
++
++ crtc = state->crtc;
++ fb = state->fb;
++
++
++ afb = to_amdgpu_framebuffer(fb);
++
++ if (NULL == state->crtc) {
++ return res;
++ }
++
++ aconnector = aconnector_from_drm_crtc(crtc, state->state);
++
++ if (NULL == aconnector) {
++ DRM_ERROR("Connector is NULL in dm_plane_atomic_check\n");
++ return res;
++ }
++
++ if (NULL == aconnector->dc_sink) {
++ DRM_ERROR("dc_sink is NULL in dm_plane_atomic_check\n");
++ return res;
++ }
++ dev = state->crtc->dev;
++ adev = dev->dev_private;
++
++ *surface = dc_create_surface(adev->dm.dc);
++ if (NULL == *surface){
++ DRM_ERROR("surface is NULL\n");
++ return res;
++ }
++
++ if (!fill_rects_from_plane_state( state, *surface)) {
++ DRM_ERROR("Failed to fill surface!\n");
++ goto fail;
++ }
++
++ fill_plane_attributes_from_fb(*surface, afb);
++
++ return MODE_OK;
++
++fail:
++ dc_surface_release(*surface);
++ return res;
++}
++
++static const struct drm_plane_helper_funcs dm_plane_helper_funcs = {
++ .prepare_fb = dm_plane_helper_prepare_fb,
++ .cleanup_fb = dm_plane_helper_cleanup_fb,
++};
++
++/*
++ * TODO: these are currently initialized to rgb formats only.
++ * For future use cases we should either initialize them dynamically based on
++ * plane capabilities, or initialize this array to all formats, so internal drm
++ * check will succeed, and let DC to implement proper check
++ */
++static uint32_t rgb_formats[] = {
++ DRM_FORMAT_XRGB4444,
++ DRM_FORMAT_ARGB4444,
++ DRM_FORMAT_RGBA4444,
++ DRM_FORMAT_ARGB1555,
++ DRM_FORMAT_RGB565,
++ DRM_FORMAT_RGB888,
++ DRM_FORMAT_XRGB8888,
++ DRM_FORMAT_ARGB8888,
++ DRM_FORMAT_RGBA8888,
++};
++
++int amdgpu_dm_crtc_init(struct amdgpu_display_manager *dm,
++ struct amdgpu_crtc *acrtc,
++ uint32_t link_index)
++{
++ int res = -ENOMEM;
++
++ struct drm_plane *primary_plane =
++ kzalloc(sizeof(*primary_plane), GFP_KERNEL);
++
++ if (!primary_plane)
++ goto fail_plane;
++
++ /* this flag is used in legacy code only */
++ primary_plane->format_default = true;
++
++ res = drm_universal_plane_init(
++ dm->adev->ddev,
++ primary_plane,
++ 0,
++ &dm_plane_funcs,
++ rgb_formats,
++ ARRAY_SIZE(rgb_formats),
++ DRM_PLANE_TYPE_PRIMARY, NULL);
++
++ primary_plane->crtc = &acrtc->base;
++
++ drm_plane_helper_add(primary_plane, &dm_plane_helper_funcs);
++
++ res = drm_crtc_init_with_planes(
++ dm->ddev,
++ &acrtc->base,
++ primary_plane,
++ NULL,
++ &amdgpu_dm_crtc_funcs, NULL);
++
++ if (res)
++ goto fail;
++
++ drm_crtc_helper_add(&acrtc->base, &amdgpu_dm_crtc_helper_funcs);
++
++ acrtc->max_cursor_width = 128;
++ acrtc->max_cursor_height = 128;
++
++ acrtc->crtc_id = link_index;
++ acrtc->base.enabled = false;
++
++ dm->adev->mode_info.crtcs[link_index] = acrtc;
++ drm_mode_crtc_set_gamma_size(&acrtc->base, 256);
++
++ return 0;
++fail:
++ kfree(primary_plane);
++fail_plane:
++ acrtc->crtc_id = -1;
++ return res;
++}
++
++static int to_drm_connector_type(enum signal_type st)
++{
++ switch (st) {
++ case SIGNAL_TYPE_HDMI_TYPE_A:
++ return DRM_MODE_CONNECTOR_HDMIA;
++ case SIGNAL_TYPE_EDP:
++ return DRM_MODE_CONNECTOR_eDP;
++ case SIGNAL_TYPE_RGB:
++ return DRM_MODE_CONNECTOR_VGA;
++ case SIGNAL_TYPE_DISPLAY_PORT:
++ case SIGNAL_TYPE_DISPLAY_PORT_MST:
++ return DRM_MODE_CONNECTOR_DisplayPort;
++ case SIGNAL_TYPE_DVI_DUAL_LINK:
++ case SIGNAL_TYPE_DVI_SINGLE_LINK:
++ return DRM_MODE_CONNECTOR_DVID;
++
++ default:
++ return DRM_MODE_CONNECTOR_Unknown;
++ }
++}
++
++static void amdgpu_dm_get_native_mode(struct drm_connector *connector)
++{
++ const struct drm_connector_helper_funcs *helper =
++ connector->helper_private;
++ struct drm_encoder *encoder;
++ struct amdgpu_encoder *amdgpu_encoder;
++
++ encoder = helper->best_encoder(connector);
++
++ if (encoder == NULL)
++ return;
++
++ amdgpu_encoder = to_amdgpu_encoder(encoder);
++
++ amdgpu_encoder->native_mode.clock = 0;
++
++ if (!list_empty(&connector->probed_modes)) {
++ struct drm_display_mode *preferred_mode = NULL;
++ list_for_each_entry(preferred_mode,
++ &connector->probed_modes,
++ head) {
++ if (preferred_mode->type & DRM_MODE_TYPE_PREFERRED) {
++ amdgpu_encoder->native_mode = *preferred_mode;
++ }
++ break;
++ }
++
++ }
++}
++
++static struct drm_display_mode *amdgpu_dm_create_common_mode(
++ struct drm_encoder *encoder, char *name,
++ int hdisplay, int vdisplay)
++{
++ struct drm_device *dev = encoder->dev;
++ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
++ struct drm_display_mode *mode = NULL;
++ struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode;
++
++ mode = drm_mode_duplicate(dev, native_mode);
++
++ if(mode == NULL)
++ return NULL;
++
++ mode->hdisplay = hdisplay;
++ mode->vdisplay = vdisplay;
++ mode->type &= ~DRM_MODE_TYPE_PREFERRED;
++ strncpy(mode->name, name, DRM_DISPLAY_MODE_LEN);
++
++ return mode;
++
++}
++
++static void amdgpu_dm_connector_add_common_modes(struct drm_encoder *encoder,
++ struct drm_connector *connector)
++{
++ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
++ struct drm_display_mode *mode = NULL;
++ struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode;
++ struct amdgpu_connector *amdgpu_connector =
++ to_amdgpu_connector(connector);
++ int i;
++ int n;
++ struct mode_size {
++ char name[DRM_DISPLAY_MODE_LEN];
++ int w;
++ int h;
++ }common_modes[] = {
++ { "640x480", 640, 480},
++ { "800x600", 800, 600},
++ { "1024x768", 1024, 768},
++ { "1280x720", 1280, 720},
++ { "1280x800", 1280, 800},
++ {"1280x1024", 1280, 1024},
++ { "1440x900", 1440, 900},
++ {"1680x1050", 1680, 1050},
++ {"1600x1200", 1600, 1200},
++ {"1920x1080", 1920, 1080},
++ {"1920x1200", 1920, 1200}
++ };
++
++ n = sizeof(common_modes) / sizeof(common_modes[0]);
++
++ for (i = 0; i < n; i++) {
++ struct drm_display_mode *curmode = NULL;
++ bool mode_existed = false;
++
++ if (common_modes[i].w > native_mode->hdisplay ||
++ common_modes[i].h > native_mode->vdisplay ||
++ (common_modes[i].w == native_mode->hdisplay &&
++ common_modes[i].h == native_mode->vdisplay))
++ continue;
++
++ list_for_each_entry(curmode, &connector->probed_modes, head) {
++ if (common_modes[i].w == curmode->hdisplay &&
++ common_modes[i].h == curmode->vdisplay) {
++ mode_existed = true;
++ break;
++ }
++ }
++
++ if (mode_existed)
++ continue;
++
++ mode = amdgpu_dm_create_common_mode(encoder,
++ common_modes[i].name, common_modes[i].w,
++ common_modes[i].h);
++ drm_mode_probed_add(connector, mode);
++ amdgpu_connector->num_modes++;
++ }
++}
++
++static void amdgpu_dm_connector_ddc_get_modes(
++ struct drm_connector *connector,
++ struct edid *edid)
++{
++ struct amdgpu_connector *amdgpu_connector =
++ to_amdgpu_connector(connector);
++
++ if (edid) {
++ /* empty probed_modes */
++ INIT_LIST_HEAD(&connector->probed_modes);
++ amdgpu_connector->num_modes =
++ drm_add_edid_modes(connector, edid);
++
++ drm_edid_to_eld(connector, edid);
++
++ amdgpu_dm_get_native_mode(connector);
++ } else
++ amdgpu_connector->num_modes = 0;
++}
++
++int amdgpu_dm_connector_get_modes(struct drm_connector *connector)
++{
++ const struct drm_connector_helper_funcs *helper =
++ connector->helper_private;
++ struct amdgpu_connector *amdgpu_connector =
++ to_amdgpu_connector(connector);
++ struct drm_encoder *encoder;
++ struct edid *edid = amdgpu_connector->edid;
++
++ encoder = helper->best_encoder(connector);
++
++ amdgpu_dm_connector_ddc_get_modes(connector, edid);
++ amdgpu_dm_connector_add_common_modes(encoder, connector);
++ return amdgpu_connector->num_modes;
++}
++
++/* Note: this function assumes that dc_link_detect() was called for the
++ * dc_link which will be represented by this aconnector. */
++int amdgpu_dm_connector_init(
++ struct amdgpu_display_manager *dm,
++ struct amdgpu_connector *aconnector,
++ uint32_t link_index,
++ struct amdgpu_encoder *aencoder)
++{
++ int res, connector_type;
++ struct amdgpu_device *adev = dm->ddev->dev_private;
++ struct dc *dc = dm->dc;
++ const struct dc_link *link = dc_get_link_at_index(dc, link_index);
++
++ DRM_DEBUG_KMS("%s()\n", __func__);
++
++ connector_type = to_drm_connector_type(link->connector_signal);
++
++ res = drm_connector_init(
++ dm->ddev,
++ &aconnector->base,
++ &amdgpu_dm_connector_funcs,
++ connector_type);
++
++ if (res) {
++ DRM_ERROR("connector_init failed\n");
++ aconnector->connector_id = -1;
++ return res;
++ }
++
++ drm_connector_helper_add(
++ &aconnector->base,
++ &amdgpu_dm_connector_helper_funcs);
++
++ aconnector->connector_id = link_index;
++ aconnector->dc_link = link;
++ aconnector->base.interlace_allowed = true;
++ aconnector->base.doublescan_allowed = true;
++ aconnector->hpd.hpd = link_index; /* maps to 'enum amdgpu_hpd_id' */
++
++ /*configure suport HPD hot plug connector_>polled default value is 0
++ * which means HPD hot plug not supported*/
++ switch (connector_type) {
++ case DRM_MODE_CONNECTOR_HDMIA:
++ aconnector->base.polled = DRM_CONNECTOR_POLL_HPD;
++ break;
++ case DRM_MODE_CONNECTOR_DisplayPort:
++ aconnector->base.polled = DRM_CONNECTOR_POLL_HPD;
++ break;
++ case DRM_MODE_CONNECTOR_DVID:
++ aconnector->base.polled = DRM_CONNECTOR_POLL_HPD;
++ break;
++ default:
++ break;
++ }
++
++ drm_object_attach_property(&aconnector->base.base,
++ dm->ddev->mode_config.scaling_mode_property,
++ DRM_MODE_SCALE_NONE);
++
++ drm_object_attach_property(&aconnector->base.base,
++ adev->mode_info.underscan_property,
++ UNDERSCAN_OFF);
++ drm_object_attach_property(&aconnector->base.base,
++ adev->mode_info.underscan_hborder_property,
++ 0);
++ drm_object_attach_property(&aconnector->base.base,
++ adev->mode_info.underscan_vborder_property,
++ 0);
++
++ /* TODO: Don't do this manually anymore
++ aconnector->base.encoder = &aencoder->base;
++ */
++
++ drm_mode_connector_attach_encoder(
++ &aconnector->base, &aencoder->base);
++
++ /*drm_sysfs_connector_add(&dm_connector->base);*/
++
++ drm_connector_register(&aconnector->base);
++
++ if (connector_type == DRM_MODE_CONNECTOR_DisplayPort)
++ amdgpu_dm_initialize_mst_connector(dm, aconnector);
++
++#if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) ||\
++ defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE)
++
++ /* NOTE: this currently will create backlight device even if a panel
++ * is not connected to the eDP/LVDS connector.
++ *
++ * This is less than ideal but we don't have sink information at this
++ * stage since detection happens after. We can't do detection earlier
++ * since MST detection needs connectors to be created first.
++ */
++ if (link->connector_signal & (SIGNAL_TYPE_EDP | SIGNAL_TYPE_LVDS)) {
++ /* Event if registration failed, we should continue with
++ * DM initialization because not having a backlight control
++ * is better then a black screen. */
++ amdgpu_dm_register_backlight_device(dm);
++
++ if (dm->backlight_dev)
++ dm->backlight_link = link;
++ }
++#endif
++
++ return 0;
++}
++
++int amdgpu_dm_encoder_init(
++ struct drm_device *dev,
++ struct amdgpu_encoder *aencoder,
++ uint32_t link_index,
++ struct amdgpu_crtc *acrtc)
++{
++ int res = drm_encoder_init(dev,
++ &aencoder->base,
++ &amdgpu_dm_encoder_funcs,
++ DRM_MODE_ENCODER_TMDS,
++ NULL);
++
++ aencoder->base.possible_crtcs = 1 << link_index;
++
++ if (!res)
++ aencoder->encoder_id = link_index;
++ else
++ aencoder->encoder_id = -1;
++
++ drm_encoder_helper_add(&aencoder->base, &amdgpu_dm_encoder_helper_funcs);
++
++ return res;
++}
++
++enum dm_commit_action {
++ DM_COMMIT_ACTION_NOTHING,
++ DM_COMMIT_ACTION_RESET,
++ DM_COMMIT_ACTION_DPMS_ON,
++ DM_COMMIT_ACTION_DPMS_OFF,
++ DM_COMMIT_ACTION_SET
++};
++
++enum dm_commit_action get_dm_commit_action(struct drm_crtc *crtc,
++ struct drm_crtc_state *state)
++{
++ /* mode changed means either actually mode changed or enabled changed */
++ /* active changed means dpms changed */
++ if (state->mode_changed) {
++ /* if it is got disabled - call reset mode */
++ if (!state->enable)
++ return DM_COMMIT_ACTION_RESET;
++
++ if (state->active)
++ return DM_COMMIT_ACTION_SET;
++ else
++ return DM_COMMIT_ACTION_RESET;
++ } else {
++ /* ! mode_changed */
++
++ /* if it is remain disable - skip it */
++ if (!state->enable)
++ return DM_COMMIT_ACTION_NOTHING;
++
++ if (state->active_changed) {
++ if (state->active) {
++ return DM_COMMIT_ACTION_DPMS_ON;
++ } else {
++ return DM_COMMIT_ACTION_DPMS_OFF;
++ }
++ } else {
++ /* ! active_changed */
++ return DM_COMMIT_ACTION_NOTHING;
++ }
++ }
++}
++
++static void manage_dm_interrupts(
++ struct amdgpu_device *adev,
++ struct amdgpu_crtc *acrtc,
++ bool enable)
++{
++ if (enable) {
++ drm_crtc_vblank_on(&acrtc->base);
++ amdgpu_irq_get(
++ adev,
++ &adev->pageflip_irq,
++ amdgpu_crtc_idx_to_irq_type(
++ adev,
++ acrtc->crtc_id));
++ } else {
++ unsigned long flags;
++ amdgpu_irq_put(
++ adev,
++ &adev->pageflip_irq,
++ amdgpu_crtc_idx_to_irq_type(
++ adev,
++ acrtc->crtc_id));
++ drm_crtc_vblank_off(&acrtc->base);
++
++ /*
++ * should be called here, to guarantee no works left in queue.
++ * As this function sleeps it was bug to call it inside the
++ * amdgpu_dm_flip_cleanup function under locked event_lock
++ */
++ if (acrtc->pflip_works) {
++ flush_work(&acrtc->pflip_works->flip_work);
++ flush_work(&acrtc->pflip_works->unpin_work);
++ }
++
++ /*
++ * TODO: once Vitaly's change to adjust locking in
++ * page_flip_work_func is submitted to base driver move
++ * lock and check to amdgpu_dm_flip_cleanup function
++ */
++
++ spin_lock_irqsave(&adev->ddev->event_lock, flags);
++ if (acrtc->pflip_status != AMDGPU_FLIP_NONE) {
++ /*
++ * this is the case when on reset, last pending pflip
++ * interrupt did not not occur. Clean-up
++ */
++ amdgpu_dm_flip_cleanup(adev, acrtc);
++ }
++ spin_unlock_irqrestore(&adev->ddev->event_lock, flags);
++ }
++}
++
++/*
++ * Handle headless hotplug workaround
++ *
++ * In case of headless hotplug, if plugging the same monitor to the same
++ * DDI, DRM consider it as mode unchanged. We should check whether the
++ * sink pointer changed, and set mode_changed properly to
++ * make sure commit is doing everything.
++ */
++static void handle_headless_hotplug(
++ const struct amdgpu_crtc *acrtc,
++ struct drm_crtc_state *state,
++ struct amdgpu_connector **aconnector)
++{
++ struct amdgpu_connector *old_connector =
++ aconnector_from_drm_crtc_id(&acrtc->base);
++
++ /*
++ * TODO Revisit this. This code is kinda hacky and might break things.
++ */
++
++ if (!old_connector)
++ return;
++
++ if (!*aconnector)
++ *aconnector = old_connector;
++
++ if (acrtc->target && (*aconnector)->dc_sink) {
++ if ((*aconnector)->dc_sink !=
++ acrtc->target->streams[0]->sink) {
++ state->mode_changed = true;
++ }
++ }
++
++ if (!acrtc->target) {
++ /* In case of headless with DPMS on, when system waked up,
++ * if no monitor connected, target is null and will not create
++ * new target, on that condition, we should check
++ * if any connector is connected, if connected,
++ * it means a hot plug happened after wake up,
++ * mode_changed should be set to true to make sure
++ * commit targets will do everything.
++ */
++ state->mode_changed =
++ (*aconnector)->base.status ==
++ connector_status_connected;
++ } else {
++ /* In case of headless hotplug, if plug same monitor to same
++ * DDI, DRM consider it as mode unchanged, we should check
++ * sink pointer changed, and set mode changed properly to
++ * make sure commit doing everything.
++ */
++ /* check if sink has changed from last commit */
++ if ((*aconnector)->dc_sink && (*aconnector)->dc_sink !=
++ acrtc->target->streams[0]->sink)
++ state->mode_changed = true;
++ }
++}
++
++int amdgpu_dm_atomic_commit(
++ struct drm_device *dev,
++ struct drm_atomic_state *state,
++ bool async)
++{
++ struct amdgpu_device *adev = dev->dev_private;
++ struct amdgpu_display_manager *dm = &adev->dm;
++ struct drm_plane *plane;
++ struct drm_plane_state *old_plane_state;
++ uint32_t i;
++ int32_t ret;
++ uint32_t commit_targets_count = 0;
++ uint32_t new_crtcs_count = 0;
++ struct drm_crtc *crtc;
++ struct drm_crtc_state *old_crtc_state;
++
++ struct dc_target *commit_targets[DAL_MAX_CONTROLLERS];
++ struct amdgpu_crtc *new_crtcs[DAL_MAX_CONTROLLERS];
++
++ /* In this step all new fb would be pinned */
++
++ ret = drm_atomic_helper_prepare_planes(dev, state);
++ if (ret)
++ return ret;
++
++ /*
++ * This is the point of no return - everything below never fails except
++ * when the hw goes bonghits. Which means we can commit the new state on
++ * the software side now.
++ */
++
++ drm_atomic_helper_swap_state(dev, state);
++
++ /*
++ * From this point state become old state really. New state is
++ * initialized to appropriate objects and could be accessed from there
++ */
++
++ /*
++ * there is no fences usage yet in state. We can skip the following line
++ * wait_for_fences(dev, state);
++ */
++
++ /* update changed items */
++ for_each_crtc_in_state(state, crtc, old_crtc_state, i) {
++ struct amdgpu_crtc *acrtc;
++ struct amdgpu_connector *aconnector;
++ enum dm_commit_action action;
++ struct drm_crtc_state *new_state = crtc->state;
++
++ acrtc = to_amdgpu_crtc(crtc);
++ aconnector = aconnector_from_drm_crtc(crtc, state);
++
++ /* handles headless hotplug case, updating new_state and
++ * aconnector as needed
++ */
++ handle_headless_hotplug(acrtc, new_state, &aconnector);
++
++ action = get_dm_commit_action(crtc, new_state);
++
++ if (!aconnector) {
++ DRM_ERROR("Can't find connector for crtc %d\n", acrtc->crtc_id);
++ break;
++ }
++
++ switch (action) {
++ case DM_COMMIT_ACTION_DPMS_ON:
++ case DM_COMMIT_ACTION_SET: {
++ const struct drm_connector_helper_funcs *connector_funcs;
++ struct dc_target *new_target =
++ create_target_for_sink(
++ aconnector,
++ &crtc->state->mode);
++
++ if (!new_target) {
++ /*
++ * this could happen because of issues with
++ * userspace notifications delivery.
++ * In this case userspace tries to set mode on
++ * display which is disconnect in fact.
++ * dc_sink in NULL in this case on aconnector.
++ * We expect reset mode will come soon.
++ *
++ * This can also happen when unplug is done
++ * during resume sequence ended
++ */
++ new_state->planes_changed = false;
++ break;
++ }
++
++ if (acrtc->target) {
++ /*
++ * we evade vblanks and pflips on crtc that
++ * should be changed
++ */
++ manage_dm_interrupts(adev, acrtc, false);
++ /* this is the update mode case */
++ dc_target_release(acrtc->target);
++ acrtc->target = NULL;
++ }
++
++ /*
++ * this loop saves set mode crtcs
++ * we needed to enable vblanks once all
++ * resources acquired in dc after dc_commit_targets
++ */
++ new_crtcs[new_crtcs_count] = acrtc;
++ new_crtcs_count++;
++
++ acrtc->target = new_target;
++ acrtc->enabled = true;
++ acrtc->base.enabled = true;
++ connector_funcs = aconnector->base.helper_private;
++ aconnector->base.encoder =
++ connector_funcs->best_encoder(
++ &aconnector->base);
++ break;
++ }
++
++ case DM_COMMIT_ACTION_NOTHING:
++ break;
++
++ case DM_COMMIT_ACTION_DPMS_OFF:
++ case DM_COMMIT_ACTION_RESET:
++ /* i.e. reset mode */
++ if (acrtc->target) {
++ manage_dm_interrupts(adev, acrtc, false);
++
++ dc_target_release(acrtc->target);
++ acrtc->target = NULL;
++ acrtc->enabled = false;
++ acrtc->base.enabled = false;
++ aconnector->base.encoder = NULL;
++ }
++ break;
++ } /* switch() */
++ } /* for_each_crtc_in_state() */
++
++ commit_targets_count = 0;
++
++ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
++
++ struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
++
++ if (acrtc->target) {
++ commit_targets[commit_targets_count] = acrtc->target;
++ ++commit_targets_count;
++ }
++ }
++
++ /* DC is optimized not to do anything if 'targets' didn't change. */
++ dc_commit_targets(dm->dc, commit_targets, commit_targets_count);
++
++ /* update planes when needed */
++ for_each_plane_in_state(state, plane, old_plane_state, i) {
++ struct drm_plane_state *plane_state = plane->state;
++ struct drm_crtc *crtc = plane_state->crtc;
++ struct drm_framebuffer *fb = plane_state->fb;
++
++ if (fb && crtc) {
++ if (!crtc->state->planes_changed)
++ continue;
++
++ if (page_flip_needed(
++ plane_state,
++ old_plane_state))
++ amdgpu_crtc_page_flip(
++ crtc,
++ fb,
++ crtc->state->event,
++ 0);
++ else
++ dm_dc_surface_commit(
++ dm->dc,
++ crtc,
++ to_amdgpu_framebuffer(fb));
++ }
++ }
++
++ for (i = 0; i < new_crtcs_count; i++) {
++ /*
++ * loop to enable interrupts on newly arrived crtc
++ */
++ struct amdgpu_crtc *acrtc = new_crtcs[i];
++
++ manage_dm_interrupts(adev, acrtc, true);
++ dm_crtc_cursor_reset(&acrtc->base);
++
++ }
++
++ drm_atomic_helper_wait_for_vblanks(dev, state);
++
++ /* In this state all old framebuffers would be unpinned */
++
++ drm_atomic_helper_cleanup_planes(dev, state);
++
++ drm_atomic_state_free(state);
++
++ return 0;
++}
++
++int amdgpu_dm_atomic_check(struct drm_device *dev,
++ struct drm_atomic_state *s)
++{
++ struct drm_crtc *crtc;
++ struct drm_crtc_state *crtc_state;
++ struct drm_plane *plane;
++ struct drm_plane_state *plane_state;
++ struct drm_connector *connector;
++ struct drm_connector_state *conn_state;
++ int i, j, ret, set_count;
++ struct dc_validation_set set[MAX_TARGET_NUM] = {{ 0 }};
++ struct amdgpu_device *adev = dev->dev_private;
++ struct amdgpu_connector *aconnector = NULL;
++ set_count = 0;
++
++ ret = drm_atomic_helper_check(dev,s);
++
++ if (ret) {
++ DRM_ERROR("Atomic state integrity validation failed with error :%d !\n",ret);
++ return ret;
++ }
++
++ ret = -EINVAL;
++
++ if (s->num_connector > MAX_TARGET_NUM) {
++ DRM_ERROR("Exceeded max targets number !\n");
++ return ret;
++ }
++
++
++ for_each_crtc_in_state(s, crtc, crtc_state, i) {
++ enum dm_commit_action action;
++ aconnector = NULL;
++
++ action = get_dm_commit_action(crtc, crtc_state);
++ if (action == DM_COMMIT_ACTION_DPMS_OFF || DM_COMMIT_ACTION_RESET)
++ continue;
++
++ for_each_connector_in_state(s, connector, conn_state, j) {
++ if (conn_state->crtc && conn_state->crtc == crtc) {
++ aconnector = to_amdgpu_connector(connector);
++ /*I assume at most once connector for CRTC*/
++ break;
++ }
++ }
++
++ /*In this case validate against existing connector if possible*/
++ if (!aconnector)
++ aconnector = aconnector_from_drm_crtc(crtc, s);
++
++ if (!aconnector || !aconnector->dc_sink)
++ continue;
++
++ set[set_count].surface_count = 0;
++ ret = dm_create_validation_set_for_target(&aconnector->base,
++ &crtc_state->adjusted_mode, &set[set_count]);
++ if (ret)
++ {
++ DRM_ERROR("Creation of validation set target failed !\n");
++ goto end;
++ }
++
++ for_each_plane_in_state(s, plane, plane_state, j) {
++ /*Since we use drm_atomic_helper_set_config as our hook we garnteed to have the mask in correct state*/
++ if (crtc_state->plane_mask & (1 << drm_plane_index(plane))) {
++ if (set[set_count].surface_count == MAX_SURFACE_NUM) {
++ DRM_ERROR("Exceeded max surfaces number per target!\n");
++ ret = MODE_OK;
++ goto end;
++ }
++
++ ret = dm_add_surface_to_validation_set(plane,plane_state,
++ (struct dc_surface **)&(set[set_count].surfaces[set[set_count].surface_count]));
++
++ if (ret) {
++ DRM_ERROR("Failed to add surface for validation!\n");
++ goto end;
++ }
++
++ set[set_count].surface_count++;
++ }
++ }
++
++ set_count++;
++ }
++
++ if (!set_count || dc_validate_resources(adev->dm.dc, set, set_count)) {
++ ret = MODE_OK;
++ }
++end:
++
++ for (i = 0; i < MAX_TARGET_NUM; i++) {
++ if (set[i].target)
++ dc_target_release((struct dc_target *)set[i].target);
++ }
++
++ return ret;
++
++}
+diff --git a/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_types.h b/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_types.h
+new file mode 100644
+index 0000000..bda39be
+--- /dev/null
++++ b/drivers/gpu/drm/amd/dal/amdgpu_dm/amdgpu_dm_types.h
+@@ -0,0 +1,96 @@
++/*
++ * Copyright 2012-13 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 __AMDGPU_DM_TYPES_H__
++#define __AMDGPU_DM_TYPES_H__
++
++#include <drm/drmP.h>
++
++struct plane_addr_flip_info;
++struct amdgpu_framebuffer;
++struct amdgpu_display_manager;
++struct dc_validation_set;
++struct dc_surface;
++
++/*TODO Jodan Hersen use the one in amdgpu_dm*/
++int amdgpu_dm_crtc_init(struct amdgpu_display_manager *dm,
++ struct amdgpu_crtc *amdgpu_crtc,
++ uint32_t link_index);
++int amdgpu_dm_connector_init(struct amdgpu_display_manager *dm,
++ struct amdgpu_connector *amdgpu_connector,
++ uint32_t link_index,
++ struct amdgpu_encoder *amdgpu_encoder);
++int amdgpu_dm_encoder_init(struct drm_device *dev,
++ struct amdgpu_encoder *amdgpu_encoder,
++ uint32_t link_index,
++ struct amdgpu_crtc *amdgpu_crtc);
++
++void amdgpu_dm_crtc_destroy(struct drm_crtc *crtc);
++void amdgpu_dm_connector_destroy(struct drm_connector *connector);
++void amdgpu_dm_encoder_destroy(struct drm_encoder *encoder);
++
++void dm_add_display_info(
++ struct drm_display_info *disp_info,
++ struct amdgpu_display_manager *dm,
++ uint32_t display_index);
++
++int amdgpu_dm_connector_get_modes(struct drm_connector *connector);
++
++struct amdgpu_connector *aconnector_from_drm_crtc(
++ struct drm_crtc *crtc,
++ struct drm_atomic_state *state);
++
++int amdgpu_dm_atomic_commit(
++ struct drm_device *dev,
++ struct drm_atomic_state *state,
++ bool async);
++int amdgpu_dm_atomic_check(struct drm_device *dev,
++ struct drm_atomic_state *state);
++
++int dm_create_validation_set_for_target(
++ struct drm_connector *connector,
++ struct drm_display_mode *mode,
++ struct dc_validation_set *val_set);
++int dm_add_surface_to_validation_set(
++ struct drm_plane *plane,
++ struct drm_plane_state *state,
++ struct dc_surface **surface);
++
++void amdgpu_dm_connector_funcs_reset(struct drm_connector *connector);
++struct drm_connector_state *amdgpu_dm_connector_atomic_duplicate_state(
++ struct drm_connector *connector);
++void amdgpu_dm_connector_atomic_destroy_state(
++ struct drm_connector *connector,
++ struct drm_connector_state *state);
++int amdgpu_dm_connector_atomic_set_property(
++ struct drm_connector *connector,
++ struct drm_connector_state *state,
++ struct drm_property *property,
++ uint64_t val);
++
++extern const struct drm_encoder_helper_funcs amdgpu_dm_encoder_helper_funcs;
++
++#endif /* __AMDGPU_DM_TYPES_H__ */
+--
+2.7.4
+