1698 lines · 51.4 KB · C
RawDownload
/* * Copyright 2007-8 Advanced Micro Devices, Inc. * Copyright 2008 Red Hat 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: Dave Airlie *          Alex Deucher */ #include <drm/amdgpu_drm.h>#include "amdgpu.h"#include "amdgpu_i2c.h"#include "atom.h"#include "amdgpu_connectors.h"#include "amdgpu_display.h"#include "soc15_common.h"#include "gc/gc_11_0_0_offset.h"#include "gc/gc_11_0_0_sh_mask.h"#include <asm/div64.h> #include <linux/pci.h>#include <linux/pm_runtime.h>#include <drm/drm_crtc_helper.h>#include <drm/drm_edid.h>#include <drm/drm_fb_helper.h>#include <drm/drm_gem_framebuffer_helper.h>#include <drm/drm_fourcc.h>#include <drm/drm_vblank.h> /** * amdgpu_display_hotplug_work_func - work handler for display hotplug event * * @work: work struct pointer * * This is the hotplug event work handler (all ASICs). * The work gets scheduled from the IRQ handler if there * was a hotplug interrupt.  It walks through the connector table * and calls hotplug handler for each connector. After this, it sends * a DRM hotplug event to alert userspace. * * This design approach is required in order to defer hotplug event handling * from the IRQ handler to a work handler because hotplug handler has to use * mutexes which cannot be locked in an IRQ handler (since &mutex_lock may * sleep). */void amdgpu_display_hotplug_work_func(struct work_struct *work){	struct amdgpu_device *adev = container_of(work, struct amdgpu_device,						  hotplug_work);	struct drm_device *dev = adev_to_drm(adev);	struct drm_mode_config *mode_config = &dev->mode_config;	struct drm_connector *connector;	struct drm_connector_list_iter iter; 	mutex_lock(&mode_config->mutex);	drm_connector_list_iter_begin(dev, &iter);	drm_for_each_connector_iter(connector, &iter)		amdgpu_connector_hotplug(connector);	drm_connector_list_iter_end(&iter);	mutex_unlock(&mode_config->mutex);	/* Just fire off a uevent and let userspace tell us what to do */	drm_helper_hpd_irq_event(dev);} static int amdgpu_display_framebuffer_init(struct drm_device *dev,					   struct amdgpu_framebuffer *rfb,					   const struct drm_mode_fb_cmd2 *mode_cmd,					   struct drm_gem_object *obj); static void amdgpu_display_flip_callback(struct dma_fence *f,					 struct dma_fence_cb *cb){	struct amdgpu_flip_work *work =		container_of(cb, struct amdgpu_flip_work, cb); 	dma_fence_put(f);	schedule_work(&work->flip_work.work);} static bool amdgpu_display_flip_handle_fence(struct amdgpu_flip_work *work,					     struct dma_fence **f){	struct dma_fence *fence= *f; 	if (fence == NULL)		return false; 	*f = NULL; 	if (!dma_fence_add_callback(fence, &work->cb,				    amdgpu_display_flip_callback))		return true; 	dma_fence_put(fence);	return false;} static void amdgpu_display_flip_work_func(struct work_struct *__work){	struct delayed_work *delayed_work =		container_of(__work, struct delayed_work, work);	struct amdgpu_flip_work *work =		container_of(delayed_work, struct amdgpu_flip_work, flip_work);	struct amdgpu_device *adev = work->adev;	struct amdgpu_crtc *amdgpu_crtc = adev->mode_info.crtcs[work->crtc_id]; 	struct drm_crtc *crtc = &amdgpu_crtc->base;	unsigned long flags;	unsigned i;	int vpos, hpos; 	for (i = 0; i < work->shared_count; ++i)		if (amdgpu_display_flip_handle_fence(work, &work->shared[i]))			return; 	/* Wait until we're out of the vertical blank period before the one	 * targeted by the flip	 */	if (amdgpu_crtc->enabled &&	    (amdgpu_display_get_crtc_scanoutpos(adev_to_drm(adev), work->crtc_id, 0,						&vpos, &hpos, NULL, NULL,						&crtc->hwmode)	     & (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK)) ==	    (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK) &&	    (int)(work->target_vblank -		  amdgpu_get_vblank_counter_kms(crtc)) > 0) {		schedule_delayed_work(&work->flip_work, usecs_to_jiffies(1000));		return;	} 	/* We borrow the event spin lock for protecting flip_status */	spin_lock_irqsave(&crtc->dev->event_lock, flags); 	/* Do the flip (mmio) */	adev->mode_info.funcs->page_flip(adev, work->crtc_id, work->base, work->async); 	/* Set the flip status */	amdgpu_crtc->pflip_status = AMDGPU_FLIP_SUBMITTED;	spin_unlock_irqrestore(&crtc->dev->event_lock, flags);  	drm_dbg_vbl(adev_to_drm(adev),		    "crtc:%d[%p], pflip_stat:AMDGPU_FLIP_SUBMITTED, work: %p,\n",		    amdgpu_crtc->crtc_id, amdgpu_crtc, work); } /* * Handle unpin events outside the interrupt handler proper. */static void amdgpu_display_unpin_work_func(struct work_struct *__work){	struct amdgpu_flip_work *work =		container_of(__work, struct amdgpu_flip_work, unpin_work);	int r; 	/* unpin of the old buffer */	r = amdgpu_bo_reserve(work->old_abo, true);	if (likely(r == 0)) {		amdgpu_bo_unpin(work->old_abo);		amdgpu_bo_unreserve(work->old_abo);	} else		DRM_ERROR("failed to reserve buffer after flip\n"); 	amdgpu_bo_unref(&work->old_abo);	kfree(work->shared);	kfree(work);} int amdgpu_display_crtc_page_flip_target(struct drm_crtc *crtc,				struct drm_framebuffer *fb,				struct drm_pending_vblank_event *event,				uint32_t page_flip_flags, uint32_t target,				struct drm_modeset_acquire_ctx *ctx){	struct drm_device *dev = crtc->dev;	struct amdgpu_device *adev = drm_to_adev(dev);	struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);	struct drm_gem_object *obj;	struct amdgpu_flip_work *work;	struct amdgpu_bo *new_abo;	unsigned long flags;	u64 tiling_flags;	int i, r; 	work = kzalloc(sizeof *work, GFP_KERNEL);	if (work == NULL)		return -ENOMEM; 	INIT_DELAYED_WORK(&work->flip_work, amdgpu_display_flip_work_func);	INIT_WORK(&work->unpin_work, amdgpu_display_unpin_work_func); 	work->event = event;	work->adev = adev;	work->crtc_id = amdgpu_crtc->crtc_id;	work->async = (page_flip_flags & DRM_MODE_PAGE_FLIP_ASYNC) != 0; 	/* schedule unpin of the old buffer */	obj = crtc->primary->fb->obj[0]; 	/* take a reference to the old object */	work->old_abo = gem_to_amdgpu_bo(obj);	amdgpu_bo_ref(work->old_abo); 	obj = fb->obj[0];	new_abo = gem_to_amdgpu_bo(obj); 	/* pin the new buffer */	r = amdgpu_bo_reserve(new_abo, false);	if (unlikely(r != 0)) {		DRM_ERROR("failed to reserve new abo buffer before flip\n");		goto cleanup;	} 	if (!adev->enable_virtual_display) {		r = amdgpu_bo_pin(new_abo,				  amdgpu_display_supported_domains(adev, new_abo->flags));		if (unlikely(r != 0)) {			DRM_ERROR("failed to pin new abo buffer before flip\n");			goto unreserve;		}	} 	r = amdgpu_ttm_alloc_gart(&new_abo->tbo);	if (unlikely(r != 0)) {		DRM_ERROR("%p bind failed\n", new_abo);		goto unpin;	} 	r = dma_resv_get_fences(new_abo->tbo.base.resv, DMA_RESV_USAGE_WRITE,				&work->shared_count,				&work->shared);	if (unlikely(r != 0)) {		DRM_ERROR("failed to get fences for buffer\n");		goto unpin;	} 	amdgpu_bo_get_tiling_flags(new_abo, &tiling_flags);	amdgpu_bo_unreserve(new_abo); 	if (!adev->enable_virtual_display)		work->base = amdgpu_bo_gpu_offset(new_abo);	work->target_vblank = target - (uint32_t)drm_crtc_vblank_count(crtc) +		amdgpu_get_vblank_counter_kms(crtc); 	/* we borrow the event spin lock for protecting flip_wrok */	spin_lock_irqsave(&crtc->dev->event_lock, flags);	if (amdgpu_crtc->pflip_status != AMDGPU_FLIP_NONE) {		DRM_DEBUG_DRIVER("flip queue: crtc already busy\n");		spin_unlock_irqrestore(&crtc->dev->event_lock, flags);		r = -EBUSY;		goto pflip_cleanup;	} 	amdgpu_crtc->pflip_status = AMDGPU_FLIP_PENDING;	amdgpu_crtc->pflip_works = work;  	DRM_DEBUG_DRIVER("crtc:%d[%p], pflip_stat:AMDGPU_FLIP_PENDING, work: %p,\n",					 amdgpu_crtc->crtc_id, amdgpu_crtc, work);	/* update crtc fb */	crtc->primary->fb = fb;	spin_unlock_irqrestore(&crtc->dev->event_lock, flags);	amdgpu_display_flip_work_func(&work->flip_work.work);	return 0; pflip_cleanup:	if (unlikely(amdgpu_bo_reserve(new_abo, false) != 0)) {		DRM_ERROR("failed to reserve new abo in error path\n");		goto cleanup;	}unpin:	if (!adev->enable_virtual_display)		amdgpu_bo_unpin(new_abo); unreserve:	amdgpu_bo_unreserve(new_abo); cleanup:	amdgpu_bo_unref(&work->old_abo);	for (i = 0; i < work->shared_count; ++i)		dma_fence_put(work->shared[i]);	kfree(work->shared);	kfree(work); 	return r;} int amdgpu_display_crtc_set_config(struct drm_mode_set *set,				   struct drm_modeset_acquire_ctx *ctx){	struct drm_device *dev;	struct amdgpu_device *adev;	struct drm_crtc *crtc;	bool active = false;	int ret; 	if (!set || !set->crtc)		return -EINVAL; 	dev = set->crtc->dev; 	ret = pm_runtime_get_sync(dev->dev);	if (ret < 0)		goto out; 	ret = drm_crtc_helper_set_config(set, ctx); 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)		if (crtc->enabled)			active = true; 	pm_runtime_mark_last_busy(dev->dev); 	adev = drm_to_adev(dev);	/* if we have active crtcs and we don't have a power ref,	   take the current one */	if (active && !adev->have_disp_power_ref) {		adev->have_disp_power_ref = true;		return ret;	}	/* if we have no active crtcs, then drop the power ref	   we got before */	if (!active && adev->have_disp_power_ref) {		pm_runtime_put_autosuspend(dev->dev);		adev->have_disp_power_ref = false;	} out:	/* drop the power reference we got coming in here */	pm_runtime_put_autosuspend(dev->dev);	return ret;} static const char *encoder_names[41] = {	"NONE",	"INTERNAL_LVDS",	"INTERNAL_TMDS1",	"INTERNAL_TMDS2",	"INTERNAL_DAC1",	"INTERNAL_DAC2",	"INTERNAL_SDVOA",	"INTERNAL_SDVOB",	"SI170B",	"CH7303",	"CH7301",	"INTERNAL_DVO1",	"EXTERNAL_SDVOA",	"EXTERNAL_SDVOB",	"TITFP513",	"INTERNAL_LVTM1",	"VT1623",	"HDMI_SI1930",	"HDMI_INTERNAL",	"INTERNAL_KLDSCP_TMDS1",	"INTERNAL_KLDSCP_DVO1",	"INTERNAL_KLDSCP_DAC1",	"INTERNAL_KLDSCP_DAC2",	"SI178",	"MVPU_FPGA",	"INTERNAL_DDI",	"VT1625",	"HDMI_SI1932",	"DP_AN9801",	"DP_DP501",	"INTERNAL_UNIPHY",	"INTERNAL_KLDSCP_LVTMA",	"INTERNAL_UNIPHY1",	"INTERNAL_UNIPHY2",	"NUTMEG",	"TRAVIS",	"INTERNAL_VCE",	"INTERNAL_UNIPHY3",	"HDMI_ANX9805",	"INTERNAL_AMCLK",	"VIRTUAL",}; static const char *hpd_names[6] = {	"HPD1",	"HPD2",	"HPD3",	"HPD4",	"HPD5",	"HPD6",}; void amdgpu_display_print_display_setup(struct drm_device *dev){	struct drm_connector *connector;	struct amdgpu_connector *amdgpu_connector;	struct drm_encoder *encoder;	struct amdgpu_encoder *amdgpu_encoder;	struct drm_connector_list_iter iter;	uint32_t devices;	int i = 0; 	drm_connector_list_iter_begin(dev, &iter);	DRM_INFO("AMDGPU Display Connectors\n");	drm_for_each_connector_iter(connector, &iter) {		amdgpu_connector = to_amdgpu_connector(connector);		DRM_INFO("Connector %d:\n", i);		DRM_INFO("  %s\n", connector->name);		if (amdgpu_connector->hpd.hpd != AMDGPU_HPD_NONE)			DRM_INFO("  %s\n", hpd_names[amdgpu_connector->hpd.hpd]);		if (amdgpu_connector->ddc_bus) {			DRM_INFO("  DDC: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",				 amdgpu_connector->ddc_bus->rec.mask_clk_reg,				 amdgpu_connector->ddc_bus->rec.mask_data_reg,				 amdgpu_connector->ddc_bus->rec.a_clk_reg,				 amdgpu_connector->ddc_bus->rec.a_data_reg,				 amdgpu_connector->ddc_bus->rec.en_clk_reg,				 amdgpu_connector->ddc_bus->rec.en_data_reg,				 amdgpu_connector->ddc_bus->rec.y_clk_reg,				 amdgpu_connector->ddc_bus->rec.y_data_reg);			if (amdgpu_connector->router.ddc_valid)				DRM_INFO("  DDC Router 0x%x/0x%x\n",					 amdgpu_connector->router.ddc_mux_control_pin,					 amdgpu_connector->router.ddc_mux_state);			if (amdgpu_connector->router.cd_valid)				DRM_INFO("  Clock/Data Router 0x%x/0x%x\n",					 amdgpu_connector->router.cd_mux_control_pin,					 amdgpu_connector->router.cd_mux_state);		} else {			if (connector->connector_type == DRM_MODE_CONNECTOR_VGA ||			    connector->connector_type == DRM_MODE_CONNECTOR_DVII ||			    connector->connector_type == DRM_MODE_CONNECTOR_DVID ||			    connector->connector_type == DRM_MODE_CONNECTOR_DVIA ||			    connector->connector_type == DRM_MODE_CONNECTOR_HDMIA ||			    connector->connector_type == DRM_MODE_CONNECTOR_HDMIB)				DRM_INFO("  DDC: no ddc bus - possible BIOS bug - please report to xorg-driver-ati@lists.x.org\n");		}		DRM_INFO("  Encoders:\n");		list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {			amdgpu_encoder = to_amdgpu_encoder(encoder);			devices = amdgpu_encoder->devices & amdgpu_connector->devices;			if (devices) {				if (devices & ATOM_DEVICE_CRT1_SUPPORT)					DRM_INFO("    CRT1: %s\n", encoder_names[amdgpu_encoder->encoder_id]);				if (devices & ATOM_DEVICE_CRT2_SUPPORT)					DRM_INFO("    CRT2: %s\n", encoder_names[amdgpu_encoder->encoder_id]);				if (devices & ATOM_DEVICE_LCD1_SUPPORT)					DRM_INFO("    LCD1: %s\n", encoder_names[amdgpu_encoder->encoder_id]);				if (devices & ATOM_DEVICE_DFP1_SUPPORT)					DRM_INFO("    DFP1: %s\n", encoder_names[amdgpu_encoder->encoder_id]);				if (devices & ATOM_DEVICE_DFP2_SUPPORT)					DRM_INFO("    DFP2: %s\n", encoder_names[amdgpu_encoder->encoder_id]);				if (devices & ATOM_DEVICE_DFP3_SUPPORT)					DRM_INFO("    DFP3: %s\n", encoder_names[amdgpu_encoder->encoder_id]);				if (devices & ATOM_DEVICE_DFP4_SUPPORT)					DRM_INFO("    DFP4: %s\n", encoder_names[amdgpu_encoder->encoder_id]);				if (devices & ATOM_DEVICE_DFP5_SUPPORT)					DRM_INFO("    DFP5: %s\n", encoder_names[amdgpu_encoder->encoder_id]);				if (devices & ATOM_DEVICE_DFP6_SUPPORT)					DRM_INFO("    DFP6: %s\n", encoder_names[amdgpu_encoder->encoder_id]);				if (devices & ATOM_DEVICE_TV1_SUPPORT)					DRM_INFO("    TV1: %s\n", encoder_names[amdgpu_encoder->encoder_id]);				if (devices & ATOM_DEVICE_CV_SUPPORT)					DRM_INFO("    CV: %s\n", encoder_names[amdgpu_encoder->encoder_id]);			}		}		i++;	}	drm_connector_list_iter_end(&iter);} bool amdgpu_display_ddc_probe(struct amdgpu_connector *amdgpu_connector,			      bool use_aux){	u8 out = 0x0;	u8 buf[8];	int ret;	struct i2c_msg msgs[] = {		{			.addr = DDC_ADDR,			.flags = 0,			.len = 1,			.buf = &out,		},		{			.addr = DDC_ADDR,			.flags = I2C_M_RD,			.len = 8,

Lines 1–500 of 1698.