forked from Minki/linux
Merge branch 'drm-fixes' of git://people.freedesktop.org/~airlied/linux
Pull drm fixes from Dave Airlie:
"Unfortunately this contains no easter eggs, its a bit larger than I'd
like, but I included a patch that just moves code from one file to
another and I'd like to avoid merge conflicts with that later, so it
makes it seem worse than it is,
Otherwise:
- radeon: fixes to use new microcode to stabilise some cards, use
some common displayport code, some runtime pm fixes, pll regression
fixes
- i915: fix for some context oopses, a warn in a used path, backlight
fixes
- nouveau: regression fix
- omap: a bunch of fixes"
* 'drm-fixes' of git://people.freedesktop.org/~airlied/linux: (51 commits)
drm: bochs: drop unused struct fields
drm: bochs: add power management support
drm: cirrus: add power management support
drm: Split out drm_probe_helper.c from drm_crtc_helper.c
drm/plane-helper: Don't fake-implement primary plane disabling
drm/ast: fix value check in cbr_scan2
drm/nouveau/bios: fix a bit shift error introduced by 457e77b
drm/radeon/ci: make sure mc ucode is loaded before checking the size
drm/radeon/si: make sure mc ucode is loaded before checking the size
drm/radeon: improve PLL params if we don't match exactly v2
drm/radeon: memory leak on bo reservation failure. v2
drm/radeon: fix VCE fence command
drm/radeon: re-enable mclk dpm on R7 260X asics
drm/radeon: add support for newer mc ucode on CI (v2)
drm/radeon: add support for newer mc ucode on SI (v2)
drm/radeon: apply more strict limits for PLL params v2
drm/radeon: update CI DPM powertune settings
drm/radeon: fix runpm handling on APUs (v4)
drm/radeon: disable mclk dpm on R7 260X
drm/tegra: Remove gratuitous pad field
...
This commit is contained in:
commit
b93124202f
@ -2285,6 +2285,11 @@ void intel_crt_init(struct drm_device *dev)
|
||||
<sect2>
|
||||
<title>Modeset Helper Functions Reference</title>
|
||||
!Edrivers/gpu/drm/drm_crtc_helper.c
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Output Probing Helper Functions Reference</title>
|
||||
!Pdrivers/gpu/drm/drm_probe_helper.c output probing helper overview
|
||||
!Edrivers/gpu/drm/drm_probe_helper.c
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>fbdev Helper Functions Reference</title>
|
||||
|
@ -23,7 +23,7 @@ drm-$(CONFIG_DRM_PANEL) += drm_panel.o
|
||||
|
||||
drm-usb-y := drm_usb.o
|
||||
|
||||
drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o
|
||||
drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o
|
||||
drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o
|
||||
drm_kms_helper-$(CONFIG_DRM_KMS_FB_HELPER) += drm_fb_helper.o
|
||||
drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o
|
||||
|
@ -572,7 +572,7 @@ static u32 cbr_scan2(struct ast_private *ast)
|
||||
for (loop = 0; loop < CBR_PASSNUM2; loop++) {
|
||||
if ((data = cbr_test2(ast)) != 0) {
|
||||
data2 &= data;
|
||||
if (!data)
|
||||
if (!data2)
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/console.h>
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
@ -87,8 +88,6 @@ struct bochs_device {
|
||||
struct bochs_framebuffer gfb;
|
||||
struct drm_fb_helper helper;
|
||||
int size;
|
||||
int x1, y1, x2, y2; /* dirty rect */
|
||||
spinlock_t dirty_lock;
|
||||
bool initialized;
|
||||
} fb;
|
||||
};
|
||||
|
@ -94,6 +94,49 @@ static struct drm_driver bochs_driver = {
|
||||
.dumb_destroy = drm_gem_dumb_destroy,
|
||||
};
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* pm interface */
|
||||
|
||||
static int bochs_pm_suspend(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct drm_device *drm_dev = pci_get_drvdata(pdev);
|
||||
struct bochs_device *bochs = drm_dev->dev_private;
|
||||
|
||||
drm_kms_helper_poll_disable(drm_dev);
|
||||
|
||||
if (bochs->fb.initialized) {
|
||||
console_lock();
|
||||
fb_set_suspend(bochs->fb.helper.fbdev, 1);
|
||||
console_unlock();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bochs_pm_resume(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct drm_device *drm_dev = pci_get_drvdata(pdev);
|
||||
struct bochs_device *bochs = drm_dev->dev_private;
|
||||
|
||||
drm_helper_resume_force_mode(drm_dev);
|
||||
|
||||
if (bochs->fb.initialized) {
|
||||
console_lock();
|
||||
fb_set_suspend(bochs->fb.helper.fbdev, 0);
|
||||
console_unlock();
|
||||
}
|
||||
|
||||
drm_kms_helper_poll_enable(drm_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops bochs_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(bochs_pm_suspend,
|
||||
bochs_pm_resume)
|
||||
};
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* pci interface */
|
||||
|
||||
@ -155,6 +198,7 @@ static struct pci_driver bochs_pci_driver = {
|
||||
.id_table = bochs_pci_tbl,
|
||||
.probe = bochs_pci_probe,
|
||||
.remove = bochs_pci_remove,
|
||||
.driver.pm = &bochs_pm_ops,
|
||||
};
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
@ -190,7 +190,6 @@ int bochs_fbdev_init(struct bochs_device *bochs)
|
||||
int ret;
|
||||
|
||||
bochs->fb.helper.funcs = &bochs_fb_helper_funcs;
|
||||
spin_lock_init(&bochs->fb.dirty_lock);
|
||||
|
||||
ret = drm_fb_helper_init(bochs->dev, &bochs->fb.helper,
|
||||
1, 1);
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/console.h>
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
|
||||
#include "cirrus_drv.h"
|
||||
|
||||
@ -75,6 +76,41 @@ static void cirrus_pci_remove(struct pci_dev *pdev)
|
||||
drm_put_dev(dev);
|
||||
}
|
||||
|
||||
static int cirrus_pm_suspend(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct drm_device *drm_dev = pci_get_drvdata(pdev);
|
||||
struct cirrus_device *cdev = drm_dev->dev_private;
|
||||
|
||||
drm_kms_helper_poll_disable(drm_dev);
|
||||
|
||||
if (cdev->mode_info.gfbdev) {
|
||||
console_lock();
|
||||
fb_set_suspend(cdev->mode_info.gfbdev->helper.fbdev, 1);
|
||||
console_unlock();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cirrus_pm_resume(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct drm_device *drm_dev = pci_get_drvdata(pdev);
|
||||
struct cirrus_device *cdev = drm_dev->dev_private;
|
||||
|
||||
drm_helper_resume_force_mode(drm_dev);
|
||||
|
||||
if (cdev->mode_info.gfbdev) {
|
||||
console_lock();
|
||||
fb_set_suspend(cdev->mode_info.gfbdev->helper.fbdev, 0);
|
||||
console_unlock();
|
||||
}
|
||||
|
||||
drm_kms_helper_poll_enable(drm_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations cirrus_driver_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = drm_open,
|
||||
@ -103,11 +139,17 @@ static struct drm_driver driver = {
|
||||
.dumb_destroy = drm_gem_dumb_destroy,
|
||||
};
|
||||
|
||||
static const struct dev_pm_ops cirrus_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(cirrus_pm_suspend,
|
||||
cirrus_pm_resume)
|
||||
};
|
||||
|
||||
static struct pci_driver cirrus_pci_driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.id_table = pciidlist,
|
||||
.probe = cirrus_pci_probe,
|
||||
.remove = cirrus_pci_remove,
|
||||
.driver.pm = &cirrus_pm_ops,
|
||||
};
|
||||
|
||||
static int __init cirrus_init(void)
|
||||
|
@ -308,6 +308,9 @@ static int cirrus_crtc_mode_set(struct drm_crtc *crtc,
|
||||
|
||||
WREG_HDR(hdr);
|
||||
cirrus_crtc_do_set_base(crtc, old_fb, x, y, 0);
|
||||
|
||||
/* Unblank (needed on S3 resume, vgabios doesn't do it then) */
|
||||
outb(0x20, 0x3c0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -72,147 +72,6 @@ void drm_helper_move_panel_connectors_to_head(struct drm_device *dev)
|
||||
}
|
||||
EXPORT_SYMBOL(drm_helper_move_panel_connectors_to_head);
|
||||
|
||||
static bool drm_kms_helper_poll = true;
|
||||
module_param_named(poll, drm_kms_helper_poll, bool, 0600);
|
||||
|
||||
static void drm_mode_validate_flag(struct drm_connector *connector,
|
||||
int flags)
|
||||
{
|
||||
struct drm_display_mode *mode;
|
||||
|
||||
if (flags == (DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_INTERLACE |
|
||||
DRM_MODE_FLAG_3D_MASK))
|
||||
return;
|
||||
|
||||
list_for_each_entry(mode, &connector->modes, head) {
|
||||
if ((mode->flags & DRM_MODE_FLAG_INTERLACE) &&
|
||||
!(flags & DRM_MODE_FLAG_INTERLACE))
|
||||
mode->status = MODE_NO_INTERLACE;
|
||||
if ((mode->flags & DRM_MODE_FLAG_DBLSCAN) &&
|
||||
!(flags & DRM_MODE_FLAG_DBLSCAN))
|
||||
mode->status = MODE_NO_DBLESCAN;
|
||||
if ((mode->flags & DRM_MODE_FLAG_3D_MASK) &&
|
||||
!(flags & DRM_MODE_FLAG_3D_MASK))
|
||||
mode->status = MODE_NO_STEREO;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_helper_probe_single_connector_modes - get complete set of display modes
|
||||
* @connector: connector to probe
|
||||
* @maxX: max width for modes
|
||||
* @maxY: max height for modes
|
||||
*
|
||||
* Based on the helper callbacks implemented by @connector try to detect all
|
||||
* valid modes. Modes will first be added to the connector's probed_modes list,
|
||||
* then culled (based on validity and the @maxX, @maxY parameters) and put into
|
||||
* the normal modes list.
|
||||
*
|
||||
* Intended to be use as a generic implementation of the ->fill_modes()
|
||||
* @connector vfunc for drivers that use the crtc helpers for output mode
|
||||
* filtering and detection.
|
||||
*
|
||||
* Returns:
|
||||
* The number of modes found on @connector.
|
||||
*/
|
||||
int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
|
||||
uint32_t maxX, uint32_t maxY)
|
||||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct drm_display_mode *mode;
|
||||
struct drm_connector_helper_funcs *connector_funcs =
|
||||
connector->helper_private;
|
||||
int count = 0;
|
||||
int mode_flags = 0;
|
||||
bool verbose_prune = true;
|
||||
|
||||
WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
|
||||
|
||||
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id,
|
||||
drm_get_connector_name(connector));
|
||||
/* set all modes to the unverified state */
|
||||
list_for_each_entry(mode, &connector->modes, head)
|
||||
mode->status = MODE_UNVERIFIED;
|
||||
|
||||
if (connector->force) {
|
||||
if (connector->force == DRM_FORCE_ON)
|
||||
connector->status = connector_status_connected;
|
||||
else
|
||||
connector->status = connector_status_disconnected;
|
||||
if (connector->funcs->force)
|
||||
connector->funcs->force(connector);
|
||||
} else {
|
||||
connector->status = connector->funcs->detect(connector, true);
|
||||
}
|
||||
|
||||
/* Re-enable polling in case the global poll config changed. */
|
||||
if (drm_kms_helper_poll != dev->mode_config.poll_running)
|
||||
drm_kms_helper_poll_enable(dev);
|
||||
|
||||
dev->mode_config.poll_running = drm_kms_helper_poll;
|
||||
|
||||
if (connector->status == connector_status_disconnected) {
|
||||
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] disconnected\n",
|
||||
connector->base.id, drm_get_connector_name(connector));
|
||||
drm_mode_connector_update_edid_property(connector, NULL);
|
||||
verbose_prune = false;
|
||||
goto prune;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DRM_LOAD_EDID_FIRMWARE
|
||||
count = drm_load_edid_firmware(connector);
|
||||
if (count == 0)
|
||||
#endif
|
||||
count = (*connector_funcs->get_modes)(connector);
|
||||
|
||||
if (count == 0 && connector->status == connector_status_connected)
|
||||
count = drm_add_modes_noedid(connector, 1024, 768);
|
||||
if (count == 0)
|
||||
goto prune;
|
||||
|
||||
drm_mode_connector_list_update(connector);
|
||||
|
||||
if (maxX && maxY)
|
||||
drm_mode_validate_size(dev, &connector->modes, maxX, maxY);
|
||||
|
||||
if (connector->interlace_allowed)
|
||||
mode_flags |= DRM_MODE_FLAG_INTERLACE;
|
||||
if (connector->doublescan_allowed)
|
||||
mode_flags |= DRM_MODE_FLAG_DBLSCAN;
|
||||
if (connector->stereo_allowed)
|
||||
mode_flags |= DRM_MODE_FLAG_3D_MASK;
|
||||
drm_mode_validate_flag(connector, mode_flags);
|
||||
|
||||
list_for_each_entry(mode, &connector->modes, head) {
|
||||
if (mode->status == MODE_OK)
|
||||
mode->status = connector_funcs->mode_valid(connector,
|
||||
mode);
|
||||
}
|
||||
|
||||
prune:
|
||||
drm_mode_prune_invalid(dev, &connector->modes, verbose_prune);
|
||||
|
||||
if (list_empty(&connector->modes))
|
||||
return 0;
|
||||
|
||||
list_for_each_entry(mode, &connector->modes, head)
|
||||
mode->vrefresh = drm_mode_vrefresh(mode);
|
||||
|
||||
drm_mode_sort(&connector->modes);
|
||||
|
||||
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] probed modes :\n", connector->base.id,
|
||||
drm_get_connector_name(connector));
|
||||
list_for_each_entry(mode, &connector->modes, head) {
|
||||
drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
|
||||
drm_mode_debug_printmodeline(mode);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_helper_probe_single_connector_modes);
|
||||
|
||||
/**
|
||||
* drm_helper_encoder_in_use - check if a given encoder is in use
|
||||
* @encoder: encoder to check
|
||||
@ -1020,232 +879,3 @@ void drm_helper_resume_force_mode(struct drm_device *dev)
|
||||
drm_modeset_unlock_all(dev);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_helper_resume_force_mode);
|
||||
|
||||
/**
|
||||
* drm_kms_helper_hotplug_event - fire off KMS hotplug events
|
||||
* @dev: drm_device whose connector state changed
|
||||
*
|
||||
* This function fires off the uevent for userspace and also calls the
|
||||
* output_poll_changed function, which is most commonly used to inform the fbdev
|
||||
* emulation code and allow it to update the fbcon output configuration.
|
||||
*
|
||||
* Drivers should call this from their hotplug handling code when a change is
|
||||
* detected. Note that this function does not do any output detection of its
|
||||
* own, like drm_helper_hpd_irq_event() does - this is assumed to be done by the
|
||||
* driver already.
|
||||
*
|
||||
* This function must be called from process context with no mode
|
||||
* setting locks held.
|
||||
*/
|
||||
void drm_kms_helper_hotplug_event(struct drm_device *dev)
|
||||
{
|
||||
/* send a uevent + call fbdev */
|
||||
drm_sysfs_hotplug_event(dev);
|
||||
if (dev->mode_config.funcs->output_poll_changed)
|
||||
dev->mode_config.funcs->output_poll_changed(dev);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_kms_helper_hotplug_event);
|
||||
|
||||
#define DRM_OUTPUT_POLL_PERIOD (10*HZ)
|
||||
static void output_poll_execute(struct work_struct *work)
|
||||
{
|
||||
struct delayed_work *delayed_work = to_delayed_work(work);
|
||||
struct drm_device *dev = container_of(delayed_work, struct drm_device, mode_config.output_poll_work);
|
||||
struct drm_connector *connector;
|
||||
enum drm_connector_status old_status;
|
||||
bool repoll = false, changed = false;
|
||||
|
||||
if (!drm_kms_helper_poll)
|
||||
return;
|
||||
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
|
||||
|
||||
/* Ignore forced connectors. */
|
||||
if (connector->force)
|
||||
continue;
|
||||
|
||||
/* Ignore HPD capable connectors and connectors where we don't
|
||||
* want any hotplug detection at all for polling. */
|
||||
if (!connector->polled || connector->polled == DRM_CONNECTOR_POLL_HPD)
|
||||
continue;
|
||||
|
||||
repoll = true;
|
||||
|
||||
old_status = connector->status;
|
||||
/* if we are connected and don't want to poll for disconnect
|
||||
skip it */
|
||||
if (old_status == connector_status_connected &&
|
||||
!(connector->polled & DRM_CONNECTOR_POLL_DISCONNECT))
|
||||
continue;
|
||||
|
||||
connector->status = connector->funcs->detect(connector, false);
|
||||
if (old_status != connector->status) {
|
||||
const char *old, *new;
|
||||
|
||||
old = drm_get_connector_status_name(old_status);
|
||||
new = drm_get_connector_status_name(connector->status);
|
||||
|
||||
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] "
|
||||
"status updated from %s to %s\n",
|
||||
connector->base.id,
|
||||
drm_get_connector_name(connector),
|
||||
old, new);
|
||||
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
|
||||
if (changed)
|
||||
drm_kms_helper_hotplug_event(dev);
|
||||
|
||||
if (repoll)
|
||||
schedule_delayed_work(delayed_work, DRM_OUTPUT_POLL_PERIOD);
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_kms_helper_poll_disable - disable output polling
|
||||
* @dev: drm_device
|
||||
*
|
||||
* This function disables the output polling work.
|
||||
*
|
||||
* Drivers can call this helper from their device suspend implementation. It is
|
||||
* not an error to call this even when output polling isn't enabled or arlready
|
||||
* disabled.
|
||||
*/
|
||||
void drm_kms_helper_poll_disable(struct drm_device *dev)
|
||||
{
|
||||
if (!dev->mode_config.poll_enabled)
|
||||
return;
|
||||
cancel_delayed_work_sync(&dev->mode_config.output_poll_work);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_kms_helper_poll_disable);
|
||||
|
||||
/**
|
||||
* drm_kms_helper_poll_enable - re-enable output polling.
|
||||
* @dev: drm_device
|
||||
*
|
||||
* This function re-enables the output polling work.
|
||||
*
|
||||
* Drivers can call this helper from their device resume implementation. It is
|
||||
* an error to call this when the output polling support has not yet been set
|
||||
* up.
|
||||
*/
|
||||
void drm_kms_helper_poll_enable(struct drm_device *dev)
|
||||
{
|
||||
bool poll = false;
|
||||
struct drm_connector *connector;
|
||||
|
||||
if (!dev->mode_config.poll_enabled || !drm_kms_helper_poll)
|
||||
return;
|
||||
|
||||
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
|
||||
if (connector->polled & (DRM_CONNECTOR_POLL_CONNECT |
|
||||
DRM_CONNECTOR_POLL_DISCONNECT))
|
||||
poll = true;
|
||||
}
|
||||
|
||||
if (poll)
|
||||
schedule_delayed_work(&dev->mode_config.output_poll_work, DRM_OUTPUT_POLL_PERIOD);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_kms_helper_poll_enable);
|
||||
|
||||
/**
|
||||
* drm_kms_helper_poll_init - initialize and enable output polling
|
||||
* @dev: drm_device
|
||||
*
|
||||
* This function intializes and then also enables output polling support for
|
||||
* @dev. Drivers which do not have reliable hotplug support in hardware can use
|
||||
* this helper infrastructure to regularly poll such connectors for changes in
|
||||
* their connection state.
|
||||
*
|
||||
* Drivers can control which connectors are polled by setting the
|
||||
* DRM_CONNECTOR_POLL_CONNECT and DRM_CONNECTOR_POLL_DISCONNECT flags. On
|
||||
* connectors where probing live outputs can result in visual distortion drivers
|
||||
* should not set the DRM_CONNECTOR_POLL_DISCONNECT flag to avoid this.
|
||||
* Connectors which have no flag or only DRM_CONNECTOR_POLL_HPD set are
|
||||
* completely ignored by the polling logic.
|
||||
*
|
||||
* Note that a connector can be both polled and probed from the hotplug handler,
|
||||
* in case the hotplug interrupt is known to be unreliable.
|
||||
*/
|
||||
void drm_kms_helper_poll_init(struct drm_device *dev)
|
||||
{
|
||||
INIT_DELAYED_WORK(&dev->mode_config.output_poll_work, output_poll_execute);
|
||||
dev->mode_config.poll_enabled = true;
|
||||
|
||||
drm_kms_helper_poll_enable(dev);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_kms_helper_poll_init);
|
||||
|
||||
/**
|
||||
* drm_kms_helper_poll_fini - disable output polling and clean it up
|
||||
* @dev: drm_device
|
||||
*/
|
||||
void drm_kms_helper_poll_fini(struct drm_device *dev)
|
||||
{
|
||||
drm_kms_helper_poll_disable(dev);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_kms_helper_poll_fini);
|
||||
|
||||
/**
|
||||
* drm_helper_hpd_irq_event - hotplug processing
|
||||
* @dev: drm_device
|
||||
*
|
||||
* Drivers can use this helper function to run a detect cycle on all connectors
|
||||
* which have the DRM_CONNECTOR_POLL_HPD flag set in their &polled member. All
|
||||
* other connectors are ignored, which is useful to avoid reprobing fixed
|
||||
* panels.
|
||||
*
|
||||
* This helper function is useful for drivers which can't or don't track hotplug
|
||||
* interrupts for each connector.
|
||||
*
|
||||
* Drivers which support hotplug interrupts for each connector individually and
|
||||
* which have a more fine-grained detect logic should bypass this code and
|
||||
* directly call drm_kms_helper_hotplug_event() in case the connector state
|
||||
* changed.
|
||||
*
|
||||
* This function must be called from process context with no mode
|
||||
* setting locks held.
|
||||
*
|
||||
* Note that a connector can be both polled and probed from the hotplug handler,
|
||||
* in case the hotplug interrupt is known to be unreliable.
|
||||
*/
|
||||
bool drm_helper_hpd_irq_event(struct drm_device *dev)
|
||||
{
|
||||
struct drm_connector *connector;
|
||||
enum drm_connector_status old_status;
|
||||
bool changed = false;
|
||||
|
||||
if (!dev->mode_config.poll_enabled)
|
||||
return false;
|
||||
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
|
||||
|
||||
/* Only handle HPD capable connectors. */
|
||||
if (!(connector->polled & DRM_CONNECTOR_POLL_HPD))
|
||||
continue;
|
||||
|
||||
old_status = connector->status;
|
||||
|
||||
connector->status = connector->funcs->detect(connector, false);
|
||||
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %s to %s\n",
|
||||
connector->base.id,
|
||||
drm_get_connector_name(connector),
|
||||
drm_get_connector_status_name(old_status),
|
||||
drm_get_connector_status_name(connector->status));
|
||||
if (old_status != connector->status)
|
||||
changed = true;
|
||||
}
|
||||
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
|
||||
if (changed)
|
||||
drm_kms_helper_hotplug_event(dev);
|
||||
|
||||
return changed;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_helper_hpd_irq_event);
|
||||
|
@ -577,7 +577,9 @@ static u32 drm_dp_i2c_functionality(struct i2c_adapter *adapter)
|
||||
|
||||
/*
|
||||
* Transfer a single I2C-over-AUX message and handle various error conditions,
|
||||
* retrying the transaction as appropriate.
|
||||
* retrying the transaction as appropriate. It is assumed that the
|
||||
* aux->transfer function does not modify anything in the msg other than the
|
||||
* reply field.
|
||||
*/
|
||||
static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
|
||||
{
|
||||
@ -665,11 +667,26 @@ static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
|
||||
{
|
||||
struct drm_dp_aux *aux = adapter->algo_data;
|
||||
unsigned int i, j;
|
||||
struct drm_dp_aux_msg msg;
|
||||
int err = 0;
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
struct drm_dp_aux_msg msg;
|
||||
int err;
|
||||
|
||||
msg.address = msgs[i].addr;
|
||||
msg.request = (msgs[i].flags & I2C_M_RD) ?
|
||||
DP_AUX_I2C_READ :
|
||||
DP_AUX_I2C_WRITE;
|
||||
msg.request |= DP_AUX_I2C_MOT;
|
||||
/* Send a bare address packet to start the transaction.
|
||||
* Zero sized messages specify an address only (bare
|
||||
* address) transaction.
|
||||
*/
|
||||
msg.buffer = NULL;
|
||||
msg.size = 0;
|
||||
err = drm_dp_i2c_do_msg(aux, &msg);
|
||||
if (err < 0)
|
||||
break;
|
||||
/*
|
||||
* Many hardware implementations support FIFOs larger than a
|
||||
* single byte, but it has been empirically determined that
|
||||
@ -678,30 +695,28 @@ static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
|
||||
* transferred byte-by-byte.
|
||||
*/
|
||||
for (j = 0; j < msgs[i].len; j++) {
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
msg.address = msgs[i].addr;
|
||||
|
||||
msg.request = (msgs[i].flags & I2C_M_RD) ?
|
||||
DP_AUX_I2C_READ :
|
||||
DP_AUX_I2C_WRITE;
|
||||
|
||||
/*
|
||||
* All messages except the last one are middle-of-
|
||||
* transfer messages.
|
||||
*/
|
||||
if ((i < num - 1) || (j < msgs[i].len - 1))
|
||||
msg.request |= DP_AUX_I2C_MOT;
|
||||
|
||||
msg.buffer = msgs[i].buf + j;
|
||||
msg.size = 1;
|
||||
|
||||
err = drm_dp_i2c_do_msg(aux, &msg);
|
||||
if (err < 0)
|
||||
return err;
|
||||
break;
|
||||
}
|
||||
if (err < 0)
|
||||
break;
|
||||
}
|
||||
if (err >= 0)
|
||||
err = num;
|
||||
/* Send a bare address packet to close out the transaction.
|
||||
* Zero sized messages specify an address only (bare
|
||||
* address) transaction.
|
||||
*/
|
||||
msg.request &= ~DP_AUX_I2C_MOT;
|
||||
msg.buffer = NULL;
|
||||
msg.size = 0;
|
||||
(void)drm_dp_i2c_do_msg(aux, &msg);
|
||||
|
||||
return num;
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm drm_dp_i2c_algo = {
|
||||
|
@ -207,8 +207,6 @@ int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node)
|
||||
return 0;
|
||||
}
|
||||
|
||||
WARN(1, "no hole found for node 0x%lx + 0x%lx\n",
|
||||
node->start, node->size);
|
||||
return -ENOSPC;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_mm_reserve_node);
|
||||
|
@ -203,9 +203,9 @@ EXPORT_SYMBOL(drm_primary_helper_update);
|
||||
*
|
||||
* Provides a default plane disable handler for primary planes. This is handler
|
||||
* is called in response to a userspace SetPlane operation on the plane with a
|
||||
* NULL framebuffer parameter. We call the driver's modeset handler with a NULL
|
||||
* framebuffer to disable the CRTC if no other planes are currently enabled.
|
||||
* If other planes are still enabled on the same CRTC, we return -EBUSY.
|
||||
* NULL framebuffer parameter. It unconditionally fails the disable call with
|
||||
* -EINVAL the only way to disable the primary plane without driver support is
|
||||
* to disable the entier CRTC. Which does not match the plane ->disable hook.
|
||||
*
|
||||
* Note that some hardware may be able to disable the primary plane without
|
||||
* disabling the whole CRTC. Drivers for such hardware should provide their
|
||||
@ -214,34 +214,11 @@ EXPORT_SYMBOL(drm_primary_helper_update);
|
||||
* disabled primary plane).
|
||||
*
|
||||
* RETURNS:
|
||||
* Zero on success, error code on failure
|
||||
* Unconditionally returns -EINVAL.
|
||||
*/
|
||||
int drm_primary_helper_disable(struct drm_plane *plane)
|
||||
{
|
||||
struct drm_plane *p;
|
||||
struct drm_mode_set set = {
|
||||
.crtc = plane->crtc,
|
||||
.fb = NULL,
|
||||
};
|
||||
|
||||
if (plane->crtc == NULL || plane->fb == NULL)
|
||||
/* Already disabled */
|
||||
return 0;
|
||||
|
||||
list_for_each_entry(p, &plane->dev->mode_config.plane_list, head)
|
||||
if (p != plane && p->fb) {
|
||||
DRM_DEBUG_KMS("Cannot disable primary plane while other planes are still active on CRTC.\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/*
|
||||
* N.B. We call set_config() directly here rather than
|
||||
* drm_mode_set_config_internal() since drm_mode_setplane() already
|
||||
* handles the basic refcounting and we don't need the special
|
||||
* cross-CRTC refcounting (no chance of stealing connectors from
|
||||
* other CRTC's with this update).
|
||||
*/
|
||||
return plane->crtc->funcs->set_config(&set);
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_primary_helper_disable);
|
||||
|
||||
|
426
drivers/gpu/drm/drm_probe_helper.c
Normal file
426
drivers/gpu/drm/drm_probe_helper.c
Normal file
@ -0,0 +1,426 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2008 Intel Corporation
|
||||
* Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
|
||||
*
|
||||
* DRM core CRTC related functions
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and its
|
||||
* documentation for any purpose is hereby granted without fee, provided that
|
||||
* the above copyright notice appear in all copies and that both that copyright
|
||||
* notice and this permission notice appear in supporting documentation, and
|
||||
* that the name of the copyright holders not be used in advertising or
|
||||
* publicity pertaining to distribution of the software without specific,
|
||||
* written prior permission. The copyright holders make no representations
|
||||
* about the suitability of this software for any purpose. It is provided "as
|
||||
* is" without express or implied warranty.
|
||||
*
|
||||
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
||||
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
||||
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
||||
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
||||
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||
* OF THIS SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Keith Packard
|
||||
* Eric Anholt <eric@anholt.net>
|
||||
* Dave Airlie <airlied@linux.ie>
|
||||
* Jesse Barnes <jesse.barnes@intel.com>
|
||||
*/
|
||||
|
||||
#include <linux/export.h>
|
||||
#include <linux/moduleparam.h>
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_fourcc.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_edid.h>
|
||||
|
||||
/**
|
||||
* DOC: output probing helper overview
|
||||
*
|
||||
* This library provides some helper code for output probing. It provides an
|
||||
* implementation of the core connector->fill_modes interface with
|
||||
* drm_helper_probe_single_connector_modes.
|
||||
*
|
||||
* It also provides support for polling connectors with a work item and for
|
||||
* generic hotplug interrupt handling where the driver doesn't or cannot keep
|
||||
* track of a per-connector hpd interrupt.
|
||||
*
|
||||
* This helper library can be used independently of the modeset helper library.
|
||||
* Drivers can also overwrite different parts e.g. use their own hotplug
|
||||
* handling code to avoid probing unrelated outputs.
|
||||
*/
|
||||
|
||||
static bool drm_kms_helper_poll = true;
|
||||
module_param_named(poll, drm_kms_helper_poll, bool, 0600);
|
||||
|
||||
static void drm_mode_validate_flag(struct drm_connector *connector,
|
||||
int flags)
|
||||
{
|
||||
struct drm_display_mode *mode;
|
||||
|
||||
if (flags == (DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_INTERLACE |
|
||||
DRM_MODE_FLAG_3D_MASK))
|
||||
return;
|
||||
|
||||
list_for_each_entry(mode, &connector->modes, head) {
|
||||
if ((mode->flags & DRM_MODE_FLAG_INTERLACE) &&
|
||||
!(flags & DRM_MODE_FLAG_INTERLACE))
|
||||
mode->status = MODE_NO_INTERLACE;
|
||||
if ((mode->flags & DRM_MODE_FLAG_DBLSCAN) &&
|
||||
!(flags & DRM_MODE_FLAG_DBLSCAN))
|
||||
mode->status = MODE_NO_DBLESCAN;
|
||||
if ((mode->flags & DRM_MODE_FLAG_3D_MASK) &&
|
||||
!(flags & DRM_MODE_FLAG_3D_MASK))
|
||||
mode->status = MODE_NO_STEREO;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_helper_probe_single_connector_modes - get complete set of display modes
|
||||
* @connector: connector to probe
|
||||
* @maxX: max width for modes
|
||||
* @maxY: max height for modes
|
||||
*
|
||||
* Based on the helper callbacks implemented by @connector try to detect all
|
||||
* valid modes. Modes will first be added to the connector's probed_modes list,
|
||||
* then culled (based on validity and the @maxX, @maxY parameters) and put into
|
||||
* the normal modes list.
|
||||
*
|
||||
* Intended to be use as a generic implementation of the ->fill_modes()
|
||||
* @connector vfunc for drivers that use the crtc helpers for output mode
|
||||
* filtering and detection.
|
||||
*
|
||||
* Returns:
|
||||
* The number of modes found on @connector.
|
||||
*/
|
||||
int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
|
||||
uint32_t maxX, uint32_t maxY)
|
||||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct drm_display_mode *mode;
|
||||
struct drm_connector_helper_funcs *connector_funcs =
|
||||
connector->helper_private;
|
||||
int count = 0;
|
||||
int mode_flags = 0;
|
||||
bool verbose_prune = true;
|
||||
|
||||
WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
|
||||
|
||||
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id,
|
||||
drm_get_connector_name(connector));
|
||||
/* set all modes to the unverified state */
|
||||
list_for_each_entry(mode, &connector->modes, head)
|
||||
mode->status = MODE_UNVERIFIED;
|
||||
|
||||
if (connector->force) {
|
||||
if (connector->force == DRM_FORCE_ON)
|
||||
connector->status = connector_status_connected;
|
||||
else
|
||||
connector->status = connector_status_disconnected;
|
||||
if (connector->funcs->force)
|
||||
connector->funcs->force(connector);
|
||||
} else {
|
||||
connector->status = connector->funcs->detect(connector, true);
|
||||
}
|
||||
|
||||
/* Re-enable polling in case the global poll config changed. */
|
||||
if (drm_kms_helper_poll != dev->mode_config.poll_running)
|
||||
drm_kms_helper_poll_enable(dev);
|
||||
|
||||
dev->mode_config.poll_running = drm_kms_helper_poll;
|
||||
|
||||
if (connector->status == connector_status_disconnected) {
|
||||
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] disconnected\n",
|
||||
connector->base.id, drm_get_connector_name(connector));
|
||||
drm_mode_connector_update_edid_property(connector, NULL);
|
||||
verbose_prune = false;
|
||||
goto prune;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DRM_LOAD_EDID_FIRMWARE
|
||||
count = drm_load_edid_firmware(connector);
|
||||
if (count == 0)
|
||||
#endif
|
||||
count = (*connector_funcs->get_modes)(connector);
|
||||
|
||||
if (count == 0 && connector->status == connector_status_connected)
|
||||
count = drm_add_modes_noedid(connector, 1024, 768);
|
||||
if (count == 0)
|
||||
goto prune;
|
||||
|
||||
drm_mode_connector_list_update(connector);
|
||||
|
||||
if (maxX && maxY)
|
||||
drm_mode_validate_size(dev, &connector->modes, maxX, maxY);
|
||||
|
||||
if (connector->interlace_allowed)
|
||||
mode_flags |= DRM_MODE_FLAG_INTERLACE;
|
||||
if (connector->doublescan_allowed)
|
||||
mode_flags |= DRM_MODE_FLAG_DBLSCAN;
|
||||
if (connector->stereo_allowed)
|
||||
mode_flags |= DRM_MODE_FLAG_3D_MASK;
|
||||
drm_mode_validate_flag(connector, mode_flags);
|
||||
|
||||
list_for_each_entry(mode, &connector->modes, head) {
|
||||
if (mode->status == MODE_OK)
|
||||
mode->status = connector_funcs->mode_valid(connector,
|
||||
mode);
|
||||
}
|
||||
|
||||
prune:
|
||||
drm_mode_prune_invalid(dev, &connector->modes, verbose_prune);
|
||||
|
||||
if (list_empty(&connector->modes))
|
||||
return 0;
|
||||
|
||||
list_for_each_entry(mode, &connector->modes, head)
|
||||
mode->vrefresh = drm_mode_vrefresh(mode);
|
||||
|
||||
drm_mode_sort(&connector->modes);
|
||||
|
||||
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] probed modes :\n", connector->base.id,
|
||||
drm_get_connector_name(connector));
|
||||
list_for_each_entry(mode, &connector->modes, head) {
|
||||
drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
|
||||
drm_mode_debug_printmodeline(mode);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_helper_probe_single_connector_modes);
|
||||
|
||||
/**
|
||||
* drm_kms_helper_hotplug_event - fire off KMS hotplug events
|
||||
* @dev: drm_device whose connector state changed
|
||||
*
|
||||
* This function fires off the uevent for userspace and also calls the
|
||||
* output_poll_changed function, which is most commonly used to inform the fbdev
|
||||
* emulation code and allow it to update the fbcon output configuration.
|
||||
*
|
||||
* Drivers should call this from their hotplug handling code when a change is
|
||||
* detected. Note that this function does not do any output detection of its
|
||||
* own, like drm_helper_hpd_irq_event() does - this is assumed to be done by the
|
||||
* driver already.
|
||||
*
|
||||
* This function must be called from process context with no mode
|
||||
* setting locks held.
|
||||
*/
|
||||
void drm_kms_helper_hotplug_event(struct drm_device *dev)
|
||||
{
|
||||
/* send a uevent + call fbdev */
|
||||
drm_sysfs_hotplug_event(dev);
|
||||
if (dev->mode_config.funcs->output_poll_changed)
|
||||
dev->mode_config.funcs->output_poll_changed(dev);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_kms_helper_hotplug_event);
|
||||
|
||||
#define DRM_OUTPUT_POLL_PERIOD (10*HZ)
|
||||
static void output_poll_execute(struct work_struct *work)
|
||||
{
|
||||
struct delayed_work *delayed_work = to_delayed_work(work);
|
||||
struct drm_device *dev = container_of(delayed_work, struct drm_device, mode_config.output_poll_work);
|
||||
struct drm_connector *connector;
|
||||
enum drm_connector_status old_status;
|
||||
bool repoll = false, changed = false;
|
||||
|
||||
if (!drm_kms_helper_poll)
|
||||
return;
|
||||
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
|
||||
|
||||
/* Ignore forced connectors. */
|
||||
if (connector->force)
|
||||
continue;
|
||||
|
||||
/* Ignore HPD capable connectors and connectors where we don't
|
||||
* want any hotplug detection at all for polling. */
|
||||
if (!connector->polled || connector->polled == DRM_CONNECTOR_POLL_HPD)
|
||||
continue;
|
||||
|
||||
repoll = true;
|
||||
|
||||
old_status = connector->status;
|
||||
/* if we are connected and don't want to poll for disconnect
|
||||
skip it */
|
||||
if (old_status == connector_status_connected &&
|
||||
!(connector->polled & DRM_CONNECTOR_POLL_DISCONNECT))
|
||||
continue;
|
||||
|
||||
connector->status = connector->funcs->detect(connector, false);
|
||||
if (old_status != connector->status) {
|
||||
const char *old, *new;
|
||||
|
||||
old = drm_get_connector_status_name(old_status);
|
||||
new = drm_get_connector_status_name(connector->status);
|
||||
|
||||
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] "
|
||||
"status updated from %s to %s\n",
|
||||
connector->base.id,
|
||||
drm_get_connector_name(connector),
|
||||
old, new);
|
||||
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
|
||||
if (changed)
|
||||
drm_kms_helper_hotplug_event(dev);
|
||||
|
||||
if (repoll)
|
||||
schedule_delayed_work(delayed_work, DRM_OUTPUT_POLL_PERIOD);
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_kms_helper_poll_disable - disable output polling
|
||||
* @dev: drm_device
|
||||
*
|
||||
* This function disables the output polling work.
|
||||
*
|
||||
* Drivers can call this helper from their device suspend implementation. It is
|
||||
* not an error to call this even when output polling isn't enabled or arlready
|
||||
* disabled.
|
||||
*/
|
||||
void drm_kms_helper_poll_disable(struct drm_device *dev)
|
||||
{
|
||||
if (!dev->mode_config.poll_enabled)
|
||||
return;
|
||||
cancel_delayed_work_sync(&dev->mode_config.output_poll_work);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_kms_helper_poll_disable);
|
||||
|
||||
/**
|
||||
* drm_kms_helper_poll_enable - re-enable output polling.
|
||||
* @dev: drm_device
|
||||
*
|
||||
* This function re-enables the output polling work.
|
||||
*
|
||||
* Drivers can call this helper from their device resume implementation. It is
|
||||
* an error to call this when the output polling support has not yet been set
|
||||
* up.
|
||||
*/
|
||||
void drm_kms_helper_poll_enable(struct drm_device *dev)
|
||||
{
|
||||
bool poll = false;
|
||||
struct drm_connector *connector;
|
||||
|
||||
if (!dev->mode_config.poll_enabled || !drm_kms_helper_poll)
|
||||
return;
|
||||
|
||||
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
|
||||
if (connector->polled & (DRM_CONNECTOR_POLL_CONNECT |
|
||||
DRM_CONNECTOR_POLL_DISCONNECT))
|
||||
poll = true;
|
||||
}
|
||||
|
||||
if (poll)
|
||||
schedule_delayed_work(&dev->mode_config.output_poll_work, DRM_OUTPUT_POLL_PERIOD);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_kms_helper_poll_enable);
|
||||
|
||||
/**
|
||||
* drm_kms_helper_poll_init - initialize and enable output polling
|
||||
* @dev: drm_device
|
||||
*
|
||||
* This function intializes and then also enables output polling support for
|
||||
* @dev. Drivers which do not have reliable hotplug support in hardware can use
|
||||
* this helper infrastructure to regularly poll such connectors for changes in
|
||||
* their connection state.
|
||||
*
|
||||
* Drivers can control which connectors are polled by setting the
|
||||
* DRM_CONNECTOR_POLL_CONNECT and DRM_CONNECTOR_POLL_DISCONNECT flags. On
|
||||
* connectors where probing live outputs can result in visual distortion drivers
|
||||
* should not set the DRM_CONNECTOR_POLL_DISCONNECT flag to avoid this.
|
||||
* Connectors which have no flag or only DRM_CONNECTOR_POLL_HPD set are
|
||||
* completely ignored by the polling logic.
|
||||
*
|
||||
* Note that a connector can be both polled and probed from the hotplug handler,
|
||||
* in case the hotplug interrupt is known to be unreliable.
|
||||
*/
|
||||
void drm_kms_helper_poll_init(struct drm_device *dev)
|
||||
{
|
||||
INIT_DELAYED_WORK(&dev->mode_config.output_poll_work, output_poll_execute);
|
||||
dev->mode_config.poll_enabled = true;
|
||||
|
||||
drm_kms_helper_poll_enable(dev);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_kms_helper_poll_init);
|
||||
|
||||
/**
|
||||
* drm_kms_helper_poll_fini - disable output polling and clean it up
|
||||
* @dev: drm_device
|
||||
*/
|
||||
void drm_kms_helper_poll_fini(struct drm_device *dev)
|
||||
{
|
||||
drm_kms_helper_poll_disable(dev);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_kms_helper_poll_fini);
|
||||
|
||||
/**
|
||||
* drm_helper_hpd_irq_event - hotplug processing
|
||||
* @dev: drm_device
|
||||
*
|
||||
* Drivers can use this helper function to run a detect cycle on all connectors
|
||||
* which have the DRM_CONNECTOR_POLL_HPD flag set in their &polled member. All
|
||||
* other connectors are ignored, which is useful to avoid reprobing fixed
|
||||
* panels.
|
||||
*
|
||||
* This helper function is useful for drivers which can't or don't track hotplug
|
||||
* interrupts for each connector.
|
||||
*
|
||||
* Drivers which support hotplug interrupts for each connector individually and
|
||||
* which have a more fine-grained detect logic should bypass this code and
|
||||
* directly call drm_kms_helper_hotplug_event() in case the connector state
|
||||
* changed.
|
||||
*
|
||||
* This function must be called from process context with no mode
|
||||
* setting locks held.
|
||||
*
|
||||
* Note that a connector can be both polled and probed from the hotplug handler,
|
||||
* in case the hotplug interrupt is known to be unreliable.
|
||||
*/
|
||||
bool drm_helper_hpd_irq_event(struct drm_device *dev)
|
||||
{
|
||||
struct drm_connector *connector;
|
||||
enum drm_connector_status old_status;
|
||||
bool changed = false;
|
||||
|
||||
if (!dev->mode_config.poll_enabled)
|
||||
return false;
|
||||
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
|
||||
|
||||
/* Only handle HPD capable connectors. */
|
||||
if (!(connector->polled & DRM_CONNECTOR_POLL_HPD))
|
||||
continue;
|
||||
|
||||
old_status = connector->status;
|
||||
|
||||
connector->status = connector->funcs->detect(connector, false);
|
||||
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %s to %s\n",
|
||||
connector->base.id,
|
||||
drm_get_connector_name(connector),
|
||||
drm_get_connector_status_name(old_status),
|
||||
drm_get_connector_status_name(connector->status));
|
||||
if (old_status != connector->status)
|
||||
changed = true;
|
||||
}
|
||||
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
|
||||
if (changed)
|
||||
drm_kms_helper_hotplug_event(dev);
|
||||
|
||||
return changed;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_helper_hpd_irq_event);
|
@ -1308,6 +1308,7 @@ struct intel_vbt_data {
|
||||
|
||||
struct {
|
||||
u16 pwm_freq_hz;
|
||||
bool present;
|
||||
bool active_low_pwm;
|
||||
} backlight;
|
||||
|
||||
@ -2431,20 +2432,18 @@ int i915_gem_context_open(struct drm_device *dev, struct drm_file *file);
|
||||
int i915_gem_context_enable(struct drm_i915_private *dev_priv);
|
||||
void i915_gem_context_close(struct drm_device *dev, struct drm_file *file);
|
||||
int i915_switch_context(struct intel_ring_buffer *ring,
|
||||
struct drm_file *file, struct i915_hw_context *to);
|
||||
struct i915_hw_context *to);
|
||||
struct i915_hw_context *
|
||||
i915_gem_context_get(struct drm_i915_file_private *file_priv, u32 id);
|
||||
void i915_gem_context_free(struct kref *ctx_ref);
|
||||
static inline void i915_gem_context_reference(struct i915_hw_context *ctx)
|
||||
{
|
||||
if (ctx->obj && HAS_HW_CONTEXTS(ctx->obj->base.dev))
|
||||
kref_get(&ctx->ref);
|
||||
kref_get(&ctx->ref);
|
||||
}
|
||||
|
||||
static inline void i915_gem_context_unreference(struct i915_hw_context *ctx)
|
||||
{
|
||||
if (ctx->obj && HAS_HW_CONTEXTS(ctx->obj->base.dev))
|
||||
kref_put(&ctx->ref, i915_gem_context_free);
|
||||
kref_put(&ctx->ref, i915_gem_context_free);
|
||||
}
|
||||
|
||||
static inline bool i915_gem_context_is_default(const struct i915_hw_context *c)
|
||||
|
@ -2790,7 +2790,7 @@ int i915_gpu_idle(struct drm_device *dev)
|
||||
|
||||
/* Flush everything onto the inactive list. */
|
||||
for_each_ring(ring, dev_priv, i) {
|
||||
ret = i915_switch_context(ring, NULL, ring->default_context);
|
||||
ret = i915_switch_context(ring, ring->default_context);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -96,9 +96,6 @@
|
||||
#define GEN6_CONTEXT_ALIGN (64<<10)
|
||||
#define GEN7_CONTEXT_ALIGN 4096
|
||||
|
||||
static int do_switch(struct intel_ring_buffer *ring,
|
||||
struct i915_hw_context *to);
|
||||
|
||||
static void do_ppgtt_cleanup(struct i915_hw_ppgtt *ppgtt)
|
||||
{
|
||||
struct drm_device *dev = ppgtt->base.dev;
|
||||
@ -185,13 +182,15 @@ void i915_gem_context_free(struct kref *ctx_ref)
|
||||
typeof(*ctx), ref);
|
||||
struct i915_hw_ppgtt *ppgtt = NULL;
|
||||
|
||||
/* We refcount even the aliasing PPGTT to keep the code symmetric */
|
||||
if (USES_PPGTT(ctx->obj->base.dev))
|
||||
ppgtt = ctx_to_ppgtt(ctx);
|
||||
if (ctx->obj) {
|
||||
/* We refcount even the aliasing PPGTT to keep the code symmetric */
|
||||
if (USES_PPGTT(ctx->obj->base.dev))
|
||||
ppgtt = ctx_to_ppgtt(ctx);
|
||||
|
||||
/* XXX: Free up the object before tearing down the address space, in
|
||||
* case we're bound in the PPGTT */
|
||||
drm_gem_object_unreference(&ctx->obj->base);
|
||||
/* XXX: Free up the object before tearing down the address space, in
|
||||
* case we're bound in the PPGTT */
|
||||
drm_gem_object_unreference(&ctx->obj->base);
|
||||
}
|
||||
|
||||
if (ppgtt)
|
||||
kref_put(&ppgtt->ref, ppgtt_release);
|
||||
@ -232,32 +231,32 @@ __create_hw_context(struct drm_device *dev,
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
kref_init(&ctx->ref);
|
||||
ctx->obj = i915_gem_alloc_object(dev, dev_priv->hw_context_size);
|
||||
INIT_LIST_HEAD(&ctx->link);
|
||||
if (ctx->obj == NULL) {
|
||||
kfree(ctx);
|
||||
DRM_DEBUG_DRIVER("Context object allocated failed\n");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
if (INTEL_INFO(dev)->gen >= 7) {
|
||||
ret = i915_gem_object_set_cache_level(ctx->obj,
|
||||
I915_CACHE_L3_LLC);
|
||||
/* Failure shouldn't ever happen this early */
|
||||
if (WARN_ON(ret))
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
list_add_tail(&ctx->link, &dev_priv->context_list);
|
||||
|
||||
/* Default context will never have a file_priv */
|
||||
if (file_priv == NULL)
|
||||
return ctx;
|
||||
if (dev_priv->hw_context_size) {
|
||||
ctx->obj = i915_gem_alloc_object(dev, dev_priv->hw_context_size);
|
||||
if (ctx->obj == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
ret = idr_alloc(&file_priv->context_idr, ctx, DEFAULT_CONTEXT_ID, 0,
|
||||
GFP_KERNEL);
|
||||
if (ret < 0)
|
||||
goto err_out;
|
||||
if (INTEL_INFO(dev)->gen >= 7) {
|
||||
ret = i915_gem_object_set_cache_level(ctx->obj,
|
||||
I915_CACHE_L3_LLC);
|
||||
/* Failure shouldn't ever happen this early */
|
||||
if (WARN_ON(ret))
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Default context will never have a file_priv */
|
||||
if (file_priv != NULL) {
|
||||
ret = idr_alloc(&file_priv->context_idr, ctx,
|
||||
DEFAULT_CONTEXT_ID, 0, GFP_KERNEL);
|
||||
if (ret < 0)
|
||||
goto err_out;
|
||||
} else
|
||||
ret = DEFAULT_CONTEXT_ID;
|
||||
|
||||
ctx->file_priv = file_priv;
|
||||
ctx->id = ret;
|
||||
@ -294,7 +293,7 @@ i915_gem_create_context(struct drm_device *dev,
|
||||
if (IS_ERR(ctx))
|
||||
return ctx;
|
||||
|
||||
if (is_global_default_ctx) {
|
||||
if (is_global_default_ctx && ctx->obj) {
|
||||
/* We may need to do things with the shrinker which
|
||||
* require us to immediately switch back to the default
|
||||
* context. This can cause a problem as pinning the
|
||||
@ -342,7 +341,7 @@ i915_gem_create_context(struct drm_device *dev,
|
||||
return ctx;
|
||||
|
||||
err_unpin:
|
||||
if (is_global_default_ctx)
|
||||
if (is_global_default_ctx && ctx->obj)
|
||||
i915_gem_object_ggtt_unpin(ctx->obj);
|
||||
err_destroy:
|
||||
i915_gem_context_unreference(ctx);
|
||||
@ -352,32 +351,22 @@ err_destroy:
|
||||
void i915_gem_context_reset(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct intel_ring_buffer *ring;
|
||||
int i;
|
||||
|
||||
if (!HAS_HW_CONTEXTS(dev))
|
||||
return;
|
||||
|
||||
/* Prevent the hardware from restoring the last context (which hung) on
|
||||
* the next switch */
|
||||
for (i = 0; i < I915_NUM_RINGS; i++) {
|
||||
struct i915_hw_context *dctx;
|
||||
if (!(INTEL_INFO(dev)->ring_mask & (1<<i)))
|
||||
continue;
|
||||
struct intel_ring_buffer *ring = &dev_priv->ring[i];
|
||||
struct i915_hw_context *dctx = ring->default_context;
|
||||
|
||||
/* Do a fake switch to the default context */
|
||||
ring = &dev_priv->ring[i];
|
||||
dctx = ring->default_context;
|
||||
if (WARN_ON(!dctx))
|
||||
if (ring->last_context == dctx)
|
||||
continue;
|
||||
|
||||
if (!ring->last_context)
|
||||
continue;
|
||||
|
||||
if (ring->last_context == dctx)
|
||||
continue;
|
||||
|
||||
if (i == RCS) {
|
||||
if (dctx->obj && i == RCS) {
|
||||
WARN_ON(i915_gem_obj_ggtt_pin(dctx->obj,
|
||||
get_context_alignment(dev), 0));
|
||||
/* Fake a finish/inactive */
|
||||
@ -394,44 +383,35 @@ void i915_gem_context_reset(struct drm_device *dev)
|
||||
int i915_gem_context_init(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct intel_ring_buffer *ring;
|
||||
struct i915_hw_context *ctx;
|
||||
int i;
|
||||
|
||||
if (!HAS_HW_CONTEXTS(dev))
|
||||
return 0;
|
||||
|
||||
/* Init should only be called once per module load. Eventually the
|
||||
* restriction on the context_disabled check can be loosened. */
|
||||
if (WARN_ON(dev_priv->ring[RCS].default_context))
|
||||
return 0;
|
||||
|
||||
dev_priv->hw_context_size = round_up(get_context_size(dev), 4096);
|
||||
|
||||
if (dev_priv->hw_context_size > (1<<20)) {
|
||||
DRM_DEBUG_DRIVER("Disabling HW Contexts; invalid size\n");
|
||||
return -E2BIG;
|
||||
if (HAS_HW_CONTEXTS(dev)) {
|
||||
dev_priv->hw_context_size = round_up(get_context_size(dev), 4096);
|
||||
if (dev_priv->hw_context_size > (1<<20)) {
|
||||
DRM_DEBUG_DRIVER("Disabling HW Contexts; invalid size %d\n",
|
||||
dev_priv->hw_context_size);
|
||||
dev_priv->hw_context_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
dev_priv->ring[RCS].default_context =
|
||||
i915_gem_create_context(dev, NULL, USES_PPGTT(dev));
|
||||
|
||||
if (IS_ERR_OR_NULL(dev_priv->ring[RCS].default_context)) {
|
||||
DRM_DEBUG_DRIVER("Disabling HW Contexts; create failed %ld\n",
|
||||
PTR_ERR(dev_priv->ring[RCS].default_context));
|
||||
return PTR_ERR(dev_priv->ring[RCS].default_context);
|
||||
ctx = i915_gem_create_context(dev, NULL, USES_PPGTT(dev));
|
||||
if (IS_ERR(ctx)) {
|
||||
DRM_ERROR("Failed to create default global context (error %ld)\n",
|
||||
PTR_ERR(ctx));
|
||||
return PTR_ERR(ctx);
|
||||
}
|
||||
|
||||
for (i = RCS + 1; i < I915_NUM_RINGS; i++) {
|
||||
if (!(INTEL_INFO(dev)->ring_mask & (1<<i)))
|
||||
continue;
|
||||
/* NB: RCS will hold a ref for all rings */
|
||||
for (i = 0; i < I915_NUM_RINGS; i++)
|
||||
dev_priv->ring[i].default_context = ctx;
|
||||
|
||||
ring = &dev_priv->ring[i];
|
||||
|
||||
/* NB: RCS will hold a ref for all rings */
|
||||
ring->default_context = dev_priv->ring[RCS].default_context;
|
||||
}
|
||||
|
||||
DRM_DEBUG_DRIVER("HW context support initialized\n");
|
||||
DRM_DEBUG_DRIVER("%s context support initialized\n", dev_priv->hw_context_size ? "HW" : "fake");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -441,33 +421,30 @@ void i915_gem_context_fini(struct drm_device *dev)
|
||||
struct i915_hw_context *dctx = dev_priv->ring[RCS].default_context;
|
||||
int i;
|
||||
|
||||
if (!HAS_HW_CONTEXTS(dev))
|
||||
return;
|
||||
if (dctx->obj) {
|
||||
/* The only known way to stop the gpu from accessing the hw context is
|
||||
* to reset it. Do this as the very last operation to avoid confusing
|
||||
* other code, leading to spurious errors. */
|
||||
intel_gpu_reset(dev);
|
||||
|
||||
/* The only known way to stop the gpu from accessing the hw context is
|
||||
* to reset it. Do this as the very last operation to avoid confusing
|
||||
* other code, leading to spurious errors. */
|
||||
intel_gpu_reset(dev);
|
||||
|
||||
/* When default context is created and switched to, base object refcount
|
||||
* will be 2 (+1 from object creation and +1 from do_switch()).
|
||||
* i915_gem_context_fini() will be called after gpu_idle() has switched
|
||||
* to default context. So we need to unreference the base object once
|
||||
* to offset the do_switch part, so that i915_gem_context_unreference()
|
||||
* can then free the base object correctly. */
|
||||
WARN_ON(!dev_priv->ring[RCS].last_context);
|
||||
if (dev_priv->ring[RCS].last_context == dctx) {
|
||||
/* Fake switch to NULL context */
|
||||
WARN_ON(dctx->obj->active);
|
||||
i915_gem_object_ggtt_unpin(dctx->obj);
|
||||
i915_gem_context_unreference(dctx);
|
||||
dev_priv->ring[RCS].last_context = NULL;
|
||||
/* When default context is created and switched to, base object refcount
|
||||
* will be 2 (+1 from object creation and +1 from do_switch()).
|
||||
* i915_gem_context_fini() will be called after gpu_idle() has switched
|
||||
* to default context. So we need to unreference the base object once
|
||||
* to offset the do_switch part, so that i915_gem_context_unreference()
|
||||
* can then free the base object correctly. */
|
||||
WARN_ON(!dev_priv->ring[RCS].last_context);
|
||||
if (dev_priv->ring[RCS].last_context == dctx) {
|
||||
/* Fake switch to NULL context */
|
||||
WARN_ON(dctx->obj->active);
|
||||
i915_gem_object_ggtt_unpin(dctx->obj);
|
||||
i915_gem_context_unreference(dctx);
|
||||
dev_priv->ring[RCS].last_context = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < I915_NUM_RINGS; i++) {
|
||||
struct intel_ring_buffer *ring = &dev_priv->ring[i];
|
||||
if (!(INTEL_INFO(dev)->ring_mask & (1<<i)))
|
||||
continue;
|
||||
|
||||
if (ring->last_context)
|
||||
i915_gem_context_unreference(ring->last_context);
|
||||
@ -478,7 +455,6 @@ void i915_gem_context_fini(struct drm_device *dev)
|
||||
|
||||
i915_gem_object_ggtt_unpin(dctx->obj);
|
||||
i915_gem_context_unreference(dctx);
|
||||
dev_priv->mm.aliasing_ppgtt = NULL;
|
||||
}
|
||||
|
||||
int i915_gem_context_enable(struct drm_i915_private *dev_priv)
|
||||
@ -486,9 +462,6 @@ int i915_gem_context_enable(struct drm_i915_private *dev_priv)
|
||||
struct intel_ring_buffer *ring;
|
||||
int ret, i;
|
||||
|
||||
if (!HAS_HW_CONTEXTS(dev_priv->dev))
|
||||
return 0;
|
||||
|
||||
/* This is the only place the aliasing PPGTT gets enabled, which means
|
||||
* it has to happen before we bail on reset */
|
||||
if (dev_priv->mm.aliasing_ppgtt) {
|
||||
@ -503,7 +476,7 @@ int i915_gem_context_enable(struct drm_i915_private *dev_priv)
|
||||
BUG_ON(!dev_priv->ring[RCS].default_context);
|
||||
|
||||
for_each_ring(ring, dev_priv, i) {
|
||||
ret = do_switch(ring, ring->default_context);
|
||||
ret = i915_switch_context(ring, ring->default_context);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
@ -526,19 +499,6 @@ static int context_idr_cleanup(int id, void *p, void *data)
|
||||
int i915_gem_context_open(struct drm_device *dev, struct drm_file *file)
|
||||
{
|
||||
struct drm_i915_file_private *file_priv = file->driver_priv;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
|
||||
if (!HAS_HW_CONTEXTS(dev)) {
|
||||
/* Cheat for hang stats */
|
||||
file_priv->private_default_ctx =
|
||||
kzalloc(sizeof(struct i915_hw_context), GFP_KERNEL);
|
||||
|
||||
if (file_priv->private_default_ctx == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
file_priv->private_default_ctx->vm = &dev_priv->gtt.base;
|
||||
return 0;
|
||||
}
|
||||
|
||||
idr_init(&file_priv->context_idr);
|
||||
|
||||
@ -559,14 +519,10 @@ void i915_gem_context_close(struct drm_device *dev, struct drm_file *file)
|
||||
{
|
||||
struct drm_i915_file_private *file_priv = file->driver_priv;
|
||||
|
||||
if (!HAS_HW_CONTEXTS(dev)) {
|
||||
kfree(file_priv->private_default_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
idr_for_each(&file_priv->context_idr, context_idr_cleanup, NULL);
|
||||
i915_gem_context_unreference(file_priv->private_default_ctx);
|
||||
idr_destroy(&file_priv->context_idr);
|
||||
|
||||
i915_gem_context_unreference(file_priv->private_default_ctx);
|
||||
}
|
||||
|
||||
struct i915_hw_context *
|
||||
@ -574,9 +530,6 @@ i915_gem_context_get(struct drm_i915_file_private *file_priv, u32 id)
|
||||
{
|
||||
struct i915_hw_context *ctx;
|
||||
|
||||
if (!HAS_HW_CONTEXTS(file_priv->dev_priv->dev))
|
||||
return file_priv->private_default_ctx;
|
||||
|
||||
ctx = (struct i915_hw_context *)idr_find(&file_priv->context_idr, id);
|
||||
if (!ctx)
|
||||
return ERR_PTR(-ENOENT);
|
||||
@ -758,7 +711,6 @@ unpin_out:
|
||||
/**
|
||||
* i915_switch_context() - perform a GPU context switch.
|
||||
* @ring: ring for which we'll execute the context switch
|
||||
* @file_priv: file_priv associated with the context, may be NULL
|
||||
* @to: the context to switch to
|
||||
*
|
||||
* The context life cycle is simple. The context refcount is incremented and
|
||||
@ -767,24 +719,30 @@ unpin_out:
|
||||
* object while letting the normal object tracking destroy the backing BO.
|
||||
*/
|
||||
int i915_switch_context(struct intel_ring_buffer *ring,
|
||||
struct drm_file *file,
|
||||
struct i915_hw_context *to)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = ring->dev->dev_private;
|
||||
|
||||
WARN_ON(!mutex_is_locked(&dev_priv->dev->struct_mutex));
|
||||
|
||||
BUG_ON(file && to == NULL);
|
||||
|
||||
/* We have the fake context */
|
||||
if (!HAS_HW_CONTEXTS(ring->dev)) {
|
||||
ring->last_context = to;
|
||||
if (to->obj == NULL) { /* We have the fake context */
|
||||
if (to != ring->last_context) {
|
||||
i915_gem_context_reference(to);
|
||||
if (ring->last_context)
|
||||
i915_gem_context_unreference(ring->last_context);
|
||||
ring->last_context = to;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return do_switch(ring, to);
|
||||
}
|
||||
|
||||
static bool hw_context_enabled(struct drm_device *dev)
|
||||
{
|
||||
return to_i915(dev)->hw_context_size;
|
||||
}
|
||||
|
||||
int i915_gem_context_create_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file)
|
||||
{
|
||||
@ -793,7 +751,7 @@ int i915_gem_context_create_ioctl(struct drm_device *dev, void *data,
|
||||
struct i915_hw_context *ctx;
|
||||
int ret;
|
||||
|
||||
if (!HAS_HW_CONTEXTS(dev))
|
||||
if (!hw_context_enabled(dev))
|
||||
return -ENODEV;
|
||||
|
||||
ret = i915_mutex_lock_interruptible(dev);
|
||||
|
@ -1221,7 +1221,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = i915_switch_context(ring, file, ctx);
|
||||
ret = i915_switch_context(ring, ctx);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
|
@ -287,6 +287,9 @@ parse_lfp_backlight(struct drm_i915_private *dev_priv, struct bdb_header *bdb)
|
||||
const struct bdb_lfp_backlight_data *backlight_data;
|
||||
const struct bdb_lfp_backlight_data_entry *entry;
|
||||
|
||||
/* Err to enabling backlight if no backlight block. */
|
||||
dev_priv->vbt.backlight.present = true;
|
||||
|
||||
backlight_data = find_section(bdb, BDB_LVDS_BACKLIGHT);
|
||||
if (!backlight_data)
|
||||
return;
|
||||
@ -299,6 +302,13 @@ parse_lfp_backlight(struct drm_i915_private *dev_priv, struct bdb_header *bdb)
|
||||
|
||||
entry = &backlight_data->data[panel_type];
|
||||
|
||||
dev_priv->vbt.backlight.present = entry->type == BDB_BACKLIGHT_TYPE_PWM;
|
||||
if (!dev_priv->vbt.backlight.present) {
|
||||
DRM_DEBUG_KMS("PWM backlight not present in VBT (type %u)\n",
|
||||
entry->type);
|
||||
return;
|
||||
}
|
||||
|
||||
dev_priv->vbt.backlight.pwm_freq_hz = entry->pwm_freq_hz;
|
||||
dev_priv->vbt.backlight.active_low_pwm = entry->active_low_pwm;
|
||||
DRM_DEBUG_KMS("VBT backlight PWM modulation frequency %u Hz, "
|
||||
|
@ -374,6 +374,9 @@ struct bdb_lvds_lfp_data {
|
||||
struct bdb_lvds_lfp_data_entry data[16];
|
||||
} __packed;
|
||||
|
||||
#define BDB_BACKLIGHT_TYPE_NONE 0
|
||||
#define BDB_BACKLIGHT_TYPE_PWM 2
|
||||
|
||||
struct bdb_lfp_backlight_data_entry {
|
||||
u8 type:2;
|
||||
u8 active_low_pwm:1;
|
||||
|
@ -575,7 +575,8 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define HEADER_SIZE 4
|
||||
#define BARE_ADDRESS_SIZE 3
|
||||
#define HEADER_SIZE (BARE_ADDRESS_SIZE + 1)
|
||||
static ssize_t
|
||||
intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
|
||||
{
|
||||
@ -592,7 +593,7 @@ intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
|
||||
switch (msg->request & ~DP_AUX_I2C_MOT) {
|
||||
case DP_AUX_NATIVE_WRITE:
|
||||
case DP_AUX_I2C_WRITE:
|
||||
txsize = HEADER_SIZE + msg->size;
|
||||
txsize = msg->size ? HEADER_SIZE + msg->size : BARE_ADDRESS_SIZE;
|
||||
rxsize = 1;
|
||||
|
||||
if (WARN_ON(txsize > 20))
|
||||
@ -611,7 +612,7 @@ intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
|
||||
|
||||
case DP_AUX_NATIVE_READ:
|
||||
case DP_AUX_I2C_READ:
|
||||
txsize = HEADER_SIZE;
|
||||
txsize = msg->size ? HEADER_SIZE : BARE_ADDRESS_SIZE;
|
||||
rxsize = msg->size + 1;
|
||||
|
||||
if (WARN_ON(rxsize > 20))
|
||||
|
@ -1065,6 +1065,11 @@ int intel_panel_setup_backlight(struct drm_connector *connector)
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
if (!dev_priv->vbt.backlight.present) {
|
||||
DRM_DEBUG_KMS("native backlight control not available per VBT\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* set level and max in panel struct */
|
||||
spin_lock_irqsave(&dev_priv->backlight_lock, flags);
|
||||
ret = dev_priv->display.setup_backlight(intel_connector);
|
||||
|
@ -1545,6 +1545,16 @@ static void i9xx_update_wm(struct drm_crtc *unused_crtc)
|
||||
|
||||
DRM_DEBUG_KMS("FIFO watermarks - A: %d, B: %d\n", planea_wm, planeb_wm);
|
||||
|
||||
if (IS_I915GM(dev) && enabled) {
|
||||
struct intel_framebuffer *fb;
|
||||
|
||||
fb = to_intel_framebuffer(enabled->primary->fb);
|
||||
|
||||
/* self-refresh seems busted with untiled */
|
||||
if (fb->obj->tiling_mode == I915_TILING_NONE)
|
||||
enabled = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Overlay gets an aggressive default since video jitter is bad.
|
||||
*/
|
||||
|
@ -109,7 +109,7 @@ nouveau_bios_shadow_pramin(struct nouveau_bios *bios)
|
||||
return;
|
||||
}
|
||||
|
||||
addr = (u64)(addr >> 8) << 8;
|
||||
addr = (addr & 0xffffff00) << 8;
|
||||
if (!addr) {
|
||||
addr = (u64)nv_rd32(bios, 0x001700) << 16;
|
||||
addr += 0xf0000;
|
||||
|
@ -33,6 +33,7 @@ struct omap_crtc {
|
||||
int pipe;
|
||||
enum omap_channel channel;
|
||||
struct omap_overlay_manager_info info;
|
||||
struct drm_encoder *current_encoder;
|
||||
|
||||
/*
|
||||
* Temporary: eventually this will go away, but it is needed
|
||||
@ -120,13 +121,25 @@ static void omap_crtc_start_update(struct omap_overlay_manager *mgr)
|
||||
{
|
||||
}
|
||||
|
||||
static void set_enabled(struct drm_crtc *crtc, bool enable);
|
||||
|
||||
static int omap_crtc_enable(struct omap_overlay_manager *mgr)
|
||||
{
|
||||
struct omap_crtc *omap_crtc = omap_crtcs[mgr->id];
|
||||
|
||||
dispc_mgr_setup(omap_crtc->channel, &omap_crtc->info);
|
||||
dispc_mgr_set_timings(omap_crtc->channel,
|
||||
&omap_crtc->timings);
|
||||
set_enabled(&omap_crtc->base, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void omap_crtc_disable(struct omap_overlay_manager *mgr)
|
||||
{
|
||||
struct omap_crtc *omap_crtc = omap_crtcs[mgr->id];
|
||||
|
||||
set_enabled(&omap_crtc->base, false);
|
||||
}
|
||||
|
||||
static void omap_crtc_set_timings(struct omap_overlay_manager *mgr,
|
||||
@ -184,7 +197,6 @@ static void omap_crtc_destroy(struct drm_crtc *crtc)
|
||||
WARN_ON(omap_crtc->apply_irq.registered);
|
||||
omap_irq_unregister(crtc->dev, &omap_crtc->error_irq);
|
||||
|
||||
omap_crtc->plane->funcs->destroy(omap_crtc->plane);
|
||||
drm_crtc_cleanup(crtc);
|
||||
|
||||
kfree(omap_crtc);
|
||||
@ -338,17 +350,23 @@ static int omap_crtc_page_flip_locked(struct drm_crtc *crtc,
|
||||
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
|
||||
struct drm_plane *primary = crtc->primary;
|
||||
struct drm_gem_object *bo;
|
||||
unsigned long flags;
|
||||
|
||||
DBG("%d -> %d (event=%p)", primary->fb ? primary->fb->base.id : -1,
|
||||
fb->base.id, event);
|
||||
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
|
||||
if (omap_crtc->old_fb) {
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
dev_err(dev->dev, "already a pending flip\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
omap_crtc->event = event;
|
||||
primary->fb = fb;
|
||||
omap_crtc->old_fb = primary->fb = fb;
|
||||
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
|
||||
/*
|
||||
* Hold a reference temporarily until the crtc is updated
|
||||
@ -528,38 +546,46 @@ static void set_enabled(struct drm_crtc *crtc, bool enable)
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
|
||||
enum omap_channel channel = omap_crtc->channel;
|
||||
struct omap_irq_wait *wait = NULL;
|
||||
struct omap_irq_wait *wait;
|
||||
u32 framedone_irq, vsync_irq;
|
||||
int ret;
|
||||
|
||||
if (dispc_mgr_is_enabled(channel) == enable)
|
||||
return;
|
||||
|
||||
/* ignore sync-lost irqs during enable/disable */
|
||||
/*
|
||||
* Digit output produces some sync lost interrupts during the first
|
||||
* frame when enabling, so we need to ignore those.
|
||||
*/
|
||||
omap_irq_unregister(crtc->dev, &omap_crtc->error_irq);
|
||||
|
||||
if (dispc_mgr_get_framedone_irq(channel)) {
|
||||
if (!enable) {
|
||||
wait = omap_irq_wait_init(dev,
|
||||
dispc_mgr_get_framedone_irq(channel), 1);
|
||||
}
|
||||
framedone_irq = dispc_mgr_get_framedone_irq(channel);
|
||||
vsync_irq = dispc_mgr_get_vsync_irq(channel);
|
||||
|
||||
if (enable) {
|
||||
wait = omap_irq_wait_init(dev, vsync_irq, 1);
|
||||
} else {
|
||||
/*
|
||||
* When we disable digit output, we need to wait until fields
|
||||
* are done. Otherwise the DSS is still working, and turning
|
||||
* off the clocks prevents DSS from going to OFF mode. And when
|
||||
* enabling, we need to wait for the extra sync losts
|
||||
* When we disable the digit output, we need to wait for
|
||||
* FRAMEDONE to know that DISPC has finished with the output.
|
||||
*
|
||||
* OMAP2/3 does not have FRAMEDONE irq for digit output, and in
|
||||
* that case we need to use vsync interrupt, and wait for both
|
||||
* even and odd frames.
|
||||
*/
|
||||
wait = omap_irq_wait_init(dev,
|
||||
dispc_mgr_get_vsync_irq(channel), 2);
|
||||
|
||||
if (framedone_irq)
|
||||
wait = omap_irq_wait_init(dev, framedone_irq, 1);
|
||||
else
|
||||
wait = omap_irq_wait_init(dev, vsync_irq, 2);
|
||||
}
|
||||
|
||||
dispc_mgr_enable(channel, enable);
|
||||
|
||||
if (wait) {
|
||||
int ret = omap_irq_wait(dev, wait, msecs_to_jiffies(100));
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "%s: timeout waiting for %s\n",
|
||||
omap_crtc->name, enable ? "enable" : "disable");
|
||||
}
|
||||
ret = omap_irq_wait(dev, wait, msecs_to_jiffies(100));
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "%s: timeout waiting for %s\n",
|
||||
omap_crtc->name, enable ? "enable" : "disable");
|
||||
}
|
||||
|
||||
omap_irq_register(crtc->dev, &omap_crtc->error_irq);
|
||||
@ -586,8 +612,12 @@ static void omap_crtc_pre_apply(struct omap_drm_apply *apply)
|
||||
}
|
||||
}
|
||||
|
||||
if (omap_crtc->current_encoder && encoder != omap_crtc->current_encoder)
|
||||
omap_encoder_set_enabled(omap_crtc->current_encoder, false);
|
||||
|
||||
omap_crtc->current_encoder = encoder;
|
||||
|
||||
if (!omap_crtc->enabled) {
|
||||
set_enabled(&omap_crtc->base, false);
|
||||
if (encoder)
|
||||
omap_encoder_set_enabled(encoder, false);
|
||||
} else {
|
||||
@ -596,13 +626,7 @@ static void omap_crtc_pre_apply(struct omap_drm_apply *apply)
|
||||
omap_encoder_update(encoder, omap_crtc->mgr,
|
||||
&omap_crtc->timings);
|
||||
omap_encoder_set_enabled(encoder, true);
|
||||
omap_crtc->full_update = false;
|
||||
}
|
||||
|
||||
dispc_mgr_setup(omap_crtc->channel, &omap_crtc->info);
|
||||
dispc_mgr_set_timings(omap_crtc->channel,
|
||||
&omap_crtc->timings);
|
||||
set_enabled(&omap_crtc->base, true);
|
||||
}
|
||||
|
||||
omap_crtc->full_update = false;
|
||||
@ -613,10 +637,30 @@ static void omap_crtc_post_apply(struct omap_drm_apply *apply)
|
||||
/* nothing needed for post-apply */
|
||||
}
|
||||
|
||||
void omap_crtc_flush(struct drm_crtc *crtc)
|
||||
{
|
||||
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
|
||||
int loops = 0;
|
||||
|
||||
while (!list_empty(&omap_crtc->pending_applies) ||
|
||||
!list_empty(&omap_crtc->queued_applies) ||
|
||||
omap_crtc->event || omap_crtc->old_fb) {
|
||||
|
||||
if (++loops > 10) {
|
||||
dev_err(crtc->dev->dev,
|
||||
"omap_crtc_flush() timeout\n");
|
||||
break;
|
||||
}
|
||||
|
||||
schedule_timeout_uninterruptible(msecs_to_jiffies(20));
|
||||
}
|
||||
}
|
||||
|
||||
static const char *channel_names[] = {
|
||||
[OMAP_DSS_CHANNEL_LCD] = "lcd",
|
||||
[OMAP_DSS_CHANNEL_DIGIT] = "tv",
|
||||
[OMAP_DSS_CHANNEL_LCD2] = "lcd2",
|
||||
[OMAP_DSS_CHANNEL_LCD3] = "lcd3",
|
||||
};
|
||||
|
||||
void omap_crtc_pre_init(void)
|
||||
|
@ -513,12 +513,18 @@ static int dev_load(struct drm_device *dev, unsigned long flags)
|
||||
static int dev_unload(struct drm_device *dev)
|
||||
{
|
||||
struct omap_drm_private *priv = dev->dev_private;
|
||||
int i;
|
||||
|
||||
DBG("unload: dev=%p", dev);
|
||||
|
||||
drm_kms_helper_poll_fini(dev);
|
||||
|
||||
omap_fbdev_free(dev);
|
||||
|
||||
/* flush crtcs so the fbs get released */
|
||||
for (i = 0; i < priv->num_crtcs; i++)
|
||||
omap_crtc_flush(priv->crtcs[i]);
|
||||
|
||||
omap_modeset_free(dev);
|
||||
omap_gem_deinit(dev);
|
||||
|
||||
@ -696,10 +702,11 @@ static int pdev_remove(struct platform_device *device)
|
||||
{
|
||||
DBG("");
|
||||
|
||||
drm_put_dev(platform_get_drvdata(device));
|
||||
|
||||
omap_disconnect_dssdevs();
|
||||
omap_crtc_pre_uninit();
|
||||
|
||||
drm_put_dev(platform_get_drvdata(device));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -726,18 +733,33 @@ static struct platform_driver pdev = {
|
||||
|
||||
static int __init omap_drm_init(void)
|
||||
{
|
||||
int r;
|
||||
|
||||
DBG("init");
|
||||
if (platform_driver_register(&omap_dmm_driver)) {
|
||||
/* we can continue on without DMM.. so not fatal */
|
||||
dev_err(NULL, "DMM registration failed\n");
|
||||
|
||||
r = platform_driver_register(&omap_dmm_driver);
|
||||
if (r) {
|
||||
pr_err("DMM driver registration failed\n");
|
||||
return r;
|
||||
}
|
||||
return platform_driver_register(&pdev);
|
||||
|
||||
r = platform_driver_register(&pdev);
|
||||
if (r) {
|
||||
pr_err("omapdrm driver registration failed\n");
|
||||
platform_driver_unregister(&omap_dmm_driver);
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit omap_drm_fini(void)
|
||||
{
|
||||
DBG("fini");
|
||||
|
||||
platform_driver_unregister(&pdev);
|
||||
|
||||
platform_driver_unregister(&omap_dmm_driver);
|
||||
}
|
||||
|
||||
/* need late_initcall() so we load after dss_driver's are loaded */
|
||||
|
@ -163,6 +163,7 @@ void omap_crtc_pre_init(void);
|
||||
void omap_crtc_pre_uninit(void);
|
||||
struct drm_crtc *omap_crtc_init(struct drm_device *dev,
|
||||
struct drm_plane *plane, enum omap_channel channel, int id);
|
||||
void omap_crtc_flush(struct drm_crtc *crtc);
|
||||
|
||||
struct drm_plane *omap_plane_init(struct drm_device *dev,
|
||||
int plane_id, bool private_plane);
|
||||
|
@ -218,6 +218,20 @@ void omap_framebuffer_update_scanout(struct drm_framebuffer *fb,
|
||||
info->rotation_type = OMAP_DSS_ROT_TILER;
|
||||
info->screen_width = omap_gem_tiled_stride(plane->bo, orient);
|
||||
} else {
|
||||
switch (win->rotation & 0xf) {
|
||||
case 0:
|
||||
case BIT(DRM_ROTATE_0):
|
||||
/* OK */
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_warn(fb->dev->dev,
|
||||
"rotation '%d' ignored for non-tiled fb\n",
|
||||
win->rotation);
|
||||
win->rotation = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
info->paddr = get_linear_addr(plane, format, 0, x, y);
|
||||
info->rotation_type = OMAP_DSS_ROT_DMA;
|
||||
info->screen_width = plane->pitch;
|
||||
|
@ -371,6 +371,9 @@ void omap_fbdev_free(struct drm_device *dev)
|
||||
|
||||
fbdev = to_omap_fbdev(priv->fbdev);
|
||||
|
||||
/* release the ref taken in omap_fbdev_create() */
|
||||
omap_gem_put_paddr(fbdev->bo);
|
||||
|
||||
/* this will free the backing object */
|
||||
if (fbdev->fb) {
|
||||
drm_framebuffer_unregister_private(fbdev->fb);
|
||||
|
@ -980,12 +980,9 @@ int omap_gem_resume(struct device *dev)
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m)
|
||||
{
|
||||
struct drm_device *dev = obj->dev;
|
||||
struct omap_gem_object *omap_obj = to_omap_bo(obj);
|
||||
uint64_t off;
|
||||
|
||||
WARN_ON(!mutex_is_locked(&dev->struct_mutex));
|
||||
|
||||
off = drm_vma_node_start(&obj->vma_node);
|
||||
|
||||
seq_printf(m, "%08x: %2d (%2d) %08llx %08Zx (%2d) %p %4d",
|
||||
@ -1050,10 +1047,10 @@ static inline bool is_waiting(struct omap_gem_sync_waiter *waiter)
|
||||
{
|
||||
struct omap_gem_object *omap_obj = waiter->omap_obj;
|
||||
if ((waiter->op & OMAP_GEM_READ) &&
|
||||
(omap_obj->sync->read_complete < waiter->read_target))
|
||||
(omap_obj->sync->write_complete < waiter->write_target))
|
||||
return true;
|
||||
if ((waiter->op & OMAP_GEM_WRITE) &&
|
||||
(omap_obj->sync->write_complete < waiter->write_target))
|
||||
(omap_obj->sync->read_complete < waiter->read_target))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
@ -1229,6 +1226,8 @@ int omap_gem_op_async(struct drm_gem_object *obj, enum omap_gem_op op,
|
||||
}
|
||||
|
||||
spin_unlock(&sync_lock);
|
||||
|
||||
kfree(waiter);
|
||||
}
|
||||
|
||||
/* no waiting.. */
|
||||
|
@ -225,6 +225,11 @@ int omap_plane_mode_set(struct drm_plane *plane,
|
||||
omap_plane->apply_done_cb.arg = arg;
|
||||
}
|
||||
|
||||
if (plane->fb)
|
||||
drm_framebuffer_unreference(plane->fb);
|
||||
|
||||
drm_framebuffer_reference(fb);
|
||||
|
||||
plane->fb = fb;
|
||||
plane->crtc = crtc;
|
||||
|
||||
@ -241,10 +246,13 @@ static int omap_plane_update(struct drm_plane *plane,
|
||||
struct omap_plane *omap_plane = to_omap_plane(plane);
|
||||
omap_plane->enabled = true;
|
||||
|
||||
if (plane->fb)
|
||||
drm_framebuffer_unreference(plane->fb);
|
||||
|
||||
drm_framebuffer_reference(fb);
|
||||
/* omap_plane_mode_set() takes adjusted src */
|
||||
switch (omap_plane->win.rotation & 0xf) {
|
||||
case BIT(DRM_ROTATE_90):
|
||||
case BIT(DRM_ROTATE_270):
|
||||
swap(src_w, src_h);
|
||||
break;
|
||||
}
|
||||
|
||||
return omap_plane_mode_set(plane, crtc, fb,
|
||||
crtc_x, crtc_y, crtc_w, crtc_h,
|
||||
|
@ -142,7 +142,8 @@ static int radeon_process_aux_ch(struct radeon_i2c_chan *chan,
|
||||
return recv_bytes;
|
||||
}
|
||||
|
||||
#define HEADER_SIZE 4
|
||||
#define BARE_ADDRESS_SIZE 3
|
||||
#define HEADER_SIZE (BARE_ADDRESS_SIZE + 1)
|
||||
|
||||
static ssize_t
|
||||
radeon_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
|
||||
@ -160,13 +161,19 @@ radeon_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
|
||||
tx_buf[0] = msg->address & 0xff;
|
||||
tx_buf[1] = msg->address >> 8;
|
||||
tx_buf[2] = msg->request << 4;
|
||||
tx_buf[3] = msg->size - 1;
|
||||
tx_buf[3] = msg->size ? (msg->size - 1) : 0;
|
||||
|
||||
switch (msg->request & ~DP_AUX_I2C_MOT) {
|
||||
case DP_AUX_NATIVE_WRITE:
|
||||
case DP_AUX_I2C_WRITE:
|
||||
/* tx_size needs to be 4 even for bare address packets since the atom
|
||||
* table needs the info in tx_buf[3].
|
||||
*/
|
||||
tx_size = HEADER_SIZE + msg->size;
|
||||
tx_buf[3] |= tx_size << 4;
|
||||
if (msg->size == 0)
|
||||
tx_buf[3] |= BARE_ADDRESS_SIZE << 4;
|
||||
else
|
||||
tx_buf[3] |= tx_size << 4;
|
||||
memcpy(tx_buf + HEADER_SIZE, msg->buffer, msg->size);
|
||||
ret = radeon_process_aux_ch(chan,
|
||||
tx_buf, tx_size, NULL, 0, delay, &ack);
|
||||
@ -176,8 +183,14 @@ radeon_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
|
||||
break;
|
||||
case DP_AUX_NATIVE_READ:
|
||||
case DP_AUX_I2C_READ:
|
||||
/* tx_size needs to be 4 even for bare address packets since the atom
|
||||
* table needs the info in tx_buf[3].
|
||||
*/
|
||||
tx_size = HEADER_SIZE;
|
||||
tx_buf[3] |= tx_size << 4;
|
||||
if (msg->size == 0)
|
||||
tx_buf[3] |= BARE_ADDRESS_SIZE << 4;
|
||||
else
|
||||
tx_buf[3] |= tx_size << 4;
|
||||
ret = radeon_process_aux_ch(chan,
|
||||
tx_buf, tx_size, msg->buffer, msg->size, delay, &ack);
|
||||
break;
|
||||
@ -186,7 +199,7 @@ radeon_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret > 0)
|
||||
if (ret >= 0)
|
||||
msg->reply = ack >> 4;
|
||||
|
||||
return ret;
|
||||
@ -194,98 +207,15 @@ radeon_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
|
||||
|
||||
void radeon_dp_aux_init(struct radeon_connector *radeon_connector)
|
||||
{
|
||||
struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
|
||||
|
||||
dig_connector->dp_i2c_bus->aux.dev = radeon_connector->base.kdev;
|
||||
dig_connector->dp_i2c_bus->aux.transfer = radeon_dp_aux_transfer;
|
||||
}
|
||||
|
||||
int radeon_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
|
||||
u8 write_byte, u8 *read_byte)
|
||||
{
|
||||
struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
|
||||
struct radeon_i2c_chan *auxch = i2c_get_adapdata(adapter);
|
||||
u16 address = algo_data->address;
|
||||
u8 msg[5];
|
||||
u8 reply[2];
|
||||
unsigned retry;
|
||||
int msg_bytes;
|
||||
int reply_bytes = 1;
|
||||
int ret;
|
||||
u8 ack;
|
||||
|
||||
/* Set up the address */
|
||||
msg[0] = address;
|
||||
msg[1] = address >> 8;
|
||||
radeon_connector->ddc_bus->aux.dev = radeon_connector->base.kdev;
|
||||
radeon_connector->ddc_bus->aux.transfer = radeon_dp_aux_transfer;
|
||||
ret = drm_dp_aux_register_i2c_bus(&radeon_connector->ddc_bus->aux);
|
||||
if (!ret)
|
||||
radeon_connector->ddc_bus->has_aux = true;
|
||||
|
||||
/* Set up the command byte */
|
||||
if (mode & MODE_I2C_READ) {
|
||||
msg[2] = DP_AUX_I2C_READ << 4;
|
||||
msg_bytes = 4;
|
||||
msg[3] = msg_bytes << 4;
|
||||
} else {
|
||||
msg[2] = DP_AUX_I2C_WRITE << 4;
|
||||
msg_bytes = 5;
|
||||
msg[3] = msg_bytes << 4;
|
||||
msg[4] = write_byte;
|
||||
}
|
||||
|
||||
/* special handling for start/stop */
|
||||
if (mode & (MODE_I2C_START | MODE_I2C_STOP))
|
||||
msg[3] = 3 << 4;
|
||||
|
||||
/* Set MOT bit for all but stop */
|
||||
if ((mode & MODE_I2C_STOP) == 0)
|
||||
msg[2] |= DP_AUX_I2C_MOT << 4;
|
||||
|
||||
for (retry = 0; retry < 7; retry++) {
|
||||
ret = radeon_process_aux_ch(auxch,
|
||||
msg, msg_bytes, reply, reply_bytes, 0, &ack);
|
||||
if (ret == -EBUSY)
|
||||
continue;
|
||||
else if (ret < 0) {
|
||||
DRM_DEBUG_KMS("aux_ch failed %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch ((ack >> 4) & DP_AUX_NATIVE_REPLY_MASK) {
|
||||
case DP_AUX_NATIVE_REPLY_ACK:
|
||||
/* I2C-over-AUX Reply field is only valid
|
||||
* when paired with AUX ACK.
|
||||
*/
|
||||
break;
|
||||
case DP_AUX_NATIVE_REPLY_NACK:
|
||||
DRM_DEBUG_KMS("aux_ch native nack\n");
|
||||
return -EREMOTEIO;
|
||||
case DP_AUX_NATIVE_REPLY_DEFER:
|
||||
DRM_DEBUG_KMS("aux_ch native defer\n");
|
||||
usleep_range(500, 600);
|
||||
continue;
|
||||
default:
|
||||
DRM_ERROR("aux_ch invalid native reply 0x%02x\n", ack);
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
|
||||
switch ((ack >> 4) & DP_AUX_I2C_REPLY_MASK) {
|
||||
case DP_AUX_I2C_REPLY_ACK:
|
||||
if (mode == MODE_I2C_READ)
|
||||
*read_byte = reply[0];
|
||||
return ret;
|
||||
case DP_AUX_I2C_REPLY_NACK:
|
||||
DRM_DEBUG_KMS("aux_i2c nack\n");
|
||||
return -EREMOTEIO;
|
||||
case DP_AUX_I2C_REPLY_DEFER:
|
||||
DRM_DEBUG_KMS("aux_i2c defer\n");
|
||||
usleep_range(400, 500);
|
||||
break;
|
||||
default:
|
||||
DRM_ERROR("aux_i2c invalid reply 0x%02x\n", ack);
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
}
|
||||
|
||||
DRM_DEBUG_KMS("aux i2c too many retries, giving up\n");
|
||||
return -EREMOTEIO;
|
||||
WARN(ret, "drm_dp_aux_register_i2c_bus() failed with error %d\n", ret);
|
||||
}
|
||||
|
||||
/***** general DP utility functions *****/
|
||||
@ -420,12 +350,11 @@ static u8 radeon_dp_encoder_service(struct radeon_device *rdev,
|
||||
|
||||
u8 radeon_dp_getsinktype(struct radeon_connector *radeon_connector)
|
||||
{
|
||||
struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
|
||||
struct drm_device *dev = radeon_connector->base.dev;
|
||||
struct radeon_device *rdev = dev->dev_private;
|
||||
|
||||
return radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_GET_SINK_TYPE, 0,
|
||||
dig_connector->dp_i2c_bus->rec.i2c_id, 0);
|
||||
radeon_connector->ddc_bus->rec.i2c_id, 0);
|
||||
}
|
||||
|
||||
static void radeon_dp_probe_oui(struct radeon_connector *radeon_connector)
|
||||
@ -436,11 +365,11 @@ static void radeon_dp_probe_oui(struct radeon_connector *radeon_connector)
|
||||
if (!(dig_connector->dpcd[DP_DOWN_STREAM_PORT_COUNT] & DP_OUI_SUPPORT))
|
||||
return;
|
||||
|
||||
if (drm_dp_dpcd_read(&dig_connector->dp_i2c_bus->aux, DP_SINK_OUI, buf, 3))
|
||||
if (drm_dp_dpcd_read(&radeon_connector->ddc_bus->aux, DP_SINK_OUI, buf, 3))
|
||||
DRM_DEBUG_KMS("Sink OUI: %02hx%02hx%02hx\n",
|
||||
buf[0], buf[1], buf[2]);
|
||||
|
||||
if (drm_dp_dpcd_read(&dig_connector->dp_i2c_bus->aux, DP_BRANCH_OUI, buf, 3))
|
||||
if (drm_dp_dpcd_read(&radeon_connector->ddc_bus->aux, DP_BRANCH_OUI, buf, 3))
|
||||
DRM_DEBUG_KMS("Branch OUI: %02hx%02hx%02hx\n",
|
||||
buf[0], buf[1], buf[2]);
|
||||
}
|
||||
@ -451,7 +380,7 @@ bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector)
|
||||
u8 msg[DP_DPCD_SIZE];
|
||||
int ret, i;
|
||||
|
||||
ret = drm_dp_dpcd_read(&dig_connector->dp_i2c_bus->aux, DP_DPCD_REV, msg,
|
||||
ret = drm_dp_dpcd_read(&radeon_connector->ddc_bus->aux, DP_DPCD_REV, msg,
|
||||
DP_DPCD_SIZE);
|
||||
if (ret > 0) {
|
||||
memcpy(dig_connector->dpcd, msg, DP_DPCD_SIZE);
|
||||
@ -489,7 +418,7 @@ int radeon_dp_get_panel_mode(struct drm_encoder *encoder,
|
||||
|
||||
if (dp_bridge != ENCODER_OBJECT_ID_NONE) {
|
||||
/* DP bridge chips */
|
||||
drm_dp_dpcd_readb(&dig_connector->dp_i2c_bus->aux,
|
||||
drm_dp_dpcd_readb(&radeon_connector->ddc_bus->aux,
|
||||
DP_EDP_CONFIGURATION_CAP, &tmp);
|
||||
if (tmp & 1)
|
||||
panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE;
|
||||
@ -500,7 +429,7 @@ int radeon_dp_get_panel_mode(struct drm_encoder *encoder,
|
||||
panel_mode = DP_PANEL_MODE_EXTERNAL_DP_MODE;
|
||||
} else if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) {
|
||||
/* eDP */
|
||||
drm_dp_dpcd_readb(&dig_connector->dp_i2c_bus->aux,
|
||||
drm_dp_dpcd_readb(&radeon_connector->ddc_bus->aux,
|
||||
DP_EDP_CONFIGURATION_CAP, &tmp);
|
||||
if (tmp & 1)
|
||||
panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE;
|
||||
@ -554,7 +483,8 @@ bool radeon_dp_needs_link_train(struct radeon_connector *radeon_connector)
|
||||
u8 link_status[DP_LINK_STATUS_SIZE];
|
||||
struct radeon_connector_atom_dig *dig = radeon_connector->con_priv;
|
||||
|
||||
if (drm_dp_dpcd_read_link_status(&dig->dp_i2c_bus->aux, link_status) <= 0)
|
||||
if (drm_dp_dpcd_read_link_status(&radeon_connector->ddc_bus->aux, link_status)
|
||||
<= 0)
|
||||
return false;
|
||||
if (drm_dp_channel_eq_ok(link_status, dig->dp_lane_count))
|
||||
return false;
|
||||
@ -574,7 +504,7 @@ void radeon_dp_set_rx_power_state(struct drm_connector *connector,
|
||||
|
||||
/* power up/down the sink */
|
||||
if (dig_connector->dpcd[0] >= 0x11) {
|
||||
drm_dp_dpcd_writeb(&dig_connector->dp_i2c_bus->aux,
|
||||
drm_dp_dpcd_writeb(&radeon_connector->ddc_bus->aux,
|
||||
DP_SET_POWER, power_state);
|
||||
usleep_range(1000, 2000);
|
||||
}
|
||||
@ -878,7 +808,7 @@ void radeon_dp_link_train(struct drm_encoder *encoder,
|
||||
else
|
||||
dp_info.enc_id |= ATOM_DP_CONFIG_LINK_A;
|
||||
|
||||
drm_dp_dpcd_readb(&dig_connector->dp_i2c_bus->aux, DP_MAX_LANE_COUNT, &tmp);
|
||||
drm_dp_dpcd_readb(&radeon_connector->ddc_bus->aux, DP_MAX_LANE_COUNT, &tmp);
|
||||
if (ASIC_IS_DCE5(rdev) && (tmp & DP_TPS3_SUPPORTED))
|
||||
dp_info.tp3_supported = true;
|
||||
else
|
||||
@ -890,7 +820,7 @@ void radeon_dp_link_train(struct drm_encoder *encoder,
|
||||
dp_info.connector = connector;
|
||||
dp_info.dp_lane_count = dig_connector->dp_lane_count;
|
||||
dp_info.dp_clock = dig_connector->dp_clock;
|
||||
dp_info.aux = &dig_connector->dp_i2c_bus->aux;
|
||||
dp_info.aux = &radeon_connector->ddc_bus->aux;
|
||||
|
||||
if (radeon_dp_link_train_init(&dp_info))
|
||||
goto done;
|
||||
|
@ -21,8 +21,10 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/firmware.h>
|
||||
#include "drmP.h"
|
||||
#include "radeon.h"
|
||||
#include "radeon_ucode.h"
|
||||
#include "cikd.h"
|
||||
#include "r600_dpm.h"
|
||||
#include "ci_dpm.h"
|
||||
@ -202,24 +204,29 @@ static void ci_initialize_powertune_defaults(struct radeon_device *rdev)
|
||||
struct ci_power_info *pi = ci_get_pi(rdev);
|
||||
|
||||
switch (rdev->pdev->device) {
|
||||
case 0x6649:
|
||||
case 0x6650:
|
||||
case 0x6651:
|
||||
case 0x6658:
|
||||
case 0x665C:
|
||||
case 0x665D:
|
||||
default:
|
||||
pi->powertune_defaults = &defaults_bonaire_xt;
|
||||
break;
|
||||
case 0x6651:
|
||||
case 0x665D:
|
||||
pi->powertune_defaults = &defaults_bonaire_pro;
|
||||
break;
|
||||
case 0x6640:
|
||||
pi->powertune_defaults = &defaults_saturn_xt;
|
||||
break;
|
||||
case 0x6641:
|
||||
pi->powertune_defaults = &defaults_saturn_pro;
|
||||
case 0x6646:
|
||||
case 0x6647:
|
||||
pi->powertune_defaults = &defaults_saturn_xt;
|
||||
break;
|
||||
case 0x67B8:
|
||||
case 0x67B0:
|
||||
pi->powertune_defaults = &defaults_hawaii_xt;
|
||||
break;
|
||||
case 0x67BA:
|
||||
case 0x67B1:
|
||||
pi->powertune_defaults = &defaults_hawaii_pro;
|
||||
break;
|
||||
case 0x67A0:
|
||||
case 0x67A1:
|
||||
case 0x67A2:
|
||||
@ -228,11 +235,7 @@ static void ci_initialize_powertune_defaults(struct radeon_device *rdev)
|
||||
case 0x67AA:
|
||||
case 0x67B9:
|
||||
case 0x67BE:
|
||||
pi->powertune_defaults = &defaults_hawaii_xt;
|
||||
break;
|
||||
case 0x67BA:
|
||||
case 0x67B1:
|
||||
pi->powertune_defaults = &defaults_hawaii_pro;
|
||||
pi->powertune_defaults = &defaults_bonaire_xt;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -5146,6 +5149,12 @@ int ci_dpm_init(struct radeon_device *rdev)
|
||||
pi->mclk_dpm_key_disabled = 0;
|
||||
pi->pcie_dpm_key_disabled = 0;
|
||||
|
||||
/* mclk dpm is unstable on some R7 260X cards with the old mc ucode */
|
||||
if ((rdev->pdev->device == 0x6658) &&
|
||||
(rdev->mc_fw->size == (BONAIRE_MC_UCODE_SIZE * 4))) {
|
||||
pi->mclk_dpm_key_disabled = 1;
|
||||
}
|
||||
|
||||
pi->caps_sclk_ds = true;
|
||||
|
||||
pi->mclk_strobe_mode_threshold = 40000;
|
||||
|
@ -38,6 +38,7 @@ MODULE_FIRMWARE("radeon/BONAIRE_me.bin");
|
||||
MODULE_FIRMWARE("radeon/BONAIRE_ce.bin");
|
||||
MODULE_FIRMWARE("radeon/BONAIRE_mec.bin");
|
||||
MODULE_FIRMWARE("radeon/BONAIRE_mc.bin");
|
||||
MODULE_FIRMWARE("radeon/BONAIRE_mc2.bin");
|
||||
MODULE_FIRMWARE("radeon/BONAIRE_rlc.bin");
|
||||
MODULE_FIRMWARE("radeon/BONAIRE_sdma.bin");
|
||||
MODULE_FIRMWARE("radeon/BONAIRE_smc.bin");
|
||||
@ -46,6 +47,7 @@ MODULE_FIRMWARE("radeon/HAWAII_me.bin");
|
||||
MODULE_FIRMWARE("radeon/HAWAII_ce.bin");
|
||||
MODULE_FIRMWARE("radeon/HAWAII_mec.bin");
|
||||
MODULE_FIRMWARE("radeon/HAWAII_mc.bin");
|
||||
MODULE_FIRMWARE("radeon/HAWAII_mc2.bin");
|
||||
MODULE_FIRMWARE("radeon/HAWAII_rlc.bin");
|
||||
MODULE_FIRMWARE("radeon/HAWAII_sdma.bin");
|
||||
MODULE_FIRMWARE("radeon/HAWAII_smc.bin");
|
||||
@ -1703,20 +1705,20 @@ int ci_mc_load_microcode(struct radeon_device *rdev)
|
||||
const __be32 *fw_data;
|
||||
u32 running, blackout = 0;
|
||||
u32 *io_mc_regs;
|
||||
int i, ucode_size, regs_size;
|
||||
int i, regs_size, ucode_size;
|
||||
|
||||
if (!rdev->mc_fw)
|
||||
return -EINVAL;
|
||||
|
||||
ucode_size = rdev->mc_fw->size / 4;
|
||||
|
||||
switch (rdev->family) {
|
||||
case CHIP_BONAIRE:
|
||||
io_mc_regs = (u32 *)&bonaire_io_mc_regs;
|
||||
ucode_size = CIK_MC_UCODE_SIZE;
|
||||
regs_size = BONAIRE_IO_MC_REGS_SIZE;
|
||||
break;
|
||||
case CHIP_HAWAII:
|
||||
io_mc_regs = (u32 *)&hawaii_io_mc_regs;
|
||||
ucode_size = HAWAII_MC_UCODE_SIZE;
|
||||
regs_size = HAWAII_IO_MC_REGS_SIZE;
|
||||
break;
|
||||
default:
|
||||
@ -1783,7 +1785,7 @@ static int cik_init_microcode(struct radeon_device *rdev)
|
||||
const char *chip_name;
|
||||
size_t pfp_req_size, me_req_size, ce_req_size,
|
||||
mec_req_size, rlc_req_size, mc_req_size = 0,
|
||||
sdma_req_size, smc_req_size = 0;
|
||||
sdma_req_size, smc_req_size = 0, mc2_req_size = 0;
|
||||
char fw_name[30];
|
||||
int err;
|
||||
|
||||
@ -1797,7 +1799,8 @@ static int cik_init_microcode(struct radeon_device *rdev)
|
||||
ce_req_size = CIK_CE_UCODE_SIZE * 4;
|
||||
mec_req_size = CIK_MEC_UCODE_SIZE * 4;
|
||||
rlc_req_size = BONAIRE_RLC_UCODE_SIZE * 4;
|
||||
mc_req_size = CIK_MC_UCODE_SIZE * 4;
|
||||
mc_req_size = BONAIRE_MC_UCODE_SIZE * 4;
|
||||
mc2_req_size = BONAIRE_MC2_UCODE_SIZE * 4;
|
||||
sdma_req_size = CIK_SDMA_UCODE_SIZE * 4;
|
||||
smc_req_size = ALIGN(BONAIRE_SMC_UCODE_SIZE, 4);
|
||||
break;
|
||||
@ -1809,6 +1812,7 @@ static int cik_init_microcode(struct radeon_device *rdev)
|
||||
mec_req_size = CIK_MEC_UCODE_SIZE * 4;
|
||||
rlc_req_size = BONAIRE_RLC_UCODE_SIZE * 4;
|
||||
mc_req_size = HAWAII_MC_UCODE_SIZE * 4;
|
||||
mc2_req_size = HAWAII_MC2_UCODE_SIZE * 4;
|
||||
sdma_req_size = CIK_SDMA_UCODE_SIZE * 4;
|
||||
smc_req_size = ALIGN(HAWAII_SMC_UCODE_SIZE, 4);
|
||||
break;
|
||||
@ -1904,16 +1908,22 @@ static int cik_init_microcode(struct radeon_device *rdev)
|
||||
|
||||
/* No SMC, MC ucode on APUs */
|
||||
if (!(rdev->flags & RADEON_IS_IGP)) {
|
||||
snprintf(fw_name, sizeof(fw_name), "radeon/%s_mc.bin", chip_name);
|
||||
snprintf(fw_name, sizeof(fw_name), "radeon/%s_mc2.bin", chip_name);
|
||||
err = request_firmware(&rdev->mc_fw, fw_name, rdev->dev);
|
||||
if (err)
|
||||
goto out;
|
||||
if (rdev->mc_fw->size != mc_req_size) {
|
||||
if (err) {
|
||||
snprintf(fw_name, sizeof(fw_name), "radeon/%s_mc.bin", chip_name);
|
||||
err = request_firmware(&rdev->mc_fw, fw_name, rdev->dev);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
if ((rdev->mc_fw->size != mc_req_size) &&
|
||||
(rdev->mc_fw->size != mc2_req_size)){
|
||||
printk(KERN_ERR
|
||||
"cik_mc: Bogus length %zu in firmware \"%s\"\n",
|
||||
rdev->mc_fw->size, fw_name);
|
||||
err = -EINVAL;
|
||||
}
|
||||
DRM_INFO("%s: %zu bytes\n", fw_name, rdev->mc_fw->size);
|
||||
|
||||
snprintf(fw_name, sizeof(fw_name), "radeon/%s_smc.bin", chip_name);
|
||||
err = request_firmware(&rdev->smc_fw, fw_name, rdev->dev);
|
||||
|
@ -309,11 +309,17 @@ int dce6_audio_init(struct radeon_device *rdev)
|
||||
|
||||
rdev->audio.enabled = true;
|
||||
|
||||
if (ASIC_IS_DCE8(rdev))
|
||||
if (ASIC_IS_DCE81(rdev)) /* KV: 4 streams, 7 endpoints */
|
||||
rdev->audio.num_pins = 7;
|
||||
else if (ASIC_IS_DCE83(rdev)) /* KB: 2 streams, 3 endpoints */
|
||||
rdev->audio.num_pins = 3;
|
||||
else if (ASIC_IS_DCE8(rdev)) /* BN/HW: 6 streams, 7 endpoints */
|
||||
rdev->audio.num_pins = 7;
|
||||
else if (ASIC_IS_DCE61(rdev)) /* TN: 4 streams, 6 endpoints */
|
||||
rdev->audio.num_pins = 6;
|
||||
else if (ASIC_IS_DCE61(rdev))
|
||||
rdev->audio.num_pins = 4;
|
||||
else
|
||||
else if (ASIC_IS_DCE64(rdev)) /* OL: 2 streams, 2 endpoints */
|
||||
rdev->audio.num_pins = 2;
|
||||
else /* SI: 6 streams, 6 endpoints */
|
||||
rdev->audio.num_pins = 6;
|
||||
|
||||
for (i = 0; i < rdev->audio.num_pins; i++) {
|
||||
|
@ -739,7 +739,7 @@ union radeon_irq_stat_regs {
|
||||
struct cik_irq_stat_regs cik;
|
||||
};
|
||||
|
||||
#define RADEON_MAX_HPD_PINS 6
|
||||
#define RADEON_MAX_HPD_PINS 7
|
||||
#define RADEON_MAX_CRTCS 6
|
||||
#define RADEON_MAX_AFMT_BLOCKS 7
|
||||
|
||||
@ -2321,6 +2321,7 @@ struct radeon_device {
|
||||
bool have_disp_power_ref;
|
||||
};
|
||||
|
||||
bool radeon_is_px(struct drm_device *dev);
|
||||
int radeon_device_init(struct radeon_device *rdev,
|
||||
struct drm_device *ddev,
|
||||
struct pci_dev *pdev,
|
||||
@ -2631,6 +2632,9 @@ void r100_pll_errata_after_index(struct radeon_device *rdev);
|
||||
#define ASIC_IS_DCE64(rdev) ((rdev->family == CHIP_OLAND))
|
||||
#define ASIC_IS_NODCE(rdev) ((rdev->family == CHIP_HAINAN))
|
||||
#define ASIC_IS_DCE8(rdev) ((rdev->family >= CHIP_BONAIRE))
|
||||
#define ASIC_IS_DCE81(rdev) ((rdev->family == CHIP_KAVERI))
|
||||
#define ASIC_IS_DCE82(rdev) ((rdev->family == CHIP_BONAIRE))
|
||||
#define ASIC_IS_DCE83(rdev) ((rdev->family == CHIP_KABINI))
|
||||
|
||||
#define ASIC_IS_LOMBOK(rdev) ((rdev->ddev->pdev->device == 0x6849) || \
|
||||
(rdev->ddev->pdev->device == 0x6850) || \
|
||||
|
@ -59,7 +59,7 @@ struct atpx_mux {
|
||||
u16 mux;
|
||||
} __packed;
|
||||
|
||||
bool radeon_is_px(void) {
|
||||
bool radeon_has_atpx(void) {
|
||||
return radeon_atpx_priv.atpx_detected;
|
||||
}
|
||||
|
||||
|
@ -1261,21 +1261,6 @@ static const struct drm_connector_funcs radeon_dvi_connector_funcs = {
|
||||
.force = radeon_dvi_force,
|
||||
};
|
||||
|
||||
static void radeon_dp_connector_destroy(struct drm_connector *connector)
|
||||
{
|
||||
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
|
||||
struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv;
|
||||
|
||||
if (radeon_connector->edid)
|
||||
kfree(radeon_connector->edid);
|
||||
if (radeon_dig_connector->dp_i2c_bus)
|
||||
radeon_i2c_destroy(radeon_dig_connector->dp_i2c_bus);
|
||||
kfree(radeon_connector->con_priv);
|
||||
drm_sysfs_connector_remove(connector);
|
||||
drm_connector_cleanup(connector);
|
||||
kfree(connector);
|
||||
}
|
||||
|
||||
static int radeon_dp_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
|
||||
@ -1553,7 +1538,7 @@ static const struct drm_connector_funcs radeon_dp_connector_funcs = {
|
||||
.detect = radeon_dp_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.set_property = radeon_connector_set_property,
|
||||
.destroy = radeon_dp_connector_destroy,
|
||||
.destroy = radeon_connector_destroy,
|
||||
.force = radeon_dvi_force,
|
||||
};
|
||||
|
||||
@ -1562,7 +1547,7 @@ static const struct drm_connector_funcs radeon_edp_connector_funcs = {
|
||||
.detect = radeon_dp_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.set_property = radeon_lvds_set_property,
|
||||
.destroy = radeon_dp_connector_destroy,
|
||||
.destroy = radeon_connector_destroy,
|
||||
.force = radeon_dvi_force,
|
||||
};
|
||||
|
||||
@ -1571,7 +1556,7 @@ static const struct drm_connector_funcs radeon_lvds_bridge_connector_funcs = {
|
||||
.detect = radeon_dp_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.set_property = radeon_lvds_set_property,
|
||||
.destroy = radeon_dp_connector_destroy,
|
||||
.destroy = radeon_connector_destroy,
|
||||
.force = radeon_dvi_force,
|
||||
};
|
||||
|
||||
@ -1668,17 +1653,10 @@ radeon_add_atom_connector(struct drm_device *dev,
|
||||
radeon_dig_connector->igp_lane_info = igp_lane_info;
|
||||
radeon_connector->con_priv = radeon_dig_connector;
|
||||
if (i2c_bus->valid) {
|
||||
/* add DP i2c bus */
|
||||
if (connector_type == DRM_MODE_CONNECTOR_eDP)
|
||||
radeon_dig_connector->dp_i2c_bus = radeon_i2c_create_dp(dev, i2c_bus, "eDP-auxch");
|
||||
else
|
||||
radeon_dig_connector->dp_i2c_bus = radeon_i2c_create_dp(dev, i2c_bus, "DP-auxch");
|
||||
if (radeon_dig_connector->dp_i2c_bus)
|
||||
radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
|
||||
if (radeon_connector->ddc_bus)
|
||||
has_aux = true;
|
||||
else
|
||||
DRM_ERROR("DP: Failed to assign dp ddc bus! Check dmesg for i2c errors.\n");
|
||||
radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
|
||||
if (!radeon_connector->ddc_bus)
|
||||
DRM_ERROR("DP: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
|
||||
}
|
||||
switch (connector_type) {
|
||||
@ -1893,10 +1871,6 @@ radeon_add_atom_connector(struct drm_device *dev,
|
||||
drm_connector_init(dev, &radeon_connector->base, &radeon_dp_connector_funcs, connector_type);
|
||||
drm_connector_helper_add(&radeon_connector->base, &radeon_dp_connector_helper_funcs);
|
||||
if (i2c_bus->valid) {
|
||||
/* add DP i2c bus */
|
||||
radeon_dig_connector->dp_i2c_bus = radeon_i2c_create_dp(dev, i2c_bus, "DP-auxch");
|
||||
if (!radeon_dig_connector->dp_i2c_bus)
|
||||
DRM_ERROR("DP: Failed to assign dp ddc bus! Check dmesg for i2c errors.\n");
|
||||
radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
|
||||
if (radeon_connector->ddc_bus)
|
||||
has_aux = true;
|
||||
@ -1942,14 +1916,10 @@ radeon_add_atom_connector(struct drm_device *dev,
|
||||
drm_connector_init(dev, &radeon_connector->base, &radeon_edp_connector_funcs, connector_type);
|
||||
drm_connector_helper_add(&radeon_connector->base, &radeon_dp_connector_helper_funcs);
|
||||
if (i2c_bus->valid) {
|
||||
/* add DP i2c bus */
|
||||
radeon_dig_connector->dp_i2c_bus = radeon_i2c_create_dp(dev, i2c_bus, "eDP-auxch");
|
||||
if (radeon_dig_connector->dp_i2c_bus)
|
||||
radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
|
||||
if (radeon_connector->ddc_bus)
|
||||
has_aux = true;
|
||||
else
|
||||
DRM_ERROR("DP: Failed to assign dp ddc bus! Check dmesg for i2c errors.\n");
|
||||
radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus);
|
||||
if (!radeon_connector->ddc_bus)
|
||||
DRM_ERROR("DP: Failed to assign ddc bus! Check dmesg for i2c errors.\n");
|
||||
}
|
||||
drm_object_attach_property(&radeon_connector->base.base,
|
||||
|
@ -102,11 +102,14 @@ static const char radeon_family_name[][16] = {
|
||||
"LAST",
|
||||
};
|
||||
|
||||
#if defined(CONFIG_VGA_SWITCHEROO)
|
||||
bool radeon_is_px(void);
|
||||
#else
|
||||
static inline bool radeon_is_px(void) { return false; }
|
||||
#endif
|
||||
bool radeon_is_px(struct drm_device *dev)
|
||||
{
|
||||
struct radeon_device *rdev = dev->dev_private;
|
||||
|
||||
if (rdev->flags & RADEON_IS_PX)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* radeon_program_register_sequence - program an array of registers.
|
||||
@ -1082,7 +1085,7 @@ static void radeon_switcheroo_set_state(struct pci_dev *pdev, enum vga_switchero
|
||||
{
|
||||
struct drm_device *dev = pci_get_drvdata(pdev);
|
||||
|
||||
if (radeon_is_px() && state == VGA_SWITCHEROO_OFF)
|
||||
if (radeon_is_px(dev) && state == VGA_SWITCHEROO_OFF)
|
||||
return;
|
||||
|
||||
if (state == VGA_SWITCHEROO_ON) {
|
||||
@ -1301,9 +1304,7 @@ int radeon_device_init(struct radeon_device *rdev,
|
||||
* ignore it */
|
||||
vga_client_register(rdev->pdev, rdev, NULL, radeon_vga_set_decode);
|
||||
|
||||
if (radeon_runtime_pm == 1)
|
||||
runtime = true;
|
||||
if ((radeon_runtime_pm == -1) && radeon_is_px())
|
||||
if (rdev->flags & RADEON_IS_PX)
|
||||
runtime = true;
|
||||
vga_switcheroo_register_client(rdev->pdev, &radeon_switcheroo_ops, runtime);
|
||||
if (runtime)
|
||||
|
@ -759,19 +759,18 @@ int radeon_ddc_get_modes(struct radeon_connector *radeon_connector)
|
||||
|
||||
if (radeon_connector_encoder_get_dp_bridge_encoder_id(&radeon_connector->base) !=
|
||||
ENCODER_OBJECT_ID_NONE) {
|
||||
struct radeon_connector_atom_dig *dig = radeon_connector->con_priv;
|
||||
|
||||
if (dig->dp_i2c_bus)
|
||||
if (radeon_connector->ddc_bus->has_aux)
|
||||
radeon_connector->edid = drm_get_edid(&radeon_connector->base,
|
||||
&dig->dp_i2c_bus->adapter);
|
||||
&radeon_connector->ddc_bus->aux.ddc);
|
||||
} else if ((radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_DisplayPort) ||
|
||||
(radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_eDP)) {
|
||||
struct radeon_connector_atom_dig *dig = radeon_connector->con_priv;
|
||||
|
||||
if ((dig->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT ||
|
||||
dig->dp_sink_type == CONNECTOR_OBJECT_ID_eDP) && dig->dp_i2c_bus)
|
||||
dig->dp_sink_type == CONNECTOR_OBJECT_ID_eDP) &&
|
||||
radeon_connector->ddc_bus->has_aux)
|
||||
radeon_connector->edid = drm_get_edid(&radeon_connector->base,
|
||||
&dig->dp_i2c_bus->adapter);
|
||||
&radeon_connector->ddc_bus->aux.ddc);
|
||||
else if (radeon_connector->ddc_bus && !radeon_connector->edid)
|
||||
radeon_connector->edid = drm_get_edid(&radeon_connector->base,
|
||||
&radeon_connector->ddc_bus->adapter);
|
||||
@ -865,7 +864,7 @@ void radeon_compute_pll_avivo(struct radeon_pll *pll,
|
||||
unsigned post_div_min, post_div_max, post_div;
|
||||
unsigned ref_div_min, ref_div_max, ref_div;
|
||||
unsigned post_div_best, diff_best;
|
||||
unsigned nom, den, tmp;
|
||||
unsigned nom, den;
|
||||
|
||||
/* determine allowed feedback divider range */
|
||||
fb_div_min = pll->min_feedback_div;
|
||||
@ -937,23 +936,27 @@ void radeon_compute_pll_avivo(struct radeon_pll *pll,
|
||||
}
|
||||
post_div = post_div_best;
|
||||
|
||||
/* limit reference * post divider to a maximum */
|
||||
ref_div_max = min(210 / post_div, ref_div_max);
|
||||
|
||||
/* get matching reference and feedback divider */
|
||||
ref_div = max(den / post_div, 1u);
|
||||
fb_div = nom;
|
||||
ref_div = max(DIV_ROUND_CLOSEST(den, post_div), 1u);
|
||||
fb_div = DIV_ROUND_CLOSEST(nom * ref_div * post_div, den);
|
||||
|
||||
/* we're almost done, but reference and feedback
|
||||
divider might be to large now */
|
||||
|
||||
tmp = ref_div;
|
||||
nom = fb_div;
|
||||
den = ref_div;
|
||||
|
||||
if (fb_div > fb_div_max) {
|
||||
ref_div = ref_div * fb_div_max / fb_div;
|
||||
ref_div = DIV_ROUND_CLOSEST(den * fb_div_max, nom);
|
||||
fb_div = fb_div_max;
|
||||
}
|
||||
|
||||
if (ref_div > ref_div_max) {
|
||||
ref_div = ref_div_max;
|
||||
fb_div = nom * ref_div_max / tmp;
|
||||
fb_div = DIV_ROUND_CLOSEST(nom * ref_div_max, den);
|
||||
}
|
||||
|
||||
/* reduce the numbers to a simpler ratio once more */
|
||||
|
@ -115,6 +115,7 @@ extern int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc,
|
||||
unsigned int flags,
|
||||
int *vpos, int *hpos, ktime_t *stime,
|
||||
ktime_t *etime);
|
||||
extern bool radeon_is_px(struct drm_device *dev);
|
||||
extern const struct drm_ioctl_desc radeon_ioctls_kms[];
|
||||
extern int radeon_max_kms_ioctl;
|
||||
int radeon_mmap(struct file *filp, struct vm_area_struct *vma);
|
||||
@ -144,11 +145,9 @@ void radeon_debugfs_cleanup(struct drm_minor *minor);
|
||||
#if defined(CONFIG_VGA_SWITCHEROO)
|
||||
void radeon_register_atpx_handler(void);
|
||||
void radeon_unregister_atpx_handler(void);
|
||||
bool radeon_is_px(void);
|
||||
#else
|
||||
static inline void radeon_register_atpx_handler(void) {}
|
||||
static inline void radeon_unregister_atpx_handler(void) {}
|
||||
static inline bool radeon_is_px(void) { return false; }
|
||||
#endif
|
||||
|
||||
int radeon_no_wb;
|
||||
@ -186,7 +185,7 @@ module_param_named(dynclks, radeon_dynclks, int, 0444);
|
||||
MODULE_PARM_DESC(r4xx_atom, "Enable ATOMBIOS modesetting for R4xx");
|
||||
module_param_named(r4xx_atom, radeon_r4xx_atom, int, 0444);
|
||||
|
||||
MODULE_PARM_DESC(vramlimit, "Restrict VRAM for testing");
|
||||
MODULE_PARM_DESC(vramlimit, "Restrict VRAM for testing, in megabytes");
|
||||
module_param_named(vramlimit, radeon_vram_limit, int, 0600);
|
||||
|
||||
MODULE_PARM_DESC(agpmode, "AGP Mode (-1 == PCI)");
|
||||
@ -405,12 +404,7 @@ static int radeon_pmops_runtime_suspend(struct device *dev)
|
||||
struct drm_device *drm_dev = pci_get_drvdata(pdev);
|
||||
int ret;
|
||||
|
||||
if (radeon_runtime_pm == 0) {
|
||||
pm_runtime_forbid(dev);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (radeon_runtime_pm == -1 && !radeon_is_px()) {
|
||||
if (!radeon_is_px(drm_dev)) {
|
||||
pm_runtime_forbid(dev);
|
||||
return -EBUSY;
|
||||
}
|
||||
@ -434,10 +428,7 @@ static int radeon_pmops_runtime_resume(struct device *dev)
|
||||
struct drm_device *drm_dev = pci_get_drvdata(pdev);
|
||||
int ret;
|
||||
|
||||
if (radeon_runtime_pm == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (radeon_runtime_pm == -1 && !radeon_is_px())
|
||||
if (!radeon_is_px(drm_dev))
|
||||
return -EINVAL;
|
||||
|
||||
drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
|
||||
@ -462,14 +453,7 @@ static int radeon_pmops_runtime_idle(struct device *dev)
|
||||
struct drm_device *drm_dev = pci_get_drvdata(pdev);
|
||||
struct drm_crtc *crtc;
|
||||
|
||||
if (radeon_runtime_pm == 0) {
|
||||
pm_runtime_forbid(dev);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* are we PX enabled? */
|
||||
if (radeon_runtime_pm == -1 && !radeon_is_px()) {
|
||||
DRM_DEBUG_DRIVER("failing to power off - not px\n");
|
||||
if (!radeon_is_px(drm_dev)) {
|
||||
pm_runtime_forbid(dev);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
@ -115,6 +115,7 @@ enum radeon_chip_flags {
|
||||
RADEON_NEW_MEMMAP = 0x00400000UL,
|
||||
RADEON_IS_PCI = 0x00800000UL,
|
||||
RADEON_IS_IGPGART = 0x01000000UL,
|
||||
RADEON_IS_PX = 0x02000000UL,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -64,8 +64,7 @@ bool radeon_ddc_probe(struct radeon_connector *radeon_connector, bool use_aux)
|
||||
radeon_router_select_ddc_port(radeon_connector);
|
||||
|
||||
if (use_aux) {
|
||||
struct radeon_connector_atom_dig *dig = radeon_connector->con_priv;
|
||||
ret = i2c_transfer(&dig->dp_i2c_bus->adapter, msgs, 2);
|
||||
ret = i2c_transfer(&radeon_connector->ddc_bus->aux.ddc, msgs, 2);
|
||||
} else {
|
||||
ret = i2c_transfer(&radeon_connector->ddc_bus->adapter, msgs, 2);
|
||||
}
|
||||
@ -950,16 +949,16 @@ struct radeon_i2c_chan *radeon_i2c_create(struct drm_device *dev,
|
||||
/* set the radeon bit adapter */
|
||||
snprintf(i2c->adapter.name, sizeof(i2c->adapter.name),
|
||||
"Radeon i2c bit bus %s", name);
|
||||
i2c->adapter.algo_data = &i2c->algo.bit;
|
||||
i2c->algo.bit.pre_xfer = pre_xfer;
|
||||
i2c->algo.bit.post_xfer = post_xfer;
|
||||
i2c->algo.bit.setsda = set_data;
|
||||
i2c->algo.bit.setscl = set_clock;
|
||||
i2c->algo.bit.getsda = get_data;
|
||||
i2c->algo.bit.getscl = get_clock;
|
||||
i2c->algo.bit.udelay = 10;
|
||||
i2c->algo.bit.timeout = usecs_to_jiffies(2200); /* from VESA */
|
||||
i2c->algo.bit.data = i2c;
|
||||
i2c->adapter.algo_data = &i2c->bit;
|
||||
i2c->bit.pre_xfer = pre_xfer;
|
||||
i2c->bit.post_xfer = post_xfer;
|
||||
i2c->bit.setsda = set_data;
|
||||
i2c->bit.setscl = set_clock;
|
||||
i2c->bit.getsda = get_data;
|
||||
i2c->bit.getscl = get_clock;
|
||||
i2c->bit.udelay = 10;
|
||||
i2c->bit.timeout = usecs_to_jiffies(2200); /* from VESA */
|
||||
i2c->bit.data = i2c;
|
||||
ret = i2c_bit_add_bus(&i2c->adapter);
|
||||
if (ret) {
|
||||
DRM_ERROR("Failed to register bit i2c %s\n", name);
|
||||
@ -974,46 +973,13 @@ out_free:
|
||||
|
||||
}
|
||||
|
||||
struct radeon_i2c_chan *radeon_i2c_create_dp(struct drm_device *dev,
|
||||
struct radeon_i2c_bus_rec *rec,
|
||||
const char *name)
|
||||
{
|
||||
struct radeon_i2c_chan *i2c;
|
||||
int ret;
|
||||
|
||||
i2c = kzalloc(sizeof(struct radeon_i2c_chan), GFP_KERNEL);
|
||||
if (i2c == NULL)
|
||||
return NULL;
|
||||
|
||||
i2c->rec = *rec;
|
||||
i2c->adapter.owner = THIS_MODULE;
|
||||
i2c->adapter.class = I2C_CLASS_DDC;
|
||||
i2c->adapter.dev.parent = &dev->pdev->dev;
|
||||
i2c->dev = dev;
|
||||
snprintf(i2c->adapter.name, sizeof(i2c->adapter.name),
|
||||
"Radeon aux bus %s", name);
|
||||
i2c_set_adapdata(&i2c->adapter, i2c);
|
||||
i2c->adapter.algo_data = &i2c->algo.dp;
|
||||
i2c->algo.dp.aux_ch = radeon_dp_i2c_aux_ch;
|
||||
i2c->algo.dp.address = 0;
|
||||
ret = i2c_dp_aux_add_bus(&i2c->adapter);
|
||||
if (ret) {
|
||||
DRM_INFO("Failed to register i2c %s\n", name);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
return i2c;
|
||||
out_free:
|
||||
kfree(i2c);
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
void radeon_i2c_destroy(struct radeon_i2c_chan *i2c)
|
||||
{
|
||||
if (!i2c)
|
||||
return;
|
||||
i2c_del_adapter(&i2c->adapter);
|
||||
if (i2c->has_aux)
|
||||
drm_dp_aux_unregister_i2c_bus(&i2c->aux);
|
||||
kfree(i2c);
|
||||
}
|
||||
|
||||
|
@ -35,9 +35,9 @@
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#if defined(CONFIG_VGA_SWITCHEROO)
|
||||
bool radeon_is_px(void);
|
||||
bool radeon_has_atpx(void);
|
||||
#else
|
||||
static inline bool radeon_is_px(void) { return false; }
|
||||
static inline bool radeon_has_atpx(void) { return false; }
|
||||
#endif
|
||||
|
||||
/**
|
||||
@ -107,6 +107,13 @@ int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags)
|
||||
flags |= RADEON_IS_PCI;
|
||||
}
|
||||
|
||||
if (radeon_runtime_pm == 1)
|
||||
flags |= RADEON_IS_PX;
|
||||
else if ((radeon_runtime_pm == -1) &&
|
||||
radeon_has_atpx() &&
|
||||
((flags & RADEON_IS_IGP) == 0))
|
||||
flags |= RADEON_IS_PX;
|
||||
|
||||
/* radeon_device_init should report only fatal error
|
||||
* like memory allocation failure or iomapping failure,
|
||||
* or memory manager initialization failure, it must
|
||||
@ -137,8 +144,7 @@ int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags)
|
||||
"Error during ACPI methods call\n");
|
||||
}
|
||||
|
||||
if ((radeon_runtime_pm == 1) ||
|
||||
((radeon_runtime_pm == -1) && radeon_is_px())) {
|
||||
if (radeon_is_px(dev)) {
|
||||
pm_runtime_use_autosuspend(dev->dev);
|
||||
pm_runtime_set_autosuspend_delay(dev->dev, 5000);
|
||||
pm_runtime_set_active(dev->dev);
|
||||
@ -568,12 +574,17 @@ int radeon_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv)
|
||||
}
|
||||
|
||||
r = radeon_vm_init(rdev, &fpriv->vm);
|
||||
if (r)
|
||||
if (r) {
|
||||
kfree(fpriv);
|
||||
return r;
|
||||
}
|
||||
|
||||
r = radeon_bo_reserve(rdev->ring_tmp_bo.bo, false);
|
||||
if (r)
|
||||
if (r) {
|
||||
radeon_vm_fini(rdev, &fpriv->vm);
|
||||
kfree(fpriv);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* map the ib pool buffer read only into
|
||||
* virtual address space */
|
||||
|
@ -187,12 +187,10 @@ struct radeon_pll {
|
||||
struct radeon_i2c_chan {
|
||||
struct i2c_adapter adapter;
|
||||
struct drm_device *dev;
|
||||
union {
|
||||
struct i2c_algo_bit_data bit;
|
||||
struct i2c_algo_dp_aux_data dp;
|
||||
} algo;
|
||||
struct i2c_algo_bit_data bit;
|
||||
struct radeon_i2c_bus_rec rec;
|
||||
struct drm_dp_aux aux;
|
||||
bool has_aux;
|
||||
};
|
||||
|
||||
/* mostly for macs, but really any system without connector tables */
|
||||
@ -440,7 +438,6 @@ struct radeon_encoder {
|
||||
struct radeon_connector_atom_dig {
|
||||
uint32_t igp_lane_info;
|
||||
/* displayport */
|
||||
struct radeon_i2c_chan *dp_i2c_bus;
|
||||
u8 dpcd[DP_RECEIVER_CAP_SIZE];
|
||||
u8 dp_sink_type;
|
||||
int dp_clock;
|
||||
@ -702,8 +699,6 @@ extern void atombios_dig_transmitter_setup(struct drm_encoder *encoder,
|
||||
uint8_t lane_set);
|
||||
extern void radeon_atom_ext_encoder_setup_ddc(struct drm_encoder *encoder);
|
||||
extern struct drm_encoder *radeon_get_external_encoder(struct drm_encoder *encoder);
|
||||
extern int radeon_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
|
||||
u8 write_byte, u8 *read_byte);
|
||||
void radeon_atom_copy_swap(u8 *dst, u8 *src, u8 num_bytes, bool to_le);
|
||||
|
||||
extern void radeon_i2c_init(struct radeon_device *rdev);
|
||||
@ -715,9 +710,6 @@ extern void radeon_i2c_add(struct radeon_device *rdev,
|
||||
const char *name);
|
||||
extern struct radeon_i2c_chan *radeon_i2c_lookup(struct radeon_device *rdev,
|
||||
struct radeon_i2c_bus_rec *i2c_bus);
|
||||
extern struct radeon_i2c_chan *radeon_i2c_create_dp(struct drm_device *dev,
|
||||
struct radeon_i2c_bus_rec *rec,
|
||||
const char *name);
|
||||
extern struct radeon_i2c_chan *radeon_i2c_create(struct drm_device *dev,
|
||||
struct radeon_i2c_bus_rec *rec,
|
||||
const char *name);
|
||||
|
@ -57,9 +57,14 @@
|
||||
#define BTC_MC_UCODE_SIZE 6024
|
||||
#define CAYMAN_MC_UCODE_SIZE 6037
|
||||
#define SI_MC_UCODE_SIZE 7769
|
||||
#define TAHITI_MC_UCODE_SIZE 7808
|
||||
#define PITCAIRN_MC_UCODE_SIZE 7775
|
||||
#define VERDE_MC_UCODE_SIZE 7875
|
||||
#define OLAND_MC_UCODE_SIZE 7863
|
||||
#define CIK_MC_UCODE_SIZE 7866
|
||||
#define BONAIRE_MC_UCODE_SIZE 7866
|
||||
#define BONAIRE_MC2_UCODE_SIZE 7948
|
||||
#define HAWAII_MC_UCODE_SIZE 7933
|
||||
#define HAWAII_MC2_UCODE_SIZE 8091
|
||||
|
||||
/* SDMA */
|
||||
#define CIK_SDMA_UCODE_SIZE 1050
|
||||
|
@ -613,7 +613,7 @@ void radeon_vce_fence_emit(struct radeon_device *rdev,
|
||||
struct radeon_fence *fence)
|
||||
{
|
||||
struct radeon_ring *ring = &rdev->ring[fence->ring];
|
||||
uint32_t addr = rdev->fence_drv[fence->ring].gpu_addr;
|
||||
uint64_t addr = rdev->fence_drv[fence->ring].gpu_addr;
|
||||
|
||||
radeon_ring_write(ring, VCE_CMD_FENCE);
|
||||
radeon_ring_write(ring, addr);
|
||||
|
@ -39,30 +39,35 @@ MODULE_FIRMWARE("radeon/TAHITI_pfp.bin");
|
||||
MODULE_FIRMWARE("radeon/TAHITI_me.bin");
|
||||
MODULE_FIRMWARE("radeon/TAHITI_ce.bin");
|
||||
MODULE_FIRMWARE("radeon/TAHITI_mc.bin");
|
||||
MODULE_FIRMWARE("radeon/TAHITI_mc2.bin");
|
||||
MODULE_FIRMWARE("radeon/TAHITI_rlc.bin");
|
||||
MODULE_FIRMWARE("radeon/TAHITI_smc.bin");
|
||||
MODULE_FIRMWARE("radeon/PITCAIRN_pfp.bin");
|
||||
MODULE_FIRMWARE("radeon/PITCAIRN_me.bin");
|
||||
MODULE_FIRMWARE("radeon/PITCAIRN_ce.bin");
|
||||
MODULE_FIRMWARE("radeon/PITCAIRN_mc.bin");
|
||||
MODULE_FIRMWARE("radeon/PITCAIRN_mc2.bin");
|
||||
MODULE_FIRMWARE("radeon/PITCAIRN_rlc.bin");
|
||||
MODULE_FIRMWARE("radeon/PITCAIRN_smc.bin");
|
||||
MODULE_FIRMWARE("radeon/VERDE_pfp.bin");
|
||||
MODULE_FIRMWARE("radeon/VERDE_me.bin");
|
||||
MODULE_FIRMWARE("radeon/VERDE_ce.bin");
|
||||
MODULE_FIRMWARE("radeon/VERDE_mc.bin");
|
||||
MODULE_FIRMWARE("radeon/VERDE_mc2.bin");
|
||||
MODULE_FIRMWARE("radeon/VERDE_rlc.bin");
|
||||
MODULE_FIRMWARE("radeon/VERDE_smc.bin");
|
||||
MODULE_FIRMWARE("radeon/OLAND_pfp.bin");
|
||||
MODULE_FIRMWARE("radeon/OLAND_me.bin");
|
||||
MODULE_FIRMWARE("radeon/OLAND_ce.bin");
|
||||
MODULE_FIRMWARE("radeon/OLAND_mc.bin");
|
||||
MODULE_FIRMWARE("radeon/OLAND_mc2.bin");
|
||||
MODULE_FIRMWARE("radeon/OLAND_rlc.bin");
|
||||
MODULE_FIRMWARE("radeon/OLAND_smc.bin");
|
||||
MODULE_FIRMWARE("radeon/HAINAN_pfp.bin");
|
||||
MODULE_FIRMWARE("radeon/HAINAN_me.bin");
|
||||
MODULE_FIRMWARE("radeon/HAINAN_ce.bin");
|
||||
MODULE_FIRMWARE("radeon/HAINAN_mc.bin");
|
||||
MODULE_FIRMWARE("radeon/HAINAN_mc2.bin");
|
||||
MODULE_FIRMWARE("radeon/HAINAN_rlc.bin");
|
||||
MODULE_FIRMWARE("radeon/HAINAN_smc.bin");
|
||||
|
||||
@ -1467,36 +1472,33 @@ int si_mc_load_microcode(struct radeon_device *rdev)
|
||||
const __be32 *fw_data;
|
||||
u32 running, blackout = 0;
|
||||
u32 *io_mc_regs;
|
||||
int i, ucode_size, regs_size;
|
||||
int i, regs_size, ucode_size;
|
||||
|
||||
if (!rdev->mc_fw)
|
||||
return -EINVAL;
|
||||
|
||||
ucode_size = rdev->mc_fw->size / 4;
|
||||
|
||||
switch (rdev->family) {
|
||||
case CHIP_TAHITI:
|
||||
io_mc_regs = (u32 *)&tahiti_io_mc_regs;
|
||||
ucode_size = SI_MC_UCODE_SIZE;
|
||||
regs_size = TAHITI_IO_MC_REGS_SIZE;
|
||||
break;
|
||||
case CHIP_PITCAIRN:
|
||||
io_mc_regs = (u32 *)&pitcairn_io_mc_regs;
|
||||
ucode_size = SI_MC_UCODE_SIZE;
|
||||
regs_size = TAHITI_IO_MC_REGS_SIZE;
|
||||
break;
|
||||
case CHIP_VERDE:
|
||||
default:
|
||||
io_mc_regs = (u32 *)&verde_io_mc_regs;
|
||||
ucode_size = SI_MC_UCODE_SIZE;
|
||||
regs_size = TAHITI_IO_MC_REGS_SIZE;
|
||||
break;
|
||||
case CHIP_OLAND:
|
||||
io_mc_regs = (u32 *)&oland_io_mc_regs;
|
||||
ucode_size = OLAND_MC_UCODE_SIZE;
|
||||
regs_size = TAHITI_IO_MC_REGS_SIZE;
|
||||
break;
|
||||
case CHIP_HAINAN:
|
||||
io_mc_regs = (u32 *)&hainan_io_mc_regs;
|
||||
ucode_size = OLAND_MC_UCODE_SIZE;
|
||||
regs_size = TAHITI_IO_MC_REGS_SIZE;
|
||||
break;
|
||||
}
|
||||
@ -1552,7 +1554,7 @@ static int si_init_microcode(struct radeon_device *rdev)
|
||||
const char *chip_name;
|
||||
const char *rlc_chip_name;
|
||||
size_t pfp_req_size, me_req_size, ce_req_size, rlc_req_size, mc_req_size;
|
||||
size_t smc_req_size;
|
||||
size_t smc_req_size, mc2_req_size;
|
||||
char fw_name[30];
|
||||
int err;
|
||||
|
||||
@ -1567,6 +1569,7 @@ static int si_init_microcode(struct radeon_device *rdev)
|
||||
ce_req_size = SI_CE_UCODE_SIZE * 4;
|
||||
rlc_req_size = SI_RLC_UCODE_SIZE * 4;
|
||||
mc_req_size = SI_MC_UCODE_SIZE * 4;
|
||||
mc2_req_size = TAHITI_MC_UCODE_SIZE * 4;
|
||||
smc_req_size = ALIGN(TAHITI_SMC_UCODE_SIZE, 4);
|
||||
break;
|
||||
case CHIP_PITCAIRN:
|
||||
@ -1577,6 +1580,7 @@ static int si_init_microcode(struct radeon_device *rdev)
|
||||
ce_req_size = SI_CE_UCODE_SIZE * 4;
|
||||
rlc_req_size = SI_RLC_UCODE_SIZE * 4;
|
||||
mc_req_size = SI_MC_UCODE_SIZE * 4;
|
||||
mc2_req_size = PITCAIRN_MC_UCODE_SIZE * 4;
|
||||
smc_req_size = ALIGN(PITCAIRN_SMC_UCODE_SIZE, 4);
|
||||
break;
|
||||
case CHIP_VERDE:
|
||||
@ -1587,6 +1591,7 @@ static int si_init_microcode(struct radeon_device *rdev)
|
||||
ce_req_size = SI_CE_UCODE_SIZE * 4;
|
||||
rlc_req_size = SI_RLC_UCODE_SIZE * 4;
|
||||
mc_req_size = SI_MC_UCODE_SIZE * 4;
|
||||
mc2_req_size = VERDE_MC_UCODE_SIZE * 4;
|
||||
smc_req_size = ALIGN(VERDE_SMC_UCODE_SIZE, 4);
|
||||
break;
|
||||
case CHIP_OLAND:
|
||||
@ -1596,7 +1601,7 @@ static int si_init_microcode(struct radeon_device *rdev)
|
||||
me_req_size = SI_PM4_UCODE_SIZE * 4;
|
||||
ce_req_size = SI_CE_UCODE_SIZE * 4;
|
||||
rlc_req_size = SI_RLC_UCODE_SIZE * 4;
|
||||
mc_req_size = OLAND_MC_UCODE_SIZE * 4;
|
||||
mc_req_size = mc2_req_size = OLAND_MC_UCODE_SIZE * 4;
|
||||
smc_req_size = ALIGN(OLAND_SMC_UCODE_SIZE, 4);
|
||||
break;
|
||||
case CHIP_HAINAN:
|
||||
@ -1606,7 +1611,7 @@ static int si_init_microcode(struct radeon_device *rdev)
|
||||
me_req_size = SI_PM4_UCODE_SIZE * 4;
|
||||
ce_req_size = SI_CE_UCODE_SIZE * 4;
|
||||
rlc_req_size = SI_RLC_UCODE_SIZE * 4;
|
||||
mc_req_size = OLAND_MC_UCODE_SIZE * 4;
|
||||
mc_req_size = mc2_req_size = OLAND_MC_UCODE_SIZE * 4;
|
||||
smc_req_size = ALIGN(HAINAN_SMC_UCODE_SIZE, 4);
|
||||
break;
|
||||
default: BUG();
|
||||
@ -1659,16 +1664,22 @@ static int si_init_microcode(struct radeon_device *rdev)
|
||||
err = -EINVAL;
|
||||
}
|
||||
|
||||
snprintf(fw_name, sizeof(fw_name), "radeon/%s_mc.bin", chip_name);
|
||||
snprintf(fw_name, sizeof(fw_name), "radeon/%s_mc2.bin", chip_name);
|
||||
err = request_firmware(&rdev->mc_fw, fw_name, rdev->dev);
|
||||
if (err)
|
||||
goto out;
|
||||
if (rdev->mc_fw->size != mc_req_size) {
|
||||
if (err) {
|
||||
snprintf(fw_name, sizeof(fw_name), "radeon/%s_mc.bin", chip_name);
|
||||
err = request_firmware(&rdev->mc_fw, fw_name, rdev->dev);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
if ((rdev->mc_fw->size != mc_req_size) &&
|
||||
(rdev->mc_fw->size != mc2_req_size)) {
|
||||
printk(KERN_ERR
|
||||
"si_mc: Bogus length %zu in firmware \"%s\"\n",
|
||||
rdev->mc_fw->size, fw_name);
|
||||
err = -EINVAL;
|
||||
}
|
||||
DRM_INFO("%s: %zu bytes\n", fw_name, rdev->mc_fw->size);
|
||||
|
||||
snprintf(fw_name, sizeof(fw_name), "radeon/%s_smc.bin", chip_name);
|
||||
err = request_firmware(&rdev->smc_fw, fw_name, rdev->dev);
|
||||
|
@ -99,55 +99,73 @@ static void tegra_dpaux_read_fifo(struct tegra_dpaux *dpaux, u8 *buffer,
|
||||
static ssize_t tegra_dpaux_transfer(struct drm_dp_aux *aux,
|
||||
struct drm_dp_aux_msg *msg)
|
||||
{
|
||||
unsigned long value = DPAUX_DP_AUXCTL_TRANSACTREQ;
|
||||
unsigned long timeout = msecs_to_jiffies(250);
|
||||
struct tegra_dpaux *dpaux = to_dpaux(aux);
|
||||
unsigned long status;
|
||||
ssize_t ret = 0;
|
||||
u32 value;
|
||||
|
||||
if (msg->size < 1 || msg->size > 16)
|
||||
/* Tegra has 4x4 byte DP AUX transmit and receive FIFOs. */
|
||||
if (msg->size > 16)
|
||||
return -EINVAL;
|
||||
|
||||
tegra_dpaux_writel(dpaux, msg->address, DPAUX_DP_AUXADDR);
|
||||
/*
|
||||
* Allow zero-sized messages only for I2C, in which case they specify
|
||||
* address-only transactions.
|
||||
*/
|
||||
if (msg->size < 1) {
|
||||
switch (msg->request & ~DP_AUX_I2C_MOT) {
|
||||
case DP_AUX_I2C_WRITE:
|
||||
case DP_AUX_I2C_READ:
|
||||
value = DPAUX_DP_AUXCTL_CMD_ADDRESS_ONLY;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
/* For non-zero-sized messages, set the CMDLEN field. */
|
||||
value = DPAUX_DP_AUXCTL_CMDLEN(msg->size - 1);
|
||||
}
|
||||
|
||||
switch (msg->request & ~DP_AUX_I2C_MOT) {
|
||||
case DP_AUX_I2C_WRITE:
|
||||
if (msg->request & DP_AUX_I2C_MOT)
|
||||
value = DPAUX_DP_AUXCTL_CMD_MOT_WR;
|
||||
value |= DPAUX_DP_AUXCTL_CMD_MOT_WR;
|
||||
else
|
||||
value = DPAUX_DP_AUXCTL_CMD_I2C_WR;
|
||||
value |= DPAUX_DP_AUXCTL_CMD_I2C_WR;
|
||||
|
||||
break;
|
||||
|
||||
case DP_AUX_I2C_READ:
|
||||
if (msg->request & DP_AUX_I2C_MOT)
|
||||
value = DPAUX_DP_AUXCTL_CMD_MOT_RD;
|
||||
value |= DPAUX_DP_AUXCTL_CMD_MOT_RD;
|
||||
else
|
||||
value = DPAUX_DP_AUXCTL_CMD_I2C_RD;
|
||||
value |= DPAUX_DP_AUXCTL_CMD_I2C_RD;
|
||||
|
||||
break;
|
||||
|
||||
case DP_AUX_I2C_STATUS:
|
||||
if (msg->request & DP_AUX_I2C_MOT)
|
||||
value = DPAUX_DP_AUXCTL_CMD_MOT_RQ;
|
||||
value |= DPAUX_DP_AUXCTL_CMD_MOT_RQ;
|
||||
else
|
||||
value = DPAUX_DP_AUXCTL_CMD_I2C_RQ;
|
||||
value |= DPAUX_DP_AUXCTL_CMD_I2C_RQ;
|
||||
|
||||
break;
|
||||
|
||||
case DP_AUX_NATIVE_WRITE:
|
||||
value = DPAUX_DP_AUXCTL_CMD_AUX_WR;
|
||||
value |= DPAUX_DP_AUXCTL_CMD_AUX_WR;
|
||||
break;
|
||||
|
||||
case DP_AUX_NATIVE_READ:
|
||||
value = DPAUX_DP_AUXCTL_CMD_AUX_RD;
|
||||
value |= DPAUX_DP_AUXCTL_CMD_AUX_RD;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
value |= DPAUX_DP_AUXCTL_CMDLEN(msg->size - 1);
|
||||
tegra_dpaux_writel(dpaux, msg->address, DPAUX_DP_AUXADDR);
|
||||
tegra_dpaux_writel(dpaux, value, DPAUX_DP_AUXCTL);
|
||||
|
||||
if ((msg->request & DP_AUX_I2C_READ) == 0) {
|
||||
@ -198,7 +216,7 @@ static ssize_t tegra_dpaux_transfer(struct drm_dp_aux *aux,
|
||||
break;
|
||||
}
|
||||
|
||||
if (msg->reply == DP_AUX_NATIVE_REPLY_ACK) {
|
||||
if ((msg->size > 0) && (msg->reply == DP_AUX_NATIVE_REPLY_ACK)) {
|
||||
if (msg->request & DP_AUX_I2C_READ) {
|
||||
size_t count = value & DPAUX_DP_AUXSTAT_REPLY_MASK;
|
||||
|
||||
|
@ -32,6 +32,7 @@
|
||||
#define DPAUX_DP_AUXCTL_CMD_I2C_RQ (2 << 12)
|
||||
#define DPAUX_DP_AUXCTL_CMD_I2C_RD (1 << 12)
|
||||
#define DPAUX_DP_AUXCTL_CMD_I2C_WR (0 << 12)
|
||||
#define DPAUX_DP_AUXCTL_CMD_ADDRESS_ONLY (1 << 8)
|
||||
#define DPAUX_DP_AUXCTL_CMDLEN(x) ((x) & 0xff)
|
||||
|
||||
#define DPAUX_DP_AUXSTAT 0x31
|
||||
|
@ -47,7 +47,7 @@ static irqreturn_t syncpt_thresh_isr(int irq, void *dev_id)
|
||||
unsigned long reg;
|
||||
int i, id;
|
||||
|
||||
for (i = 0; i <= BIT_WORD(host->info->nb_pts); i++) {
|
||||
for (i = 0; i < DIV_ROUND_UP(host->info->nb_pts, 32); i++) {
|
||||
reg = host1x_sync_readl(host,
|
||||
HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(i));
|
||||
for_each_set_bit(id, ®, BITS_PER_LONG) {
|
||||
@ -64,7 +64,7 @@ static void _host1x_intr_disable_all_syncpt_intrs(struct host1x *host)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i <= BIT_WORD(host->info->nb_pts); ++i) {
|
||||
for (i = 0; i < DIV_ROUND_UP(host->info->nb_pts, 32); ++i) {
|
||||
host1x_sync_writel(host, 0xffffffffu,
|
||||
HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE(i));
|
||||
host1x_sync_writel(host, 0xffffffffu,
|
||||
|
@ -125,7 +125,6 @@ struct drm_connector_helper_funcs {
|
||||
struct drm_encoder *(*best_encoder)(struct drm_connector *connector);
|
||||
};
|
||||
|
||||
extern int drm_helper_probe_single_connector_modes(struct drm_connector *connector, uint32_t maxX, uint32_t maxY);
|
||||
extern void drm_helper_disable_unused_functions(struct drm_device *dev);
|
||||
extern int drm_crtc_helper_set_config(struct drm_mode_set *set);
|
||||
extern bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
|
||||
@ -161,6 +160,11 @@ static inline void drm_connector_helper_add(struct drm_connector *connector,
|
||||
}
|
||||
|
||||
extern void drm_helper_resume_force_mode(struct drm_device *dev);
|
||||
|
||||
/* drm_probe_helper.c */
|
||||
extern int drm_helper_probe_single_connector_modes(struct drm_connector
|
||||
*connector, uint32_t maxX,
|
||||
uint32_t maxY);
|
||||
extern void drm_kms_helper_poll_init(struct drm_device *dev);
|
||||
extern void drm_kms_helper_poll_fini(struct drm_device *dev);
|
||||
extern bool drm_helper_hpd_irq_event(struct drm_device *dev);
|
||||
|
@ -456,6 +456,10 @@ struct drm_dp_aux_msg {
|
||||
* transactions. The drm_dp_aux_register_i2c_bus() function registers an
|
||||
* I2C adapter that can be passed to drm_probe_ddc(). Upon removal, drivers
|
||||
* should call drm_dp_aux_unregister_i2c_bus() to remove the I2C adapter.
|
||||
*
|
||||
* Note that the aux helper code assumes that the .transfer() function
|
||||
* only modifies the reply field of the drm_dp_aux_msg structure. The
|
||||
* retry logic and i2c helpers assume this is the case.
|
||||
*/
|
||||
struct drm_dp_aux {
|
||||
const char *name;
|
||||
|
@ -120,7 +120,6 @@ struct drm_tegra_submit {
|
||||
__u32 num_waitchks;
|
||||
__u32 waitchk_mask;
|
||||
__u32 timeout;
|
||||
__u32 pad;
|
||||
__u64 syncpts;
|
||||
__u64 cmdbufs;
|
||||
__u64 relocs;
|
||||
|
Loading…
Reference in New Issue
Block a user