2017-09-12 19:58:20 +00:00
|
|
|
/*
|
|
|
|
* 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 "dm_services_types.h"
|
|
|
|
#include "dc.h"
|
|
|
|
|
|
|
|
#include "vid.h"
|
|
|
|
#include "amdgpu.h"
|
2017-01-12 14:09:08 +00:00
|
|
|
#include "amdgpu_display.h"
|
2017-09-12 19:58:20 +00:00
|
|
|
#include "atom.h"
|
|
|
|
#include "amdgpu_dm.h"
|
|
|
|
#include "amdgpu_dm_types.h"
|
|
|
|
|
|
|
|
#include "amd_shared.h"
|
|
|
|
#include "amdgpu_dm_irq.h"
|
|
|
|
#include "dm_helpers.h"
|
|
|
|
|
|
|
|
#include "ivsrcid/ivsrcid_vislands30.h"
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/moduleparam.h>
|
|
|
|
#include <linux/version.h>
|
|
|
|
|
|
|
|
#include <drm/drm_atomic.h>
|
|
|
|
#include <drm/drm_atomic_helper.h>
|
|
|
|
#include <drm/drm_dp_mst_helper.h>
|
|
|
|
|
|
|
|
#include "modules/inc/mod_freesync.h"
|
|
|
|
|
2017-06-15 20:27:42 +00:00
|
|
|
#if defined(CONFIG_DRM_AMD_DC_DCN1_0)
|
|
|
|
#include "ivsrcid/irqsrcs_dcn_1_0.h"
|
|
|
|
|
|
|
|
#include "raven1/DCN/dcn_1_0_offset.h"
|
|
|
|
#include "raven1/DCN/dcn_1_0_sh_mask.h"
|
|
|
|
#include "vega10/soc15ip.h"
|
|
|
|
|
|
|
|
#include "soc15_common.h"
|
|
|
|
#endif
|
|
|
|
|
2017-06-15 20:24:01 +00:00
|
|
|
static enum drm_plane_type dm_surfaces_type_default[AMDGPU_MAX_PLANES] = {
|
|
|
|
DRM_PLANE_TYPE_PRIMARY,
|
|
|
|
DRM_PLANE_TYPE_PRIMARY,
|
|
|
|
DRM_PLANE_TYPE_PRIMARY,
|
|
|
|
DRM_PLANE_TYPE_PRIMARY,
|
|
|
|
DRM_PLANE_TYPE_PRIMARY,
|
|
|
|
DRM_PLANE_TYPE_PRIMARY,
|
|
|
|
};
|
|
|
|
|
|
|
|
static enum drm_plane_type dm_surfaces_type_carizzo[AMDGPU_MAX_PLANES] = {
|
|
|
|
DRM_PLANE_TYPE_PRIMARY,
|
|
|
|
DRM_PLANE_TYPE_PRIMARY,
|
|
|
|
DRM_PLANE_TYPE_PRIMARY,
|
|
|
|
DRM_PLANE_TYPE_OVERLAY,/* YUV Capable Underlay */
|
|
|
|
};
|
|
|
|
|
|
|
|
static enum drm_plane_type dm_surfaces_type_stoney[AMDGPU_MAX_PLANES] = {
|
|
|
|
DRM_PLANE_TYPE_PRIMARY,
|
|
|
|
DRM_PLANE_TYPE_PRIMARY,
|
|
|
|
DRM_PLANE_TYPE_OVERLAY, /* YUV Capable Underlay */
|
|
|
|
};
|
|
|
|
|
2017-09-12 19:58:20 +00:00
|
|
|
/*
|
|
|
|
* 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];
|
|
|
|
|
2016-12-29 20:27:12 +00:00
|
|
|
if (NULL == acrtc->stream) {
|
|
|
|
DRM_ERROR("dc_stream is NULL for crtc '%d'!\n", crtc);
|
2017-09-12 19:58:20 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-12-29 20:27:12 +00:00
|
|
|
return dc_stream_get_vblank_counter(acrtc->stream);
|
2017-09-12 19:58:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dm_crtc_get_scanoutpos(struct amdgpu_device *adev, int crtc,
|
|
|
|
u32 *vbl, u32 *position)
|
|
|
|
{
|
2017-04-11 19:15:28 +00:00
|
|
|
uint32_t v_blank_start, v_blank_end, h_position, v_position;
|
|
|
|
|
2017-09-12 19:58:20 +00:00
|
|
|
if ((crtc < 0) || (crtc >= adev->mode_info.num_crtc))
|
|
|
|
return -EINVAL;
|
|
|
|
else {
|
|
|
|
struct amdgpu_crtc *acrtc = adev->mode_info.crtcs[crtc];
|
|
|
|
|
2016-12-29 20:27:12 +00:00
|
|
|
if (NULL == acrtc->stream) {
|
|
|
|
DRM_ERROR("dc_stream is NULL for crtc '%d'!\n", crtc);
|
2017-09-12 19:58:20 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-04-11 19:15:28 +00:00
|
|
|
/*
|
|
|
|
* TODO rework base driver to use values directly.
|
|
|
|
* for now parse it back into reg-format
|
|
|
|
*/
|
|
|
|
dc_stream_get_scanoutpos(acrtc->stream,
|
|
|
|
&v_blank_start,
|
|
|
|
&v_blank_end,
|
|
|
|
&h_position,
|
|
|
|
&v_position);
|
|
|
|
|
|
|
|
*position = (v_position) || (h_position << 16);
|
|
|
|
*vbl = (v_blank_start) || (v_blank_end << 16);
|
2017-09-12 19:58:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool dm_is_idle(void *handle)
|
|
|
|
{
|
|
|
|
/* XXX todo */
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dm_wait_for_idle(void *handle)
|
|
|
|
{
|
|
|
|
/* XXX todo */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool dm_check_soft_reset(void *handle)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dm_soft_reset(void *handle)
|
|
|
|
{
|
|
|
|
/* XXX todo */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct amdgpu_crtc *get_crtc_by_otg_inst(
|
|
|
|
struct amdgpu_device *adev,
|
|
|
|
int otg_inst)
|
|
|
|
{
|
|
|
|
struct drm_device *dev = adev->ddev;
|
|
|
|
struct drm_crtc *crtc;
|
|
|
|
struct amdgpu_crtc *amdgpu_crtc;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* following if is check inherited from both functions where this one is
|
|
|
|
* used now. Need to be checked why it could happen.
|
|
|
|
*/
|
|
|
|
if (otg_inst == -1) {
|
|
|
|
WARN_ON(1);
|
|
|
|
return adev->mode_info.crtcs[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
|
|
|
amdgpu_crtc = to_amdgpu_crtc(crtc);
|
|
|
|
|
|
|
|
if (amdgpu_crtc->otg_inst == otg_inst)
|
|
|
|
return amdgpu_crtc;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dm_pflip_high_irq(void *interrupt_params)
|
|
|
|
{
|
|
|
|
struct amdgpu_crtc *amdgpu_crtc;
|
|
|
|
struct common_irq_params *irq_params = interrupt_params;
|
|
|
|
struct amdgpu_device *adev = irq_params->adev;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
amdgpu_crtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_PFLIP);
|
|
|
|
|
|
|
|
/* IRQ could occur when in initial stage */
|
|
|
|
/*TODO work and BO cleanup */
|
|
|
|
if (amdgpu_crtc == NULL) {
|
|
|
|
DRM_DEBUG_DRIVER("CRTC is null, returning.\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_lock_irqsave(&adev->ddev->event_lock, flags);
|
|
|
|
|
|
|
|
if (amdgpu_crtc->pflip_status != AMDGPU_FLIP_SUBMITTED){
|
|
|
|
DRM_DEBUG_DRIVER("amdgpu_crtc->pflip_status = %d !=AMDGPU_FLIP_SUBMITTED(%d) on crtc:%d[%p] \n",
|
|
|
|
amdgpu_crtc->pflip_status,
|
|
|
|
AMDGPU_FLIP_SUBMITTED,
|
|
|
|
amdgpu_crtc->crtc_id,
|
|
|
|
amdgpu_crtc);
|
|
|
|
spin_unlock_irqrestore(&adev->ddev->event_lock, flags);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* wakeup usersapce */
|
2017-04-20 19:57:05 +00:00
|
|
|
if (amdgpu_crtc->event
|
|
|
|
&& amdgpu_crtc->event->event.base.type
|
|
|
|
== DRM_EVENT_FLIP_COMPLETE) {
|
2017-04-24 09:46:44 +00:00
|
|
|
/* Update to correct count/ts if racing with vblank irq */
|
|
|
|
drm_crtc_accurate_vblank_count(&amdgpu_crtc->base);
|
|
|
|
|
2017-04-20 19:57:05 +00:00
|
|
|
drm_crtc_send_vblank_event(&amdgpu_crtc->base, amdgpu_crtc->event);
|
|
|
|
/* page flip completed. clean up */
|
|
|
|
amdgpu_crtc->event = NULL;
|
|
|
|
} else
|
|
|
|
WARN_ON(1);
|
2017-09-12 19:58:20 +00:00
|
|
|
|
2017-04-20 19:57:05 +00:00
|
|
|
amdgpu_crtc->pflip_status = AMDGPU_FLIP_NONE;
|
2017-09-12 19:58:20 +00:00
|
|
|
spin_unlock_irqrestore(&adev->ddev->event_lock, flags);
|
|
|
|
|
2017-04-20 19:57:05 +00:00
|
|
|
DRM_DEBUG_DRIVER("%s - crtc :%d[%p], pflip_stat:AMDGPU_FLIP_NONE\n",
|
|
|
|
__func__, amdgpu_crtc->crtc_id, amdgpu_crtc);
|
2017-09-12 19:58:20 +00:00
|
|
|
|
|
|
|
drm_crtc_vblank_put(&amdgpu_crtc->base);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dm_crtc_high_irq(void *interrupt_params)
|
|
|
|
{
|
|
|
|
struct common_irq_params *irq_params = interrupt_params;
|
|
|
|
struct amdgpu_device *adev = irq_params->adev;
|
|
|
|
uint8_t crtc_index = 0;
|
|
|
|
struct amdgpu_crtc *acrtc;
|
|
|
|
|
2017-04-19 17:29:11 +00:00
|
|
|
acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VBLANK);
|
2017-09-12 19:58:20 +00:00
|
|
|
|
|
|
|
if (acrtc)
|
|
|
|
crtc_index = acrtc->crtc_id;
|
|
|
|
|
|
|
|
drm_handle_vblank(adev->ddev, crtc_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 hotplug_notify_work_func(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct amdgpu_display_manager *dm = container_of(work, struct amdgpu_display_manager, mst_hotplug_work);
|
|
|
|
struct drm_device *dev = dm->ddev;
|
|
|
|
|
|
|
|
drm_kms_helper_hotplug_event(dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Init display KMS
|
|
|
|
*
|
|
|
|
* Returns 0 on success
|
|
|
|
*/
|
|
|
|
int amdgpu_dm_init(struct amdgpu_device *adev)
|
|
|
|
{
|
|
|
|
struct dc_init_data init_data;
|
|
|
|
adev->dm.ddev = adev->ddev;
|
|
|
|
adev->dm.adev = adev;
|
|
|
|
|
|
|
|
DRM_INFO("DAL is enabled\n");
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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.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;
|
|
|
|
|
|
|
|
init_data.dce_environment = DCE_ENV_PRODUCTION_DRV;
|
|
|
|
|
|
|
|
/* Display Core create. */
|
|
|
|
adev->dm.dc = dc_create(&init_data);
|
|
|
|
|
|
|
|
if (!adev->dm.dc)
|
|
|
|
DRM_INFO("Display Core failed to initialize!\n");
|
|
|
|
|
|
|
|
INIT_WORK(&adev->dm.mst_hotplug_work, hotplug_notify_work_func);
|
|
|
|
|
|
|
|
adev->dm.freesync_module = mod_freesync_create(adev->dm.dc);
|
|
|
|
if (!adev->dm.freesync_module) {
|
|
|
|
DRM_ERROR(
|
|
|
|
"amdgpu: failed to initialize freesync_module.\n");
|
|
|
|
} else
|
|
|
|
DRM_INFO("amdgpu: freesync_module init done %p.\n",
|
|
|
|
adev->dm.freesync_module);
|
|
|
|
|
|
|
|
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 */
|
2017-02-22 19:24:11 +00:00
|
|
|
adev->ddev->mode_config.cursor_width = adev->dm.dc->caps.max_cursor_size;
|
|
|
|
adev->ddev->mode_config.cursor_height = adev->dm.dc->caps.max_cursor_size;
|
2017-09-12 19:58:20 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
amdgpu_dm_destroy_drm_device(&adev->dm);
|
|
|
|
/*
|
|
|
|
* TODO: pageflip, vlank interrupt
|
|
|
|
*
|
|
|
|
* amdgpu_dm_irq_fini(adev);
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (adev->dm.cgs_device) {
|
|
|
|
amdgpu_cgs_destroy_device(adev->dm.cgs_device);
|
|
|
|
adev->dm.cgs_device = NULL;
|
|
|
|
}
|
|
|
|
if (adev->dm.freesync_module) {
|
|
|
|
mod_freesync_destroy(adev->dm.freesync_module);
|
|
|
|
adev->dm.freesync_module = 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;
|
|
|
|
}
|
|
|
|
|
2016-12-29 18:21:31 +00:00
|
|
|
static int detect_mst_link_for_all_connectors(struct drm_device *dev)
|
2017-09-12 19:58:20 +00:00
|
|
|
{
|
|
|
|
struct amdgpu_connector *aconnector;
|
|
|
|
struct drm_connector *connector;
|
2016-12-29 18:21:31 +00:00
|
|
|
int ret = 0;
|
2017-09-12 19:58:20 +00:00
|
|
|
|
|
|
|
drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
|
|
|
|
|
|
|
|
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
|
|
|
|
aconnector = to_amdgpu_connector(connector);
|
2016-12-29 18:21:31 +00:00
|
|
|
if (aconnector->dc_link->type == dc_connection_mst_branch) {
|
|
|
|
DRM_INFO("DM_MST: starting TM on aconnector: %p [id: %d]\n",
|
|
|
|
aconnector, aconnector->base.base.id);
|
|
|
|
|
|
|
|
ret = drm_dp_mst_topology_mgr_set_mst(&aconnector->mst_mgr, true);
|
|
|
|
if (ret < 0) {
|
|
|
|
DRM_ERROR("DM_MST: Failed to start MST\n");
|
|
|
|
((struct dc_link *)aconnector->dc_link)->type = dc_connection_single;
|
|
|
|
return ret;
|
2017-09-12 19:58:20 +00:00
|
|
|
}
|
2016-12-29 18:21:31 +00:00
|
|
|
}
|
2017-09-12 19:58:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
drm_modeset_unlock(&dev->mode_config.connection_mutex);
|
2016-12-29 18:21:31 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dm_late_init(void *handle)
|
|
|
|
{
|
|
|
|
struct drm_device *dev = ((struct amdgpu_device *)handle)->ddev;
|
|
|
|
int r = detect_mst_link_for_all_connectors(dev);
|
|
|
|
|
|
|
|
return r;
|
2017-09-12 19:58:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void s3_handle_mst(struct drm_device *dev, bool suspend)
|
|
|
|
{
|
|
|
|
struct amdgpu_connector *aconnector;
|
|
|
|
struct drm_connector *connector;
|
|
|
|
|
|
|
|
drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
|
|
|
|
|
|
|
|
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
|
|
|
|
aconnector = to_amdgpu_connector(connector);
|
|
|
|
if (aconnector->dc_link->type == dc_connection_mst_branch &&
|
|
|
|
!aconnector->mst_port) {
|
|
|
|
|
|
|
|
if (suspend)
|
|
|
|
drm_dp_mst_topology_mgr_suspend(&aconnector->mst_mgr);
|
|
|
|
else
|
|
|
|
drm_dp_mst_topology_mgr_resume(&aconnector->mst_mgr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
drm_modeset_unlock(&dev->mode_config.connection_mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
s3_handle_mst(adev->ddev, true);
|
|
|
|
|
|
|
|
amdgpu_dm_irq_suspend(adev);
|
|
|
|
|
2017-04-20 19:59:25 +00:00
|
|
|
adev->dm.cached_state = drm_atomic_helper_suspend(adev->ddev);
|
|
|
|
|
2017-09-12 19:58:20 +00:00
|
|
|
dc_set_power_state(
|
|
|
|
dm->dc,
|
2017-04-20 19:59:25 +00:00
|
|
|
DC_ACPI_CM_POWER_STATE_D3
|
|
|
|
);
|
2017-09-12 19:58:20 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct amdgpu_connector *amdgpu_dm_find_first_crct_matching_connector(
|
|
|
|
struct drm_atomic_state *state,
|
|
|
|
struct drm_crtc *crtc,
|
|
|
|
bool from_state_var)
|
|
|
|
{
|
|
|
|
uint32_t i;
|
|
|
|
struct drm_connector_state *conn_state;
|
|
|
|
struct drm_connector *connector;
|
|
|
|
struct drm_crtc *crtc_from_state;
|
|
|
|
|
|
|
|
for_each_connector_in_state(
|
|
|
|
state,
|
|
|
|
connector,
|
|
|
|
conn_state,
|
|
|
|
i) {
|
|
|
|
crtc_from_state =
|
|
|
|
from_state_var ?
|
|
|
|
conn_state->crtc :
|
|
|
|
connector->state->crtc;
|
|
|
|
|
|
|
|
if (crtc_from_state == crtc)
|
|
|
|
return to_amdgpu_connector(connector);
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dm_resume(void *handle)
|
|
|
|
{
|
|
|
|
struct amdgpu_device *adev = handle;
|
|
|
|
struct amdgpu_display_manager *dm = &adev->dm;
|
|
|
|
|
|
|
|
/* power on hardware */
|
|
|
|
dc_set_power_state(
|
|
|
|
dm->dc,
|
2017-04-20 19:59:25 +00:00
|
|
|
DC_ACPI_CM_POWER_STATE_D0
|
|
|
|
);
|
2017-09-12 19:58:20 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int amdgpu_dm_display_resume(struct amdgpu_device *adev )
|
|
|
|
{
|
|
|
|
struct drm_device *ddev = adev->ddev;
|
|
|
|
struct amdgpu_display_manager *dm = &adev->dm;
|
|
|
|
struct amdgpu_connector *aconnector;
|
|
|
|
struct drm_connector *connector;
|
|
|
|
struct drm_crtc *crtc;
|
2017-04-20 19:59:25 +00:00
|
|
|
struct drm_crtc_state *crtc_state;
|
|
|
|
int ret = 0;
|
|
|
|
int i;
|
2017-09-12 19:58:20 +00:00
|
|
|
|
|
|
|
/* program HPD filter */
|
|
|
|
dc_resume(dm->dc);
|
|
|
|
|
|
|
|
/* On resume we need to rewrite the MSTM control bits to enamble MST*/
|
|
|
|
s3_handle_mst(ddev, false);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* early enable HPD Rx IRQ, should be done before set mode as short
|
|
|
|
* pulse interrupts are used for MST
|
|
|
|
*/
|
|
|
|
amdgpu_dm_irq_resume_early(adev);
|
|
|
|
|
|
|
|
/* Do detection*/
|
|
|
|
list_for_each_entry(connector,
|
|
|
|
&ddev->mode_config.connector_list, head) {
|
|
|
|
aconnector = to_amdgpu_connector(connector);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* this is the case when traversing through already created
|
|
|
|
* MST connectors, should be skipped
|
|
|
|
*/
|
|
|
|
if (aconnector->mst_port)
|
|
|
|
continue;
|
|
|
|
|
2017-04-26 12:09:56 +00:00
|
|
|
mutex_lock(&aconnector->hpd_lock);
|
2017-09-12 19:58:20 +00:00
|
|
|
dc_link_detect(aconnector->dc_link, false);
|
|
|
|
aconnector->dc_sink = NULL;
|
|
|
|
amdgpu_dm_update_connector_after_detect(aconnector);
|
2017-04-26 12:09:56 +00:00
|
|
|
mutex_unlock(&aconnector->hpd_lock);
|
2017-09-12 19:58:20 +00:00
|
|
|
}
|
|
|
|
|
2017-04-20 19:59:25 +00:00
|
|
|
/* Force mode set in atomic comit */
|
|
|
|
for_each_crtc_in_state(adev->dm.cached_state, crtc, crtc_state, i)
|
|
|
|
crtc_state->active_changed = true;
|
|
|
|
|
|
|
|
ret = drm_atomic_helper_resume(ddev, adev->dm.cached_state);
|
2017-09-12 19:58:20 +00:00
|
|
|
|
2017-03-31 18:15:31 +00:00
|
|
|
amdgpu_dm_irq_resume_late(adev);
|
2017-09-12 19:58:20 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct amd_ip_funcs amdgpu_dm_funcs = {
|
|
|
|
.name = "dm",
|
|
|
|
.early_init = dm_early_init,
|
2016-12-29 18:21:31 +00:00
|
|
|
.late_init = dm_late_init,
|
2017-09-12 19:58:20 +00:00
|
|
|
.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,
|
|
|
|
.check_soft_reset = dm_check_soft_reset,
|
|
|
|
.soft_reset = dm_soft_reset,
|
|
|
|
.set_clockgating_state = dm_set_clockgating_state,
|
|
|
|
.set_powergating_state = dm_set_powergating_state,
|
|
|
|
};
|
|
|
|
|
|
|
|
const struct amdgpu_ip_block_version dm_ip_block =
|
|
|
|
{
|
|
|
|
.type = AMD_IP_BLOCK_TYPE_DCE,
|
|
|
|
.major = 1,
|
|
|
|
.minor = 0,
|
|
|
|
.rev = 0,
|
|
|
|
.funcs = &amdgpu_dm_funcs,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* TODO: it is temporary non-const, should fixed later */
|
|
|
|
static struct drm_mode_config_funcs amdgpu_dm_mode_funcs = {
|
2017-01-12 14:09:08 +00:00
|
|
|
.fb_create = amdgpu_user_framebuffer_create,
|
|
|
|
.output_poll_changed = amdgpu_output_poll_changed,
|
2017-09-12 19:58:20 +00:00
|
|
|
.atomic_check = amdgpu_dm_atomic_check,
|
2017-04-20 19:57:05 +00:00
|
|
|
.atomic_commit = drm_atomic_helper_commit
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct drm_mode_config_helper_funcs amdgpu_dm_mode_config_helperfuncs = {
|
|
|
|
.atomic_commit_tail = amdgpu_dm_atomic_commit_tail
|
2017-09-12 19:58:20 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
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_sink *sink;
|
|
|
|
|
|
|
|
/* MST handled by drm_mst framework */
|
|
|
|
if (aconnector->mst_mgr.mst_state == true)
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
sink = aconnector->dc_link->local_sink;
|
|
|
|
|
|
|
|
/* Edid mgmt connector gets first update only in mode_valid hook and then
|
|
|
|
* the connector sink is set to either fake or physical sink depends on link status.
|
|
|
|
* don't do it here if u are during boot
|
|
|
|
*/
|
|
|
|
if (aconnector->base.force != DRM_FORCE_UNSPECIFIED
|
|
|
|
&& aconnector->dc_em_sink) {
|
|
|
|
|
2016-12-29 20:27:12 +00:00
|
|
|
/* For S3 resume with headless use eml_sink to fake stream
|
2017-09-12 19:58:20 +00:00
|
|
|
* because on resume connecotr->sink is set ti NULL
|
|
|
|
*/
|
|
|
|
mutex_lock(&dev->mode_config.mutex);
|
|
|
|
|
|
|
|
if (sink) {
|
2016-12-07 19:10:05 +00:00
|
|
|
if (aconnector->dc_sink) {
|
2017-09-12 19:58:20 +00:00
|
|
|
amdgpu_dm_remove_sink_from_freesync_module(
|
|
|
|
connector);
|
2016-12-07 19:10:05 +00:00
|
|
|
/* retain and release bellow are used for
|
|
|
|
* bump up refcount for sink because the link don't point
|
|
|
|
* to it anymore after disconnect so on next crtc to connector
|
|
|
|
* reshuffle by UMD we will get into unwanted dc_sink release
|
|
|
|
*/
|
|
|
|
if (aconnector->dc_sink != aconnector->dc_em_sink)
|
|
|
|
dc_sink_release(aconnector->dc_sink);
|
|
|
|
}
|
2017-09-12 19:58:20 +00:00
|
|
|
aconnector->dc_sink = sink;
|
|
|
|
amdgpu_dm_add_sink_to_freesync_module(
|
|
|
|
connector, aconnector->edid);
|
|
|
|
} else {
|
|
|
|
amdgpu_dm_remove_sink_from_freesync_module(connector);
|
|
|
|
if (!aconnector->dc_sink)
|
|
|
|
aconnector->dc_sink = aconnector->dc_em_sink;
|
2016-12-07 19:10:05 +00:00
|
|
|
else if (aconnector->dc_sink != aconnector->dc_em_sink)
|
|
|
|
dc_sink_retain(aconnector->dc_sink);
|
2017-09-12 19:58:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
mutex_unlock(&dev->mode_config.mutex);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TODO: temporary guard to look for proper fix
|
|
|
|
* if this sink is MST sink, we should not do anything
|
|
|
|
*/
|
|
|
|
if (sink && sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT_MST)
|
|
|
|
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. */
|
|
|
|
if (aconnector->dc_sink)
|
|
|
|
amdgpu_dm_remove_sink_from_freesync_module(
|
|
|
|
connector);
|
|
|
|
|
|
|
|
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_add_sink_to_freesync_module(connector, aconnector->edid);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
amdgpu_dm_remove_sink_from_freesync_module(connector);
|
|
|
|
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;
|
|
|
|
|
|
|
|
/* In case of failure or MST no need to update connector status or notify the OS
|
|
|
|
* since (for MST case) MST does this in it's own context.
|
|
|
|
*/
|
|
|
|
mutex_lock(&aconnector->hpd_lock);
|
|
|
|
if (dc_link_detect(aconnector->dc_link, false)) {
|
|
|
|
amdgpu_dm_update_connector_after_detect(aconnector);
|
|
|
|
|
|
|
|
|
|
|
|
drm_modeset_lock_all(dev);
|
|
|
|
dm_restore_drm_connector_state(dev, connector);
|
|
|
|
drm_modeset_unlock_all(dev);
|
|
|
|
|
|
|
|
if (aconnector->base.force == DRM_FORCE_UNSPECIFIED)
|
|
|
|
drm_kms_helper_hotplug_event(dev);
|
|
|
|
}
|
|
|
|
mutex_unlock(&aconnector->hpd_lock);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dm_handle_hpd_rx_irq(struct amdgpu_connector *aconnector)
|
|
|
|
{
|
|
|
|
uint8_t esi[DP_PSR_ERROR_STATUS - DP_SINK_COUNT_ESI] = { 0 };
|
|
|
|
uint8_t dret;
|
|
|
|
bool new_irq_handled = false;
|
|
|
|
int dpcd_addr;
|
|
|
|
int dpcd_bytes_to_read;
|
|
|
|
|
|
|
|
const int max_process_count = 30;
|
|
|
|
int process_count = 0;
|
|
|
|
|
|
|
|
const struct dc_link_status *link_status = dc_link_get_status(aconnector->dc_link);
|
|
|
|
|
|
|
|
if (link_status->dpcd_caps->dpcd_rev.raw < 0x12) {
|
|
|
|
dpcd_bytes_to_read = DP_LANE0_1_STATUS - DP_SINK_COUNT;
|
|
|
|
/* DPCD 0x200 - 0x201 for downstream IRQ */
|
|
|
|
dpcd_addr = DP_SINK_COUNT;
|
|
|
|
} else {
|
|
|
|
dpcd_bytes_to_read = DP_PSR_ERROR_STATUS - DP_SINK_COUNT_ESI;
|
|
|
|
/* DPCD 0x2002 - 0x2005 for downstream IRQ */
|
|
|
|
dpcd_addr = DP_SINK_COUNT_ESI;
|
|
|
|
}
|
|
|
|
|
|
|
|
dret = drm_dp_dpcd_read(
|
|
|
|
&aconnector->dm_dp_aux.aux,
|
|
|
|
dpcd_addr,
|
|
|
|
esi,
|
|
|
|
dpcd_bytes_to_read);
|
|
|
|
|
|
|
|
while (dret == dpcd_bytes_to_read &&
|
|
|
|
process_count < max_process_count) {
|
|
|
|
uint8_t retry;
|
|
|
|
dret = 0;
|
|
|
|
|
|
|
|
process_count++;
|
|
|
|
|
|
|
|
DRM_DEBUG_KMS("ESI %02x %02x %02x\n", esi[0], esi[1], esi[2]);
|
|
|
|
/* handle HPD short pulse irq */
|
|
|
|
if (aconnector->mst_mgr.mst_state)
|
|
|
|
drm_dp_mst_hpd_irq(
|
|
|
|
&aconnector->mst_mgr,
|
|
|
|
esi,
|
|
|
|
&new_irq_handled);
|
|
|
|
|
|
|
|
if (new_irq_handled) {
|
|
|
|
/* ACK at DPCD to notify down stream */
|
|
|
|
const int ack_dpcd_bytes_to_write =
|
|
|
|
dpcd_bytes_to_read - 1;
|
|
|
|
|
|
|
|
for (retry = 0; retry < 3; retry++) {
|
|
|
|
uint8_t wret;
|
|
|
|
|
|
|
|
wret = drm_dp_dpcd_write(
|
|
|
|
&aconnector->dm_dp_aux.aux,
|
|
|
|
dpcd_addr + 1,
|
|
|
|
&esi[1],
|
|
|
|
ack_dpcd_bytes_to_write);
|
|
|
|
if (wret == ack_dpcd_bytes_to_write)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check if there is new irq to be handle */
|
|
|
|
dret = drm_dp_dpcd_read(
|
|
|
|
&aconnector->dm_dp_aux.aux,
|
|
|
|
dpcd_addr,
|
|
|
|
esi,
|
|
|
|
dpcd_bytes_to_read);
|
|
|
|
|
|
|
|
new_irq_handled = false;
|
|
|
|
} else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (process_count == max_process_count)
|
|
|
|
DRM_DEBUG_KMS("Loop exceeded max iterations\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
const struct dc_link *dc_link = aconnector->dc_link;
|
|
|
|
bool is_mst_root_connector = aconnector->mst_mgr.mst_state;
|
|
|
|
|
|
|
|
/* TODO:Temporary add mutex to protect hpd interrupt not have a gpio
|
|
|
|
* conflict, after implement i2c helper, this mutex should be
|
|
|
|
* retired.
|
|
|
|
*/
|
|
|
|
if (aconnector->dc_link->type != dc_connection_mst_branch)
|
|
|
|
mutex_lock(&aconnector->hpd_lock);
|
|
|
|
|
|
|
|
if (dc_link_handle_hpd_rx_irq(aconnector->dc_link) &&
|
|
|
|
!is_mst_root_connector) {
|
|
|
|
/* Downstream Port status changed. */
|
|
|
|
if (dc_link_detect(aconnector->dc_link, false)) {
|
|
|
|
amdgpu_dm_update_connector_after_detect(aconnector);
|
|
|
|
|
|
|
|
|
|
|
|
drm_modeset_lock_all(dev);
|
|
|
|
dm_restore_drm_connector_state(dev, connector);
|
|
|
|
drm_modeset_unlock_all(dev);
|
|
|
|
|
|
|
|
drm_kms_helper_hotplug_event(dev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ((dc_link->cur_link_settings.lane_count != LANE_COUNT_UNKNOWN) ||
|
|
|
|
(dc_link->type == dc_connection_mst_branch))
|
|
|
|
dm_handle_hpd_rx_irq(aconnector);
|
|
|
|
|
|
|
|
if (aconnector->dc_link->type != dc_connection_mst_branch)
|
|
|
|
mutex_unlock(&aconnector->hpd_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
if (DC_IRQ_SOURCE_INVALID != dc_link->irq_source_hpd) {
|
|
|
|
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;
|
2017-06-15 20:20:24 +00:00
|
|
|
unsigned client_id = AMDGPU_IH_CLIENTID_LEGACY;
|
|
|
|
|
2017-06-15 20:27:42 +00:00
|
|
|
if (adev->asic_type == CHIP_VEGA10 ||
|
|
|
|
adev->asic_type == CHIP_RAVEN)
|
2017-06-15 20:20:24 +00:00
|
|
|
client_id = AMDGPU_IH_CLIENTID_DCE;
|
2017-09-12 19:58:20 +00:00
|
|
|
|
|
|
|
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. */
|
|
|
|
|
2017-04-19 17:29:11 +00:00
|
|
|
/* Use VBLANK interrupt */
|
2017-03-23 19:30:35 +00:00
|
|
|
for (i = VISLANDS30_IV_SRCID_D1_VERTICAL_INTERRUPT0; i <= VISLANDS30_IV_SRCID_D6_VERTICAL_INTERRUPT0; i++) {
|
2017-06-15 20:20:24 +00:00
|
|
|
r = amdgpu_irq_add_id(adev, client_id, i, &adev->crtc_irq);
|
2017-09-12 19:58:20 +00:00
|
|
|
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 =
|
2017-04-19 17:55:35 +00:00
|
|
|
dc_interrupt_to_irq_source(dc, i, 0);
|
2017-09-12 19:58:20 +00:00
|
|
|
|
2017-04-19 17:29:11 +00:00
|
|
|
c_irq_params = &adev->dm.vblank_params[int_params.irq_source - DC_IRQ_SOURCE_VBLANK1];
|
2017-09-12 19:58:20 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2017-04-19 17:55:35 +00:00
|
|
|
/* Use GRPH_PFLIP interrupt */
|
2017-09-12 19:58:20 +00:00
|
|
|
for (i = VISLANDS30_IV_SRCID_D1_GRPH_PFLIP;
|
|
|
|
i <= VISLANDS30_IV_SRCID_D6_GRPH_PFLIP; i += 2) {
|
2017-06-15 20:20:24 +00:00
|
|
|
r = amdgpu_irq_add_id(adev, client_id, i, &adev->pageflip_irq);
|
2017-09-12 19:58:20 +00:00
|
|
|
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 */
|
2017-06-15 20:20:24 +00:00
|
|
|
r = amdgpu_irq_add_id(adev, client_id,
|
|
|
|
VISLANDS30_IV_SRCID_HOTPLUG_DETECT_A, &adev->hpd_irq);
|
2017-09-12 19:58:20 +00:00
|
|
|
if (r) {
|
|
|
|
DRM_ERROR("Failed to add hpd irq id!\n");
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
register_hpd_handlers(adev);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-06-15 20:27:42 +00:00
|
|
|
#if defined(CONFIG_DRM_AMD_DC_DCN1_0)
|
|
|
|
/* Register IRQ sources and initialize IRQ callbacks */
|
|
|
|
static int dcn10_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;
|
|
|
|
|
|
|
|
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.
|
|
|
|
* */
|
|
|
|
|
|
|
|
/* Use VSTARTUP interrupt */
|
|
|
|
for (i = DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP;
|
|
|
|
i <= DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP + adev->mode_info.num_crtc - 1;
|
|
|
|
i++) {
|
|
|
|
r = amdgpu_irq_add_id(adev, AMDGPU_IH_CLIENTID_DCE, 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.vblank_params[int_params.irq_source - DC_IRQ_SOURCE_VBLANK1];
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Use GRPH_PFLIP interrupt */
|
|
|
|
for (i = DCN_1_0__SRCID__HUBP0_FLIP_INTERRUPT;
|
|
|
|
i <= DCN_1_0__SRCID__HUBP0_FLIP_INTERRUPT + adev->mode_info.num_crtc - 1;
|
|
|
|
i++) {
|
|
|
|
r = amdgpu_irq_add_id(adev, AMDGPU_IH_CLIENTID_DCE, 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, AMDGPU_IH_CLIENTID_DCE, DCN_1_0__SRCID__DC_HPD1_INT,
|
|
|
|
&adev->hpd_irq);
|
|
|
|
if (r) {
|
|
|
|
DRM_ERROR("Failed to add hpd irq id!\n");
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
register_hpd_handlers(adev);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-09-12 19:58:20 +00:00
|
|
|
static int amdgpu_dm_mode_config_init(struct amdgpu_device *adev)
|
|
|
|
{
|
|
|
|
int r;
|
|
|
|
|
|
|
|
adev->mode_info.mode_config_initialized = true;
|
|
|
|
|
|
|
|
adev->ddev->mode_config.funcs = (void *)&amdgpu_dm_mode_funcs;
|
2017-04-20 19:57:05 +00:00
|
|
|
adev->ddev->mode_config.helper_private = &amdgpu_dm_mode_config_helperfuncs;
|
2017-09-12 19:58:20 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
/* indicate support of immediate flip */
|
|
|
|
adev->ddev->mode_config.async_page_flip = true;
|
|
|
|
|
|
|
|
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, 0, 0))
|
|
|
|
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 i;
|
2017-04-03 17:36:26 +00:00
|
|
|
struct amdgpu_connector *aconnector = NULL;
|
|
|
|
struct amdgpu_encoder *aencoder = NULL;
|
2017-06-15 20:24:01 +00:00
|
|
|
struct amdgpu_mode_info *mode_info = &adev->mode_info;
|
2017-09-12 19:58:20 +00:00
|
|
|
uint32_t link_cnt;
|
|
|
|
|
|
|
|
link_cnt = dm->dc->caps.max_links;
|
|
|
|
if (amdgpu_dm_mode_config_init(dm->adev)) {
|
|
|
|
DRM_ERROR("DM: Failed to initialize mode config\n");
|
2017-04-03 17:36:26 +00:00
|
|
|
return -1;
|
2017-09-12 19:58:20 +00:00
|
|
|
}
|
|
|
|
|
2017-06-15 20:24:01 +00:00
|
|
|
for (i = 0; i < dm->dc->caps.max_surfaces; i++) {
|
|
|
|
mode_info->planes[i] = kzalloc(sizeof(struct amdgpu_plane),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!mode_info->planes[i]) {
|
|
|
|
DRM_ERROR("KMS: Failed to allocate surface\n");
|
|
|
|
goto fail_free_planes;
|
|
|
|
}
|
|
|
|
mode_info->planes[i]->plane_type = mode_info->plane_type[i];
|
2017-04-10 19:37:32 +00:00
|
|
|
if (amdgpu_dm_plane_init(dm, mode_info->planes[i], 0xff)) {
|
2017-06-15 20:24:01 +00:00
|
|
|
DRM_ERROR("KMS: Failed to initialize plane\n");
|
|
|
|
goto fail_free_planes;
|
|
|
|
}
|
|
|
|
}
|
2017-09-12 19:58:20 +00:00
|
|
|
|
2017-06-15 20:24:01 +00:00
|
|
|
for (i = 0; i < dm->dc->caps.max_streams; i++)
|
|
|
|
if (amdgpu_dm_crtc_init(dm, &mode_info->planes[i]->base, i)) {
|
2017-09-12 19:58:20 +00:00
|
|
|
DRM_ERROR("KMS: Failed to initialize crtc\n");
|
2017-06-15 20:24:01 +00:00
|
|
|
goto fail_free_planes;
|
2017-09-12 19:58:20 +00:00
|
|
|
}
|
|
|
|
|
2016-12-29 20:27:12 +00:00
|
|
|
dm->display_indexes_num = dm->dc->caps.max_streams;
|
2017-09-12 19:58:20 +00:00
|
|
|
|
|
|
|
/* loops over all connectors on the board */
|
|
|
|
for (i = 0; i < link_cnt; i++) {
|
|
|
|
|
|
|
|
if (i > AMDGPU_DM_MAX_DISPLAY_INDEX) {
|
|
|
|
DRM_ERROR(
|
|
|
|
"KMS: Cannot support more than %d display indexes\n",
|
|
|
|
AMDGPU_DM_MAX_DISPLAY_INDEX);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
aconnector = kzalloc(sizeof(*aconnector), GFP_KERNEL);
|
|
|
|
if (!aconnector)
|
2017-04-03 17:36:26 +00:00
|
|
|
goto fail_free_planes;
|
2017-09-12 19:58:20 +00:00
|
|
|
|
|
|
|
aencoder = kzalloc(sizeof(*aencoder), GFP_KERNEL);
|
|
|
|
if (!aencoder) {
|
|
|
|
goto fail_free_connector;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (amdgpu_dm_encoder_init(dm->ddev, aencoder, i)) {
|
|
|
|
DRM_ERROR("KMS: Failed to initialize encoder\n");
|
|
|
|
goto fail_free_encoder;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (amdgpu_dm_connector_init(dm, aconnector, i, aencoder)) {
|
|
|
|
DRM_ERROR("KMS: Failed to initialize connector\n");
|
2017-04-03 17:36:26 +00:00
|
|
|
goto fail_free_encoder;
|
2017-09-12 19:58:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (dc_link_detect(dc_get_link_at_index(dm->dc, i), true))
|
|
|
|
amdgpu_dm_update_connector_after_detect(aconnector);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Software is initialized. Now we can register interrupt handlers. */
|
|
|
|
switch (adev->asic_type) {
|
|
|
|
case CHIP_BONAIRE:
|
|
|
|
case CHIP_HAWAII:
|
|
|
|
case CHIP_TONGA:
|
|
|
|
case CHIP_FIJI:
|
|
|
|
case CHIP_CARRIZO:
|
|
|
|
case CHIP_STONEY:
|
|
|
|
case CHIP_POLARIS11:
|
|
|
|
case CHIP_POLARIS10:
|
2016-12-14 20:35:13 +00:00
|
|
|
case CHIP_POLARIS12:
|
2017-06-15 20:20:24 +00:00
|
|
|
case CHIP_VEGA10:
|
2017-09-12 19:58:20 +00:00
|
|
|
if (dce110_register_irq_handlers(dm->adev)) {
|
|
|
|
DRM_ERROR("DM: Failed to initialize IRQ\n");
|
2017-06-15 20:24:01 +00:00
|
|
|
goto fail_free_encoder;
|
2017-09-12 19:58:20 +00:00
|
|
|
}
|
|
|
|
break;
|
2017-06-15 20:27:42 +00:00
|
|
|
#if defined(CONFIG_DRM_AMD_DC_DCN1_0)
|
|
|
|
case CHIP_RAVEN:
|
|
|
|
if (dcn10_register_irq_handlers(dm->adev)) {
|
|
|
|
DRM_ERROR("DM: Failed to initialize IRQ\n");
|
|
|
|
goto fail_free_encoder;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
#endif
|
2017-09-12 19:58:20 +00:00
|
|
|
default:
|
|
|
|
DRM_ERROR("Usupported ASIC type: 0x%X\n", adev->asic_type);
|
2017-06-15 20:24:01 +00:00
|
|
|
goto fail_free_encoder;
|
2017-09-12 19:58:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
drm_mode_config_reset(dm->ddev);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
fail_free_encoder:
|
|
|
|
kfree(aencoder);
|
|
|
|
fail_free_connector:
|
|
|
|
kfree(aconnector);
|
2017-06-15 20:24:01 +00:00
|
|
|
fail_free_planes:
|
|
|
|
for (i = 0; i < dm->dc->caps.max_surfaces; i++)
|
|
|
|
kfree(mode_info->planes[i]);
|
2017-09-12 19:58:20 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void amdgpu_dm_destroy_drm_device(struct amdgpu_display_manager *dm)
|
|
|
|
{
|
|
|
|
drm_mode_config_cleanup(dm->ddev);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
* amdgpu_display_funcs functions
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2016-12-05 13:51:26 +00:00
|
|
|
/* TODO: implement later */
|
2017-09-12 19:58:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void dm_set_backlight_level(struct amdgpu_encoder *amdgpu_encoder,
|
|
|
|
u8 level)
|
|
|
|
{
|
|
|
|
/* TODO: translate amdgpu_encoder to display_index and call DAL */
|
|
|
|
}
|
|
|
|
|
|
|
|
static u8 dm_get_backlight_level(struct amdgpu_encoder *amdgpu_encoder)
|
|
|
|
{
|
|
|
|
/* TODO: translate amdgpu_encoder to display_index and call DAL */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
* Page Flip functions
|
|
|
|
******************************************************************************/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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, bool async)
|
|
|
|
{
|
|
|
|
struct amdgpu_crtc *acrtc;
|
2016-12-29 20:27:12 +00:00
|
|
|
const struct dc_stream *stream;
|
2017-09-12 19:58:20 +00:00
|
|
|
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
|
2016-12-29 20:27:12 +00:00
|
|
|
* nobody messes with acrtc->stream after we read and check its value.
|
2017-09-12 19:58:20 +00:00
|
|
|
*
|
|
|
|
* 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];
|
2016-12-29 20:27:12 +00:00
|
|
|
stream = acrtc->stream;
|
2017-09-12 19:58:20 +00:00
|
|
|
|
2017-04-20 19:57:05 +00:00
|
|
|
|
|
|
|
if (acrtc->pflip_status != AMDGPU_FLIP_NONE) {
|
|
|
|
DRM_ERROR("flip queue: acrtc %d, already busy\n", acrtc->crtc_id);
|
|
|
|
/* In commit tail framework this cannot happen */
|
|
|
|
BUG_ON(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-12 19:58:20 +00:00
|
|
|
/*
|
|
|
|
* Received a page flip call after the display has been reset.
|
|
|
|
* Just return in this case. Everything should be clean-up on reset.
|
|
|
|
*/
|
|
|
|
|
2016-12-29 20:27:12 +00:00
|
|
|
if (!stream) {
|
2017-09-12 19:58:20 +00:00
|
|
|
WARN_ON(1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
addr.address.grph.addr.low_part = lower_32_bits(crtc_base);
|
|
|
|
addr.address.grph.addr.high_part = upper_32_bits(crtc_base);
|
|
|
|
addr.flip_immediate = async;
|
|
|
|
|
2017-04-20 19:57:05 +00:00
|
|
|
|
|
|
|
if (acrtc->base.state->event &&
|
|
|
|
acrtc->base.state->event->event.base.type ==
|
|
|
|
DRM_EVENT_FLIP_COMPLETE) {
|
|
|
|
acrtc->event = acrtc->base.state->event;
|
|
|
|
|
|
|
|
/* Set the flip status */
|
|
|
|
acrtc->pflip_status = AMDGPU_FLIP_SUBMITTED;
|
|
|
|
|
|
|
|
/* Mark this event as consumed */
|
|
|
|
acrtc->base.state->event = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
dc_flip_surface_addrs(adev->dm.dc,
|
|
|
|
dc_stream_get_status(stream)->surfaces,
|
|
|
|
&addr, 1);
|
|
|
|
|
2017-09-12 19:58:20 +00:00
|
|
|
DRM_DEBUG_DRIVER("%s Flipping to hi: 0x%x, low: 0x%x \n",
|
|
|
|
__func__,
|
|
|
|
addr.address.grph.addr.high_part,
|
|
|
|
addr.address.grph.addr.low_part);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static int amdgpu_notify_freesync(struct drm_device *dev, void *data,
|
|
|
|
struct drm_file *filp)
|
|
|
|
{
|
|
|
|
struct mod_freesync_params freesync_params;
|
2016-12-29 20:27:12 +00:00
|
|
|
uint8_t num_streams;
|
2017-09-12 19:58:20 +00:00
|
|
|
uint8_t i;
|
|
|
|
|
|
|
|
struct amdgpu_device *adev = dev->dev_private;
|
|
|
|
int r = 0;
|
|
|
|
|
|
|
|
/* Get freesync enable flag from DRM */
|
|
|
|
|
2016-12-29 20:27:12 +00:00
|
|
|
num_streams = dc_get_current_stream_count(adev->dm.dc);
|
2017-09-12 19:58:20 +00:00
|
|
|
|
2016-12-29 20:27:12 +00:00
|
|
|
for (i = 0; i < num_streams; i++) {
|
|
|
|
const struct dc_stream *stream;
|
|
|
|
stream = dc_get_stream_at_index(adev->dm.dc, i);
|
2017-09-12 19:58:20 +00:00
|
|
|
|
|
|
|
mod_freesync_update_state(adev->dm.freesync_module,
|
2016-12-29 20:27:12 +00:00
|
|
|
&stream, 1, &freesync_params);
|
2017-09-12 19:58:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2016-12-13 20:42:48 +00:00
|
|
|
static const struct amdgpu_display_funcs dm_display_funcs = {
|
2017-09-12 19:58:20 +00:00
|
|
|
.bandwidth_update = dm_bandwidth_update, /* called unconditionally */
|
|
|
|
.vblank_get_counter = dm_vblank_get_counter,/* called unconditionally */
|
|
|
|
.vblank_wait = NULL,
|
|
|
|
.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 = NULL, /* VBIOS parsing. DAL does it. */
|
|
|
|
.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. */
|
|
|
|
.notify_freesync = amdgpu_notify_freesync,
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2017-06-15 20:20:24 +00:00
|
|
|
|
2017-09-12 19:58:20 +00:00
|
|
|
#if defined(CONFIG_DEBUG_KERNEL_DC)
|
|
|
|
|
|
|
|
static ssize_t s3_debug_store(
|
|
|
|
struct device *device,
|
|
|
|
struct device_attribute *attr,
|
|
|
|
const char *buf,
|
|
|
|
size_t count)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
int s3_state;
|
|
|
|
struct pci_dev *pdev = to_pci_dev(device);
|
|
|
|
struct drm_device *drm_dev = pci_get_drvdata(pdev);
|
|
|
|
struct amdgpu_device *adev = drm_dev->dev_private;
|
|
|
|
|
|
|
|
ret = kstrtoint(buf, 0, &s3_state);
|
|
|
|
|
|
|
|
if (ret == 0) {
|
|
|
|
if (s3_state) {
|
|
|
|
dm_resume(adev);
|
|
|
|
amdgpu_dm_display_resume(adev);
|
|
|
|
drm_kms_helper_hotplug_event(adev->ddev);
|
|
|
|
} else
|
|
|
|
dm_suspend(adev);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret == 0 ? count : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
DEVICE_ATTR_WO(s3_debug);
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static int dm_early_init(void *handle)
|
|
|
|
{
|
|
|
|
struct amdgpu_device *adev = (struct amdgpu_device *)handle;
|
|
|
|
|
|
|
|
amdgpu_dm_set_irq_funcs(adev);
|
|
|
|
|
|
|
|
switch (adev->asic_type) {
|
|
|
|
case CHIP_BONAIRE:
|
|
|
|
case CHIP_HAWAII:
|
|
|
|
adev->mode_info.num_crtc = 6;
|
|
|
|
adev->mode_info.num_hpd = 6;
|
|
|
|
adev->mode_info.num_dig = 6;
|
2017-06-15 20:24:01 +00:00
|
|
|
adev->mode_info.plane_type = dm_surfaces_type_default;
|
2017-09-12 19:58:20 +00:00
|
|
|
break;
|
|
|
|
case CHIP_FIJI:
|
|
|
|
case CHIP_TONGA:
|
|
|
|
adev->mode_info.num_crtc = 6;
|
|
|
|
adev->mode_info.num_hpd = 6;
|
|
|
|
adev->mode_info.num_dig = 7;
|
2017-06-15 20:24:01 +00:00
|
|
|
adev->mode_info.plane_type = dm_surfaces_type_default;
|
2017-09-12 19:58:20 +00:00
|
|
|
break;
|
|
|
|
case CHIP_CARRIZO:
|
|
|
|
adev->mode_info.num_crtc = 3;
|
|
|
|
adev->mode_info.num_hpd = 6;
|
|
|
|
adev->mode_info.num_dig = 9;
|
2017-06-15 20:24:01 +00:00
|
|
|
adev->mode_info.plane_type = dm_surfaces_type_carizzo;
|
2017-09-12 19:58:20 +00:00
|
|
|
break;
|
|
|
|
case CHIP_STONEY:
|
|
|
|
adev->mode_info.num_crtc = 2;
|
|
|
|
adev->mode_info.num_hpd = 6;
|
|
|
|
adev->mode_info.num_dig = 9;
|
2017-06-15 20:24:01 +00:00
|
|
|
adev->mode_info.plane_type = dm_surfaces_type_stoney;
|
2017-09-12 19:58:20 +00:00
|
|
|
break;
|
|
|
|
case CHIP_POLARIS11:
|
2016-12-14 20:35:13 +00:00
|
|
|
case CHIP_POLARIS12:
|
2017-09-12 19:58:20 +00:00
|
|
|
adev->mode_info.num_crtc = 5;
|
|
|
|
adev->mode_info.num_hpd = 5;
|
|
|
|
adev->mode_info.num_dig = 5;
|
2017-06-15 20:24:01 +00:00
|
|
|
adev->mode_info.plane_type = dm_surfaces_type_default;
|
2017-09-12 19:58:20 +00:00
|
|
|
break;
|
|
|
|
case CHIP_POLARIS10:
|
|
|
|
adev->mode_info.num_crtc = 6;
|
|
|
|
adev->mode_info.num_hpd = 6;
|
|
|
|
adev->mode_info.num_dig = 6;
|
2017-06-15 20:24:01 +00:00
|
|
|
adev->mode_info.plane_type = dm_surfaces_type_default;
|
2017-09-12 19:58:20 +00:00
|
|
|
break;
|
2017-06-15 20:20:24 +00:00
|
|
|
case CHIP_VEGA10:
|
|
|
|
adev->mode_info.num_crtc = 6;
|
|
|
|
adev->mode_info.num_hpd = 6;
|
|
|
|
adev->mode_info.num_dig = 6;
|
2017-06-15 20:25:11 +00:00
|
|
|
adev->mode_info.plane_type = dm_surfaces_type_default;
|
2017-06-15 20:20:24 +00:00
|
|
|
break;
|
2017-06-15 20:27:42 +00:00
|
|
|
#if defined(CONFIG_DRM_AMD_DC_DCN1_0)
|
|
|
|
case CHIP_RAVEN:
|
|
|
|
adev->mode_info.num_crtc = 4;
|
|
|
|
adev->mode_info.num_hpd = 4;
|
|
|
|
adev->mode_info.num_dig = 4;
|
|
|
|
adev->mode_info.plane_type = dm_surfaces_type_default;
|
|
|
|
break;
|
|
|
|
#endif
|
2017-09-12 19:58:20 +00:00
|
|
|
default:
|
|
|
|
DRM_ERROR("Usupported ASIC type: 0x%X\n", adev->asic_type);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2016-12-13 20:42:48 +00:00
|
|
|
if (adev->mode_info.funcs == NULL)
|
|
|
|
adev->mode_info.funcs = &dm_display_funcs;
|
|
|
|
|
2017-09-12 19:58:20 +00:00
|
|
|
/* Note: Do NOT change adev->audio_endpt_rreg and
|
|
|
|
* adev->audio_endpt_wreg because they are initialised in
|
|
|
|
* amdgpu_device_init() */
|
|
|
|
#if defined(CONFIG_DEBUG_KERNEL_DC)
|
|
|
|
device_create_file(
|
|
|
|
adev->ddev->dev,
|
|
|
|
&dev_attr_s3_debug);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|