mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	nouveau: add runtime PM support (v0.9)
This hooks nouveau up to the runtime PM system to enable dynamic power management for secondary GPUs in switchable and optimus laptops. a) rewrite suspend/resume printks to hide them during dynamic s/r to avoid cluttering logs b) add runtime pm suspend to irq handler, crtc display, ioctl handler, connector status, c) handle hdmi audio dynamic power on/off using magic register. v0.5: make sure we hit D3 properly fix fbdev_set_suspend locking interaction, we only will poweroff if we have no active crtcs/fbcon anyways. add reference for active crtcs. sprinkle mark last busy for autosuspend timeout v0.6: allow more flexible debugging - to avoid log spam add option to enable/disable dynpm got to D3Cold v0.7: add hdmi audio support. v0.8: call autosuspend from idle, so pci config space access doesn't go straight back to sleep, this makes starting X faster. only signal usage if we actually handle the irq, otherwise usb keeps us awake. fix nv50 display active powerdown v0.9: use masking function to enable hdmi audio set busy when we fail to suspend Signed-off-by: Dave Airlie <airlied@redhat.com>
This commit is contained in:
		
							parent
							
								
									13bb9cc872
								
							
						
					
					
						commit
						5addcf0a5f
					
				
					 13 changed files with 404 additions and 43 deletions
				
			
		| 
						 | 
					@ -27,6 +27,8 @@
 | 
				
			||||||
#include <core/subdev.h>
 | 
					#include <core/subdev.h>
 | 
				
			||||||
#include <core/printk.h>
 | 
					#include <core/printk.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int nv_printk_suspend_level = NV_DBG_DEBUG;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void
 | 
					void
 | 
				
			||||||
nv_printk_(struct nouveau_object *object, const char *pfx, int level,
 | 
					nv_printk_(struct nouveau_object *object, const char *pfx, int level,
 | 
				
			||||||
	   const char *fmt, ...)
 | 
						   const char *fmt, ...)
 | 
				
			||||||
| 
						 | 
					@ -72,3 +74,20 @@ nv_printk_(struct nouveau_object *object, const char *pfx, int level,
 | 
				
			||||||
	vprintk(mfmt, args);
 | 
						vprintk(mfmt, args);
 | 
				
			||||||
	va_end(args);
 | 
						va_end(args);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define CONV_LEVEL(x) case NV_DBG_##x: return NV_PRINTK_##x
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char *nv_printk_level_to_pfx(int level)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						switch (level) {
 | 
				
			||||||
 | 
						CONV_LEVEL(FATAL);
 | 
				
			||||||
 | 
						CONV_LEVEL(ERROR);
 | 
				
			||||||
 | 
						CONV_LEVEL(WARN);
 | 
				
			||||||
 | 
						CONV_LEVEL(INFO);
 | 
				
			||||||
 | 
						CONV_LEVEL(DEBUG);
 | 
				
			||||||
 | 
						CONV_LEVEL(PARANOIA);
 | 
				
			||||||
 | 
						CONV_LEVEL(TRACE);
 | 
				
			||||||
 | 
						CONV_LEVEL(SPAM);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return NV_PRINTK_DEBUG;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,6 +15,12 @@ struct nouveau_object;
 | 
				
			||||||
#define NV_PRINTK_TRACE    KERN_DEBUG
 | 
					#define NV_PRINTK_TRACE    KERN_DEBUG
 | 
				
			||||||
#define NV_PRINTK_SPAM     KERN_DEBUG
 | 
					#define NV_PRINTK_SPAM     KERN_DEBUG
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern int nv_printk_suspend_level;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define NV_DBG_SUSPEND (nv_printk_suspend_level)
 | 
				
			||||||
 | 
					#define NV_PRINTK_SUSPEND  (nv_printk_level_to_pfx(nv_printk_suspend_level))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char *nv_printk_level_to_pfx(int level);
 | 
				
			||||||
void __printf(4, 5)
 | 
					void __printf(4, 5)
 | 
				
			||||||
nv_printk_(struct nouveau_object *, const char *, int, const char *, ...);
 | 
					nv_printk_(struct nouveau_object *, const char *, int, const char *, ...);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,6 +37,13 @@ nv_printk_(struct nouveau_object *, const char *, int, const char *, ...);
 | 
				
			||||||
#define nv_trace(o,f,a...) nv_printk((o), TRACE, f, ##a)
 | 
					#define nv_trace(o,f,a...) nv_printk((o), TRACE, f, ##a)
 | 
				
			||||||
#define nv_spam(o,f,a...) nv_printk((o), SPAM, f, ##a)
 | 
					#define nv_spam(o,f,a...) nv_printk((o), SPAM, f, ##a)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define nv_suspend(o,f,a...) nv_printk((o), SUSPEND, f, ##a)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline void nv_suspend_set_printk_level(int level)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						nv_printk_suspend_level = level;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define nv_assert(f,a...) do {                                                 \
 | 
					#define nv_assert(f,a...) do {                                                 \
 | 
				
			||||||
	if (NV_DBG_FATAL <= CONFIG_NOUVEAU_DEBUG)                              \
 | 
						if (NV_DBG_FATAL <= CONFIG_NOUVEAU_DEBUG)                              \
 | 
				
			||||||
		nv_printk_(NULL, NV_PRINTK_FATAL, NV_DBG_FATAL, f "\n", ##a);  \
 | 
							nv_printk_(NULL, NV_PRINTK_FATAL, NV_DBG_FATAL, f "\n", ##a);  \
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2165,7 +2165,7 @@ nvbios_init(struct nouveau_subdev *subdev, bool execute)
 | 
				
			||||||
	u16 data;
 | 
						u16 data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (execute)
 | 
						if (execute)
 | 
				
			||||||
		nv_info(bios, "running init tables\n");
 | 
							nv_suspend(bios, "running init tables\n");
 | 
				
			||||||
	while (!ret && (data = (init_script(bios, ++i)))) {
 | 
						while (!ret && (data = (init_script(bios, ++i)))) {
 | 
				
			||||||
		struct nvbios_init init = {
 | 
							struct nvbios_init init = {
 | 
				
			||||||
			.subdev = subdev,
 | 
								.subdev = subdev,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,16 +23,20 @@
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <subdev/mc.h>
 | 
					#include <subdev/mc.h>
 | 
				
			||||||
 | 
					#include <linux/pm_runtime.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static irqreturn_t
 | 
					static irqreturn_t
 | 
				
			||||||
nouveau_mc_intr(int irq, void *arg)
 | 
					nouveau_mc_intr(int irq, void *arg)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct nouveau_mc *pmc = arg;
 | 
						struct nouveau_mc *pmc = arg;
 | 
				
			||||||
	const struct nouveau_mc_intr *map = pmc->intr_map;
 | 
						const struct nouveau_mc_intr *map = pmc->intr_map;
 | 
				
			||||||
 | 
						struct nouveau_device *device = nv_device(pmc);
 | 
				
			||||||
	struct nouveau_subdev *unit;
 | 
						struct nouveau_subdev *unit;
 | 
				
			||||||
	u32 stat, intr;
 | 
						u32 stat, intr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	intr = stat = nv_rd32(pmc, 0x000100);
 | 
						intr = stat = nv_rd32(pmc, 0x000100);
 | 
				
			||||||
 | 
						if (intr == 0xffffffff)
 | 
				
			||||||
 | 
							return IRQ_NONE;
 | 
				
			||||||
	while (stat && map->stat) {
 | 
						while (stat && map->stat) {
 | 
				
			||||||
		if (stat & map->stat) {
 | 
							if (stat & map->stat) {
 | 
				
			||||||
			unit = nouveau_subdev(pmc, map->unit);
 | 
								unit = nouveau_subdev(pmc, map->unit);
 | 
				
			||||||
| 
						 | 
					@ -47,6 +51,8 @@ nouveau_mc_intr(int irq, void *arg)
 | 
				
			||||||
		nv_error(pmc, "unknown intr 0x%08x\n", stat);
 | 
							nv_error(pmc, "unknown intr 0x%08x\n", stat);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (stat == IRQ_HANDLED)
 | 
				
			||||||
 | 
							pm_runtime_mark_last_busy(&device->pdev->dev);
 | 
				
			||||||
	return stat ? IRQ_HANDLED : IRQ_NONE;
 | 
						return stat ? IRQ_HANDLED : IRQ_NONE;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,6 +22,7 @@
 | 
				
			||||||
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 | 
					 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 | 
				
			||||||
 * DEALINGS IN THE SOFTWARE.
 | 
					 * DEALINGS IN THE SOFTWARE.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					#include <linux/pm_runtime.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <drm/drmP.h>
 | 
					#include <drm/drmP.h>
 | 
				
			||||||
#include <drm/drm_crtc_helper.h>
 | 
					#include <drm/drm_crtc_helper.h>
 | 
				
			||||||
| 
						 | 
					@ -1007,13 +1008,59 @@ nv04_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int
 | 
				
			||||||
 | 
					nouveau_crtc_set_config(struct drm_mode_set *set)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct drm_device *dev;
 | 
				
			||||||
 | 
						struct nouveau_drm *drm;
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
						struct drm_crtc *crtc;
 | 
				
			||||||
 | 
						bool active = false;
 | 
				
			||||||
 | 
						if (!set || !set->crtc)
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dev = set->crtc->dev;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* get a pm reference here */
 | 
				
			||||||
 | 
						ret = pm_runtime_get_sync(dev->dev);
 | 
				
			||||||
 | 
						if (ret < 0)
 | 
				
			||||||
 | 
							return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = drm_crtc_helper_set_config(set);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						drm = nouveau_drm(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* if we get here with no crtcs active then we can drop a reference */
 | 
				
			||||||
 | 
						list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
 | 
				
			||||||
 | 
							if (crtc->enabled)
 | 
				
			||||||
 | 
								active = true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pm_runtime_mark_last_busy(dev->dev);
 | 
				
			||||||
 | 
						/* if we have active crtcs and we don't have a power ref,
 | 
				
			||||||
 | 
						   take the current one */
 | 
				
			||||||
 | 
						if (active && !drm->have_disp_power_ref) {
 | 
				
			||||||
 | 
							drm->have_disp_power_ref = true;
 | 
				
			||||||
 | 
							return ret;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						/* if we have no active crtcs, then drop the power ref
 | 
				
			||||||
 | 
						   we got before */
 | 
				
			||||||
 | 
						if (!active && drm->have_disp_power_ref) {
 | 
				
			||||||
 | 
							pm_runtime_put_autosuspend(dev->dev);
 | 
				
			||||||
 | 
							drm->have_disp_power_ref = false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						/* drop the power reference we got coming in here */
 | 
				
			||||||
 | 
						pm_runtime_put_autosuspend(dev->dev);
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const struct drm_crtc_funcs nv04_crtc_funcs = {
 | 
					static const struct drm_crtc_funcs nv04_crtc_funcs = {
 | 
				
			||||||
	.save = nv_crtc_save,
 | 
						.save = nv_crtc_save,
 | 
				
			||||||
	.restore = nv_crtc_restore,
 | 
						.restore = nv_crtc_restore,
 | 
				
			||||||
	.cursor_set = nv04_crtc_cursor_set,
 | 
						.cursor_set = nv04_crtc_cursor_set,
 | 
				
			||||||
	.cursor_move = nv04_crtc_cursor_move,
 | 
						.cursor_move = nv04_crtc_cursor_move,
 | 
				
			||||||
	.gamma_set = nv_crtc_gamma_set,
 | 
						.gamma_set = nv_crtc_gamma_set,
 | 
				
			||||||
	.set_config = drm_crtc_helper_set_config,
 | 
						.set_config = nouveau_crtc_set_config,
 | 
				
			||||||
	.page_flip = nouveau_crtc_page_flip,
 | 
						.page_flip = nouveau_crtc_page_flip,
 | 
				
			||||||
	.destroy = nv_crtc_destroy,
 | 
						.destroy = nv_crtc_destroy,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,8 +25,27 @@
 | 
				
			||||||
#define NOUVEAU_DSM_POWER_SPEED 0x01
 | 
					#define NOUVEAU_DSM_POWER_SPEED 0x01
 | 
				
			||||||
#define NOUVEAU_DSM_POWER_STAMINA 0x02
 | 
					#define NOUVEAU_DSM_POWER_STAMINA 0x02
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define NOUVEAU_DSM_OPTIMUS_FN 0x1A
 | 
					#define NOUVEAU_DSM_OPTIMUS_CAPS 0x1A
 | 
				
			||||||
#define NOUVEAU_DSM_OPTIMUS_ARGS 0x03000001
 | 
					#define NOUVEAU_DSM_OPTIMUS_FLAGS 0x1B
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define NOUVEAU_DSM_OPTIMUS_POWERDOWN_PS3 (3 << 24)
 | 
				
			||||||
 | 
					#define NOUVEAU_DSM_OPTIMUS_NO_POWERDOWN_PS3 (2 << 24)
 | 
				
			||||||
 | 
					#define NOUVEAU_DSM_OPTIMUS_FLAGS_CHANGED (1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define NOUVEAU_DSM_OPTIMUS_SET_POWERDOWN (NOUVEAU_DSM_OPTIMUS_POWERDOWN_PS3 | NOUVEAU_DSM_OPTIMUS_FLAGS_CHANGED)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* result of the optimus caps function */
 | 
				
			||||||
 | 
					#define OPTIMUS_ENABLED (1 << 0)
 | 
				
			||||||
 | 
					#define OPTIMUS_STATUS_MASK (3 << 3)
 | 
				
			||||||
 | 
					#define OPTIMUS_STATUS_OFF  (0 << 3)
 | 
				
			||||||
 | 
					#define OPTIMUS_STATUS_ON_ENABLED  (1 << 3)
 | 
				
			||||||
 | 
					#define OPTIMUS_STATUS_PWR_STABLE  (3 << 3)
 | 
				
			||||||
 | 
					#define OPTIMUS_DISPLAY_HOTPLUG (1 << 6)
 | 
				
			||||||
 | 
					#define OPTIMUS_CAPS_MASK (7 << 24)
 | 
				
			||||||
 | 
					#define OPTIMUS_DYNAMIC_PWR_CAP (1 << 24)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define OPTIMUS_AUDIO_CAPS_MASK (3 << 27)
 | 
				
			||||||
 | 
					#define OPTIMUS_HDA_CODEC_MASK (2 << 27) /* hda bios control */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct nouveau_dsm_priv {
 | 
					static struct nouveau_dsm_priv {
 | 
				
			||||||
	bool dsm_detected;
 | 
						bool dsm_detected;
 | 
				
			||||||
| 
						 | 
					@ -251,9 +270,18 @@ static int nouveau_dsm_pci_probe(struct pci_dev *pdev)
 | 
				
			||||||
		retval |= NOUVEAU_DSM_HAS_MUX;
 | 
							retval |= NOUVEAU_DSM_HAS_MUX;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (nouveau_test_dsm(dhandle, nouveau_optimus_dsm,
 | 
						if (nouveau_test_dsm(dhandle, nouveau_optimus_dsm,
 | 
				
			||||||
		NOUVEAU_DSM_OPTIMUS_FN))
 | 
							NOUVEAU_DSM_OPTIMUS_CAPS))
 | 
				
			||||||
		retval |= NOUVEAU_DSM_HAS_OPT;
 | 
							retval |= NOUVEAU_DSM_HAS_OPT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (retval & NOUVEAU_DSM_HAS_OPT) {
 | 
				
			||||||
 | 
							uint32_t result;
 | 
				
			||||||
 | 
							nouveau_optimus_dsm(dhandle, NOUVEAU_DSM_OPTIMUS_CAPS, 0,
 | 
				
			||||||
 | 
									    &result);
 | 
				
			||||||
 | 
							dev_info(&pdev->dev, "optimus capabilities: %s, status %s%s\n",
 | 
				
			||||||
 | 
								 (result & OPTIMUS_ENABLED) ? "enabled" : "disabled",
 | 
				
			||||||
 | 
								 (result & OPTIMUS_DYNAMIC_PWR_CAP) ? "dynamic power, " : "",
 | 
				
			||||||
 | 
								 (result & OPTIMUS_HDA_CODEC_MASK) ? "hda bios codec supported" : "");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if (retval)
 | 
						if (retval)
 | 
				
			||||||
		nouveau_dsm_priv.dhandle = dhandle;
 | 
							nouveau_dsm_priv.dhandle = dhandle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -328,8 +356,12 @@ void nouveau_switcheroo_optimus_dsm(void)
 | 
				
			||||||
	if (!nouveau_dsm_priv.optimus_detected)
 | 
						if (!nouveau_dsm_priv.optimus_detected)
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	nouveau_optimus_dsm(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_OPTIMUS_FN,
 | 
						nouveau_optimus_dsm(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_OPTIMUS_FLAGS,
 | 
				
			||||||
		NOUVEAU_DSM_OPTIMUS_ARGS, &result);
 | 
								    0x3, &result);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nouveau_optimus_dsm(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_OPTIMUS_CAPS,
 | 
				
			||||||
 | 
							NOUVEAU_DSM_OPTIMUS_SET_POWERDOWN, &result);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void nouveau_unregister_dsm_handler(void)
 | 
					void nouveau_unregister_dsm_handler(void)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,6 +26,8 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <acpi/button.h>
 | 
					#include <acpi/button.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <linux/pm_runtime.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <drm/drmP.h>
 | 
					#include <drm/drmP.h>
 | 
				
			||||||
#include <drm/drm_edid.h>
 | 
					#include <drm/drm_edid.h>
 | 
				
			||||||
#include <drm/drm_crtc_helper.h>
 | 
					#include <drm/drm_crtc_helper.h>
 | 
				
			||||||
| 
						 | 
					@ -240,6 +242,8 @@ nouveau_connector_detect(struct drm_connector *connector, bool force)
 | 
				
			||||||
	struct nouveau_encoder *nv_partner;
 | 
						struct nouveau_encoder *nv_partner;
 | 
				
			||||||
	struct nouveau_i2c_port *i2c;
 | 
						struct nouveau_i2c_port *i2c;
 | 
				
			||||||
	int type;
 | 
						int type;
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
						enum drm_connector_status conn_status = connector_status_disconnected;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Cleanup the previous EDID block. */
 | 
						/* Cleanup the previous EDID block. */
 | 
				
			||||||
	if (nv_connector->edid) {
 | 
						if (nv_connector->edid) {
 | 
				
			||||||
| 
						 | 
					@ -248,6 +252,10 @@ nouveau_connector_detect(struct drm_connector *connector, bool force)
 | 
				
			||||||
		nv_connector->edid = NULL;
 | 
							nv_connector->edid = NULL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = pm_runtime_get_sync(connector->dev->dev);
 | 
				
			||||||
 | 
						if (ret < 0)
 | 
				
			||||||
 | 
							return conn_status;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	i2c = nouveau_connector_ddc_detect(connector, &nv_encoder);
 | 
						i2c = nouveau_connector_ddc_detect(connector, &nv_encoder);
 | 
				
			||||||
	if (i2c) {
 | 
						if (i2c) {
 | 
				
			||||||
		nv_connector->edid = drm_get_edid(connector, &i2c->adapter);
 | 
							nv_connector->edid = drm_get_edid(connector, &i2c->adapter);
 | 
				
			||||||
| 
						 | 
					@ -263,7 +271,8 @@ nouveau_connector_detect(struct drm_connector *connector, bool force)
 | 
				
			||||||
		    !nouveau_dp_detect(to_drm_encoder(nv_encoder))) {
 | 
							    !nouveau_dp_detect(to_drm_encoder(nv_encoder))) {
 | 
				
			||||||
			NV_ERROR(drm, "Detected %s, but failed init\n",
 | 
								NV_ERROR(drm, "Detected %s, but failed init\n",
 | 
				
			||||||
				 drm_get_connector_name(connector));
 | 
									 drm_get_connector_name(connector));
 | 
				
			||||||
			return connector_status_disconnected;
 | 
								conn_status = connector_status_disconnected;
 | 
				
			||||||
 | 
								goto out;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* Override encoder type for DVI-I based on whether EDID
 | 
							/* Override encoder type for DVI-I based on whether EDID
 | 
				
			||||||
| 
						 | 
					@ -290,13 +299,15 @@ nouveau_connector_detect(struct drm_connector *connector, bool force)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		nouveau_connector_set_encoder(connector, nv_encoder);
 | 
							nouveau_connector_set_encoder(connector, nv_encoder);
 | 
				
			||||||
		return connector_status_connected;
 | 
							conn_status = connector_status_connected;
 | 
				
			||||||
 | 
							goto out;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	nv_encoder = nouveau_connector_of_detect(connector);
 | 
						nv_encoder = nouveau_connector_of_detect(connector);
 | 
				
			||||||
	if (nv_encoder) {
 | 
						if (nv_encoder) {
 | 
				
			||||||
		nouveau_connector_set_encoder(connector, nv_encoder);
 | 
							nouveau_connector_set_encoder(connector, nv_encoder);
 | 
				
			||||||
		return connector_status_connected;
 | 
							conn_status = connector_status_connected;
 | 
				
			||||||
 | 
							goto out;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
detect_analog:
 | 
					detect_analog:
 | 
				
			||||||
| 
						 | 
					@ -311,12 +322,18 @@ nouveau_connector_detect(struct drm_connector *connector, bool force)
 | 
				
			||||||
		if (helper->detect(encoder, connector) ==
 | 
							if (helper->detect(encoder, connector) ==
 | 
				
			||||||
						connector_status_connected) {
 | 
											connector_status_connected) {
 | 
				
			||||||
			nouveau_connector_set_encoder(connector, nv_encoder);
 | 
								nouveau_connector_set_encoder(connector, nv_encoder);
 | 
				
			||||||
			return connector_status_connected;
 | 
								conn_status = connector_status_connected;
 | 
				
			||||||
 | 
								goto out;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return connector_status_disconnected;
 | 
					 out:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pm_runtime_mark_last_busy(connector->dev->dev);
 | 
				
			||||||
 | 
						pm_runtime_put_autosuspend(connector->dev->dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return conn_status;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static enum drm_connector_status
 | 
					static enum drm_connector_status
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -394,7 +394,7 @@ nouveau_display_suspend(struct drm_device *dev)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	nouveau_display_fini(dev);
 | 
						nouveau_display_fini(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	NV_INFO(drm, "unpinning framebuffer(s)...\n");
 | 
						NV_SUSPEND(drm, "unpinning framebuffer(s)...\n");
 | 
				
			||||||
	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
 | 
						list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
 | 
				
			||||||
		struct nouveau_framebuffer *nouveau_fb;
 | 
							struct nouveau_framebuffer *nouveau_fb;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -416,7 +416,7 @@ nouveau_display_suspend(struct drm_device *dev)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void
 | 
					void
 | 
				
			||||||
nouveau_display_resume(struct drm_device *dev)
 | 
					nouveau_display_repin(struct drm_device *dev)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct nouveau_drm *drm = nouveau_drm(dev);
 | 
						struct nouveau_drm *drm = nouveau_drm(dev);
 | 
				
			||||||
	struct drm_crtc *crtc;
 | 
						struct drm_crtc *crtc;
 | 
				
			||||||
| 
						 | 
					@ -441,10 +441,12 @@ nouveau_display_resume(struct drm_device *dev)
 | 
				
			||||||
		if (ret)
 | 
							if (ret)
 | 
				
			||||||
			NV_ERROR(drm, "Could not pin/map cursor.\n");
 | 
								NV_ERROR(drm, "Could not pin/map cursor.\n");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	nouveau_fbcon_set_suspend(dev, 0);
 | 
					void
 | 
				
			||||||
	nouveau_fbcon_zfill_all(dev);
 | 
					nouveau_display_resume(struct drm_device *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct drm_crtc *crtc;
 | 
				
			||||||
	nouveau_display_init(dev);
 | 
						nouveau_display_init(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Force CLUT to get re-loaded during modeset */
 | 
						/* Force CLUT to get re-loaded during modeset */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -57,6 +57,7 @@ void nouveau_display_destroy(struct drm_device *dev);
 | 
				
			||||||
int  nouveau_display_init(struct drm_device *dev);
 | 
					int  nouveau_display_init(struct drm_device *dev);
 | 
				
			||||||
void nouveau_display_fini(struct drm_device *dev);
 | 
					void nouveau_display_fini(struct drm_device *dev);
 | 
				
			||||||
int  nouveau_display_suspend(struct drm_device *dev);
 | 
					int  nouveau_display_suspend(struct drm_device *dev);
 | 
				
			||||||
 | 
					void nouveau_display_repin(struct drm_device *dev);
 | 
				
			||||||
void nouveau_display_resume(struct drm_device *dev);
 | 
					void nouveau_display_resume(struct drm_device *dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int  nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
 | 
					int  nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
 | 
				
			||||||
| 
						 | 
					@ -71,6 +72,7 @@ int  nouveau_display_dumb_map_offset(struct drm_file *, struct drm_device *,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void nouveau_hdmi_mode_set(struct drm_encoder *, struct drm_display_mode *);
 | 
					void nouveau_hdmi_mode_set(struct drm_encoder *, struct drm_display_mode *);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int nouveau_crtc_set_config(struct drm_mode_set *set);
 | 
				
			||||||
#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT
 | 
					#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT
 | 
				
			||||||
extern int nouveau_backlight_init(struct drm_device *);
 | 
					extern int nouveau_backlight_init(struct drm_device *);
 | 
				
			||||||
extern void nouveau_backlight_exit(struct drm_device *);
 | 
					extern void nouveau_backlight_exit(struct drm_device *);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,7 +25,10 @@
 | 
				
			||||||
#include <linux/console.h>
 | 
					#include <linux/console.h>
 | 
				
			||||||
#include <linux/module.h>
 | 
					#include <linux/module.h>
 | 
				
			||||||
#include <linux/pci.h>
 | 
					#include <linux/pci.h>
 | 
				
			||||||
 | 
					#include <linux/pm_runtime.h>
 | 
				
			||||||
 | 
					#include <linux/vga_switcheroo.h>
 | 
				
			||||||
 | 
					#include "drmP.h"
 | 
				
			||||||
 | 
					#include "drm_crtc_helper.h"
 | 
				
			||||||
#include <core/device.h>
 | 
					#include <core/device.h>
 | 
				
			||||||
#include <core/client.h>
 | 
					#include <core/client.h>
 | 
				
			||||||
#include <core/gpuobj.h>
 | 
					#include <core/gpuobj.h>
 | 
				
			||||||
| 
						 | 
					@ -69,6 +72,10 @@ MODULE_PARM_DESC(modeset, "enable driver (default: auto, "
 | 
				
			||||||
int nouveau_modeset = -1;
 | 
					int nouveau_modeset = -1;
 | 
				
			||||||
module_param_named(modeset, nouveau_modeset, int, 0400);
 | 
					module_param_named(modeset, nouveau_modeset, int, 0400);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MODULE_PARM_DESC(runpm, "disable (0), force enable (1), optimus only default (-1)");
 | 
				
			||||||
 | 
					int nouveau_runtime_pm = -1;
 | 
				
			||||||
 | 
					module_param_named(runpm, nouveau_runtime_pm, int, 0400);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct drm_driver driver;
 | 
					static struct drm_driver driver;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int
 | 
					static int
 | 
				
			||||||
| 
						 | 
					@ -296,6 +303,31 @@ static int nouveau_drm_probe(struct pci_dev *pdev,
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					nouveau_get_hdmi_dev(struct drm_device *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct nouveau_drm *drm = dev->dev_private;
 | 
				
			||||||
 | 
						struct pci_dev *pdev = dev->pdev;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* subfunction one is a hdmi audio device? */
 | 
				
			||||||
 | 
						drm->hdmi_device = pci_get_bus_and_slot((unsigned int)pdev->bus->number,
 | 
				
			||||||
 | 
											PCI_DEVFN(PCI_SLOT(pdev->devfn), 1));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!drm->hdmi_device) {
 | 
				
			||||||
 | 
							DRM_INFO("hdmi device  not found %d %d %d\n", pdev->bus->number, PCI_SLOT(pdev->devfn), 1);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ((drm->hdmi_device->class >> 8) != PCI_CLASS_MULTIMEDIA_HD_AUDIO) {
 | 
				
			||||||
 | 
							DRM_INFO("possible hdmi device  not audio %d\n", drm->hdmi_device->class);
 | 
				
			||||||
 | 
							pci_dev_put(drm->hdmi_device);
 | 
				
			||||||
 | 
							drm->hdmi_device = NULL;
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int
 | 
					static int
 | 
				
			||||||
nouveau_drm_load(struct drm_device *dev, unsigned long flags)
 | 
					nouveau_drm_load(struct drm_device *dev, unsigned long flags)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -314,6 +346,8 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)
 | 
				
			||||||
	INIT_LIST_HEAD(&drm->clients);
 | 
						INIT_LIST_HEAD(&drm->clients);
 | 
				
			||||||
	spin_lock_init(&drm->tile.lock);
 | 
						spin_lock_init(&drm->tile.lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nouveau_get_hdmi_dev(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* make sure AGP controller is in a consistent state before we
 | 
						/* make sure AGP controller is in a consistent state before we
 | 
				
			||||||
	 * (possibly) execute vbios init tables (see nouveau_agp.h)
 | 
						 * (possibly) execute vbios init tables (see nouveau_agp.h)
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
| 
						 | 
					@ -388,6 +422,15 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	nouveau_accel_init(drm);
 | 
						nouveau_accel_init(drm);
 | 
				
			||||||
	nouveau_fbcon_init(dev);
 | 
						nouveau_fbcon_init(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (nouveau_runtime_pm != 0) {
 | 
				
			||||||
 | 
							pm_runtime_use_autosuspend(dev->dev);
 | 
				
			||||||
 | 
							pm_runtime_set_autosuspend_delay(dev->dev, 5000);
 | 
				
			||||||
 | 
							pm_runtime_set_active(dev->dev);
 | 
				
			||||||
 | 
							pm_runtime_allow(dev->dev);
 | 
				
			||||||
 | 
							pm_runtime_mark_last_busy(dev->dev);
 | 
				
			||||||
 | 
							pm_runtime_put(dev->dev);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fail_dispinit:
 | 
					fail_dispinit:
 | 
				
			||||||
| 
						 | 
					@ -409,6 +452,7 @@ nouveau_drm_unload(struct drm_device *dev)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct nouveau_drm *drm = nouveau_drm(dev);
 | 
						struct nouveau_drm *drm = nouveau_drm(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pm_runtime_get_sync(dev->dev);
 | 
				
			||||||
	nouveau_fbcon_fini(dev);
 | 
						nouveau_fbcon_fini(dev);
 | 
				
			||||||
	nouveau_accel_fini(drm);
 | 
						nouveau_accel_fini(drm);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -424,6 +468,8 @@ nouveau_drm_unload(struct drm_device *dev)
 | 
				
			||||||
	nouveau_agp_fini(drm);
 | 
						nouveau_agp_fini(drm);
 | 
				
			||||||
	nouveau_vga_fini(drm);
 | 
						nouveau_vga_fini(drm);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (drm->hdmi_device)
 | 
				
			||||||
 | 
							pci_dev_put(drm->hdmi_device);
 | 
				
			||||||
	nouveau_cli_destroy(&drm->client);
 | 
						nouveau_cli_destroy(&drm->client);
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -450,19 +496,16 @@ nouveau_do_suspend(struct drm_device *dev)
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (dev->mode_config.num_crtc) {
 | 
						if (dev->mode_config.num_crtc) {
 | 
				
			||||||
		NV_INFO(drm, "suspending fbcon...\n");
 | 
							NV_SUSPEND(drm, "suspending display...\n");
 | 
				
			||||||
		nouveau_fbcon_set_suspend(dev, 1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		NV_INFO(drm, "suspending display...\n");
 | 
					 | 
				
			||||||
		ret = nouveau_display_suspend(dev);
 | 
							ret = nouveau_display_suspend(dev);
 | 
				
			||||||
		if (ret)
 | 
							if (ret)
 | 
				
			||||||
			return ret;
 | 
								return ret;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	NV_INFO(drm, "evicting buffers...\n");
 | 
						NV_SUSPEND(drm, "evicting buffers...\n");
 | 
				
			||||||
	ttm_bo_evict_mm(&drm->ttm.bdev, TTM_PL_VRAM);
 | 
						ttm_bo_evict_mm(&drm->ttm.bdev, TTM_PL_VRAM);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	NV_INFO(drm, "waiting for kernel channels to go idle...\n");
 | 
						NV_SUSPEND(drm, "waiting for kernel channels to go idle...\n");
 | 
				
			||||||
	if (drm->cechan) {
 | 
						if (drm->cechan) {
 | 
				
			||||||
		ret = nouveau_channel_idle(drm->cechan);
 | 
							ret = nouveau_channel_idle(drm->cechan);
 | 
				
			||||||
		if (ret)
 | 
							if (ret)
 | 
				
			||||||
| 
						 | 
					@ -475,7 +518,7 @@ nouveau_do_suspend(struct drm_device *dev)
 | 
				
			||||||
			return ret;
 | 
								return ret;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	NV_INFO(drm, "suspending client object trees...\n");
 | 
						NV_SUSPEND(drm, "suspending client object trees...\n");
 | 
				
			||||||
	if (drm->fence && nouveau_fence(drm)->suspend) {
 | 
						if (drm->fence && nouveau_fence(drm)->suspend) {
 | 
				
			||||||
		if (!nouveau_fence(drm)->suspend(drm))
 | 
							if (!nouveau_fence(drm)->suspend(drm))
 | 
				
			||||||
			return -ENOMEM;
 | 
								return -ENOMEM;
 | 
				
			||||||
| 
						 | 
					@ -487,7 +530,7 @@ nouveau_do_suspend(struct drm_device *dev)
 | 
				
			||||||
			goto fail_client;
 | 
								goto fail_client;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	NV_INFO(drm, "suspending kernel object tree...\n");
 | 
						NV_SUSPEND(drm, "suspending kernel object tree...\n");
 | 
				
			||||||
	ret = nouveau_client_fini(&drm->client.base, true);
 | 
						ret = nouveau_client_fini(&drm->client.base, true);
 | 
				
			||||||
	if (ret)
 | 
						if (ret)
 | 
				
			||||||
		goto fail_client;
 | 
							goto fail_client;
 | 
				
			||||||
| 
						 | 
					@ -501,7 +544,7 @@ nouveau_do_suspend(struct drm_device *dev)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (dev->mode_config.num_crtc) {
 | 
						if (dev->mode_config.num_crtc) {
 | 
				
			||||||
		NV_INFO(drm, "resuming display...\n");
 | 
							NV_SUSPEND(drm, "resuming display...\n");
 | 
				
			||||||
		nouveau_display_resume(dev);
 | 
							nouveau_display_resume(dev);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
| 
						 | 
					@ -513,9 +556,14 @@ int nouveau_pmops_suspend(struct device *dev)
 | 
				
			||||||
	struct drm_device *drm_dev = pci_get_drvdata(pdev);
 | 
						struct drm_device *drm_dev = pci_get_drvdata(pdev);
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF)
 | 
						if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF ||
 | 
				
			||||||
 | 
						    drm_dev->switch_power_state == DRM_SWITCH_POWER_DYNAMIC_OFF)
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (drm_dev->mode_config.num_crtc)
 | 
				
			||||||
 | 
							nouveau_fbcon_set_suspend(drm_dev, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nv_suspend_set_printk_level(NV_DBG_INFO);
 | 
				
			||||||
	ret = nouveau_do_suspend(drm_dev);
 | 
						ret = nouveau_do_suspend(drm_dev);
 | 
				
			||||||
	if (ret)
 | 
						if (ret)
 | 
				
			||||||
		return ret;
 | 
							return ret;
 | 
				
			||||||
| 
						 | 
					@ -523,6 +571,7 @@ int nouveau_pmops_suspend(struct device *dev)
 | 
				
			||||||
	pci_save_state(pdev);
 | 
						pci_save_state(pdev);
 | 
				
			||||||
	pci_disable_device(pdev);
 | 
						pci_disable_device(pdev);
 | 
				
			||||||
	pci_set_power_state(pdev, PCI_D3hot);
 | 
						pci_set_power_state(pdev, PCI_D3hot);
 | 
				
			||||||
 | 
						nv_suspend_set_printk_level(NV_DBG_DEBUG);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -533,15 +582,15 @@ nouveau_do_resume(struct drm_device *dev)
 | 
				
			||||||
	struct nouveau_drm *drm = nouveau_drm(dev);
 | 
						struct nouveau_drm *drm = nouveau_drm(dev);
 | 
				
			||||||
	struct nouveau_cli *cli;
 | 
						struct nouveau_cli *cli;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	NV_INFO(drm, "re-enabling device...\n");
 | 
						NV_SUSPEND(drm, "re-enabling device...\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	nouveau_agp_reset(drm);
 | 
						nouveau_agp_reset(drm);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	NV_INFO(drm, "resuming kernel object tree...\n");
 | 
						NV_SUSPEND(drm, "resuming kernel object tree...\n");
 | 
				
			||||||
	nouveau_client_init(&drm->client.base);
 | 
						nouveau_client_init(&drm->client.base);
 | 
				
			||||||
	nouveau_agp_init(drm);
 | 
						nouveau_agp_init(drm);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	NV_INFO(drm, "resuming client object trees...\n");
 | 
						NV_SUSPEND(drm, "resuming client object trees...\n");
 | 
				
			||||||
	if (drm->fence && nouveau_fence(drm)->resume)
 | 
						if (drm->fence && nouveau_fence(drm)->resume)
 | 
				
			||||||
		nouveau_fence(drm)->resume(drm);
 | 
							nouveau_fence(drm)->resume(drm);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -553,9 +602,10 @@ nouveau_do_resume(struct drm_device *dev)
 | 
				
			||||||
	nouveau_pm_resume(dev);
 | 
						nouveau_pm_resume(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (dev->mode_config.num_crtc) {
 | 
						if (dev->mode_config.num_crtc) {
 | 
				
			||||||
		NV_INFO(drm, "resuming display...\n");
 | 
							NV_SUSPEND(drm, "resuming display...\n");
 | 
				
			||||||
		nouveau_display_resume(dev);
 | 
							nouveau_display_repin(dev);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -565,7 +615,8 @@ int nouveau_pmops_resume(struct device *dev)
 | 
				
			||||||
	struct drm_device *drm_dev = pci_get_drvdata(pdev);
 | 
						struct drm_device *drm_dev = pci_get_drvdata(pdev);
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF)
 | 
						if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF ||
 | 
				
			||||||
 | 
						    drm_dev->switch_power_state == DRM_SWITCH_POWER_DYNAMIC_OFF)
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pci_set_power_state(pdev, PCI_D0);
 | 
						pci_set_power_state(pdev, PCI_D0);
 | 
				
			||||||
| 
						 | 
					@ -575,23 +626,54 @@ int nouveau_pmops_resume(struct device *dev)
 | 
				
			||||||
		return ret;
 | 
							return ret;
 | 
				
			||||||
	pci_set_master(pdev);
 | 
						pci_set_master(pdev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nouveau_do_resume(drm_dev);
 | 
						nv_suspend_set_printk_level(NV_DBG_INFO);
 | 
				
			||||||
 | 
						ret = nouveau_do_resume(drm_dev);
 | 
				
			||||||
 | 
						if (ret) {
 | 
				
			||||||
 | 
							nv_suspend_set_printk_level(NV_DBG_DEBUG);
 | 
				
			||||||
 | 
							return ret;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (drm_dev->mode_config.num_crtc)
 | 
				
			||||||
 | 
							nouveau_fbcon_set_suspend(drm_dev, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nouveau_fbcon_zfill_all(drm_dev);
 | 
				
			||||||
 | 
						nouveau_display_resume(drm_dev);
 | 
				
			||||||
 | 
						nv_suspend_set_printk_level(NV_DBG_DEBUG);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int nouveau_pmops_freeze(struct device *dev)
 | 
					static int nouveau_pmops_freeze(struct device *dev)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct pci_dev *pdev = to_pci_dev(dev);
 | 
						struct pci_dev *pdev = to_pci_dev(dev);
 | 
				
			||||||
	struct drm_device *drm_dev = pci_get_drvdata(pdev);
 | 
						struct drm_device *drm_dev = pci_get_drvdata(pdev);
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nouveau_do_suspend(drm_dev);
 | 
						nv_suspend_set_printk_level(NV_DBG_INFO);
 | 
				
			||||||
 | 
						if (drm_dev->mode_config.num_crtc)
 | 
				
			||||||
 | 
							nouveau_fbcon_set_suspend(drm_dev, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = nouveau_do_suspend(drm_dev);
 | 
				
			||||||
 | 
						nv_suspend_set_printk_level(NV_DBG_DEBUG);
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int nouveau_pmops_thaw(struct device *dev)
 | 
					static int nouveau_pmops_thaw(struct device *dev)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct pci_dev *pdev = to_pci_dev(dev);
 | 
						struct pci_dev *pdev = to_pci_dev(dev);
 | 
				
			||||||
	struct drm_device *drm_dev = pci_get_drvdata(pdev);
 | 
						struct drm_device *drm_dev = pci_get_drvdata(pdev);
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nouveau_do_resume(drm_dev);
 | 
						nv_suspend_set_printk_level(NV_DBG_INFO);
 | 
				
			||||||
 | 
						ret = nouveau_do_resume(drm_dev);
 | 
				
			||||||
 | 
						if (ret) {
 | 
				
			||||||
 | 
							nv_suspend_set_printk_level(NV_DBG_DEBUG);
 | 
				
			||||||
 | 
							return ret;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (drm_dev->mode_config.num_crtc)
 | 
				
			||||||
 | 
							nouveau_fbcon_set_suspend(drm_dev, 0);
 | 
				
			||||||
 | 
						nouveau_fbcon_zfill_all(drm_dev);
 | 
				
			||||||
 | 
						nouveau_display_resume(drm_dev);
 | 
				
			||||||
 | 
						nv_suspend_set_printk_level(NV_DBG_DEBUG);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -604,19 +686,24 @@ nouveau_drm_open(struct drm_device *dev, struct drm_file *fpriv)
 | 
				
			||||||
	char name[32], tmpname[TASK_COMM_LEN];
 | 
						char name[32], tmpname[TASK_COMM_LEN];
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* need to bring up power immediately if opening device */
 | 
				
			||||||
 | 
						ret = pm_runtime_get_sync(dev->dev);
 | 
				
			||||||
 | 
						if (ret < 0)
 | 
				
			||||||
 | 
							return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	get_task_comm(tmpname, current);
 | 
						get_task_comm(tmpname, current);
 | 
				
			||||||
	snprintf(name, sizeof(name), "%s[%d]", tmpname, pid_nr(fpriv->pid));
 | 
						snprintf(name, sizeof(name), "%s[%d]", tmpname, pid_nr(fpriv->pid));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ret = nouveau_cli_create(pdev, name, sizeof(*cli), (void **)&cli);
 | 
						ret = nouveau_cli_create(pdev, name, sizeof(*cli), (void **)&cli);
 | 
				
			||||||
	if (ret)
 | 
						if (ret)
 | 
				
			||||||
		return ret;
 | 
							goto out_suspend;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (nv_device(drm->device)->card_type >= NV_50) {
 | 
						if (nv_device(drm->device)->card_type >= NV_50) {
 | 
				
			||||||
		ret = nouveau_vm_new(nv_device(drm->device), 0, (1ULL << 40),
 | 
							ret = nouveau_vm_new(nv_device(drm->device), 0, (1ULL << 40),
 | 
				
			||||||
				     0x1000, &cli->base.vm);
 | 
									     0x1000, &cli->base.vm);
 | 
				
			||||||
		if (ret) {
 | 
							if (ret) {
 | 
				
			||||||
			nouveau_cli_destroy(cli);
 | 
								nouveau_cli_destroy(cli);
 | 
				
			||||||
			return ret;
 | 
								goto out_suspend;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -625,7 +712,12 @@ nouveau_drm_open(struct drm_device *dev, struct drm_file *fpriv)
 | 
				
			||||||
	mutex_lock(&drm->client.mutex);
 | 
						mutex_lock(&drm->client.mutex);
 | 
				
			||||||
	list_add(&cli->head, &drm->clients);
 | 
						list_add(&cli->head, &drm->clients);
 | 
				
			||||||
	mutex_unlock(&drm->client.mutex);
 | 
						mutex_unlock(&drm->client.mutex);
 | 
				
			||||||
	return 0;
 | 
					
 | 
				
			||||||
 | 
					out_suspend:
 | 
				
			||||||
 | 
						pm_runtime_mark_last_busy(dev->dev);
 | 
				
			||||||
 | 
						pm_runtime_put_autosuspend(dev->dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
| 
						 | 
					@ -634,12 +726,15 @@ nouveau_drm_preclose(struct drm_device *dev, struct drm_file *fpriv)
 | 
				
			||||||
	struct nouveau_cli *cli = nouveau_cli(fpriv);
 | 
						struct nouveau_cli *cli = nouveau_cli(fpriv);
 | 
				
			||||||
	struct nouveau_drm *drm = nouveau_drm(dev);
 | 
						struct nouveau_drm *drm = nouveau_drm(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pm_runtime_get_sync(dev->dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (cli->abi16)
 | 
						if (cli->abi16)
 | 
				
			||||||
		nouveau_abi16_fini(cli->abi16);
 | 
							nouveau_abi16_fini(cli->abi16);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mutex_lock(&drm->client.mutex);
 | 
						mutex_lock(&drm->client.mutex);
 | 
				
			||||||
	list_del(&cli->head);
 | 
						list_del(&cli->head);
 | 
				
			||||||
	mutex_unlock(&drm->client.mutex);
 | 
						mutex_unlock(&drm->client.mutex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
| 
						 | 
					@ -647,6 +742,8 @@ nouveau_drm_postclose(struct drm_device *dev, struct drm_file *fpriv)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct nouveau_cli *cli = nouveau_cli(fpriv);
 | 
						struct nouveau_cli *cli = nouveau_cli(fpriv);
 | 
				
			||||||
	nouveau_cli_destroy(cli);
 | 
						nouveau_cli_destroy(cli);
 | 
				
			||||||
 | 
						pm_runtime_mark_last_busy(dev->dev);
 | 
				
			||||||
 | 
						pm_runtime_put_autosuspend(dev->dev);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const struct drm_ioctl_desc
 | 
					static const struct drm_ioctl_desc
 | 
				
			||||||
| 
						 | 
					@ -665,12 +762,30 @@ nouveau_ioctls[] = {
 | 
				
			||||||
	DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_INFO, nouveau_gem_ioctl_info, DRM_UNLOCKED|DRM_AUTH),
 | 
						DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_INFO, nouveau_gem_ioctl_info, DRM_UNLOCKED|DRM_AUTH),
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					long nouveau_drm_ioctl(struct file *filp,
 | 
				
			||||||
 | 
							       unsigned int cmd, unsigned long arg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct drm_file *file_priv = filp->private_data;
 | 
				
			||||||
 | 
						struct drm_device *dev;
 | 
				
			||||||
 | 
						long ret;
 | 
				
			||||||
 | 
						dev = file_priv->minor->dev;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = pm_runtime_get_sync(dev->dev);
 | 
				
			||||||
 | 
						if (ret < 0)
 | 
				
			||||||
 | 
							return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = drm_ioctl(filp, cmd, arg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pm_runtime_mark_last_busy(dev->dev);
 | 
				
			||||||
 | 
						pm_runtime_put_autosuspend(dev->dev);
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
static const struct file_operations
 | 
					static const struct file_operations
 | 
				
			||||||
nouveau_driver_fops = {
 | 
					nouveau_driver_fops = {
 | 
				
			||||||
	.owner = THIS_MODULE,
 | 
						.owner = THIS_MODULE,
 | 
				
			||||||
	.open = drm_open,
 | 
						.open = drm_open,
 | 
				
			||||||
	.release = drm_release,
 | 
						.release = drm_release,
 | 
				
			||||||
	.unlocked_ioctl = drm_ioctl,
 | 
						.unlocked_ioctl = nouveau_drm_ioctl,
 | 
				
			||||||
	.mmap = nouveau_ttm_mmap,
 | 
						.mmap = nouveau_ttm_mmap,
 | 
				
			||||||
	.poll = drm_poll,
 | 
						.poll = drm_poll,
 | 
				
			||||||
	.read = drm_read,
 | 
						.read = drm_read,
 | 
				
			||||||
| 
						 | 
					@ -753,6 +868,90 @@ nouveau_drm_pci_table[] = {
 | 
				
			||||||
	{}
 | 
						{}
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int nouveau_pmops_runtime_suspend(struct device *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct pci_dev *pdev = to_pci_dev(dev);
 | 
				
			||||||
 | 
						struct drm_device *drm_dev = pci_get_drvdata(pdev);
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (nouveau_runtime_pm == 0)
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						drm_kms_helper_poll_disable(drm_dev);
 | 
				
			||||||
 | 
						vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_OFF);
 | 
				
			||||||
 | 
						nouveau_switcheroo_optimus_dsm();
 | 
				
			||||||
 | 
						ret = nouveau_do_suspend(drm_dev);
 | 
				
			||||||
 | 
						pci_save_state(pdev);
 | 
				
			||||||
 | 
						pci_disable_device(pdev);
 | 
				
			||||||
 | 
						pci_set_power_state(pdev, PCI_D3cold);
 | 
				
			||||||
 | 
						drm_dev->switch_power_state = DRM_SWITCH_POWER_DYNAMIC_OFF;
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int nouveau_pmops_runtime_resume(struct device *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct pci_dev *pdev = to_pci_dev(dev);
 | 
				
			||||||
 | 
						struct drm_device *drm_dev = pci_get_drvdata(pdev);
 | 
				
			||||||
 | 
						struct nouveau_device *device = nouveau_dev(drm_dev);
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (nouveau_runtime_pm == 0)
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pci_set_power_state(pdev, PCI_D0);
 | 
				
			||||||
 | 
						pci_restore_state(pdev);
 | 
				
			||||||
 | 
						ret = pci_enable_device(pdev);
 | 
				
			||||||
 | 
						if (ret)
 | 
				
			||||||
 | 
							return ret;
 | 
				
			||||||
 | 
						pci_set_master(pdev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = nouveau_do_resume(drm_dev);
 | 
				
			||||||
 | 
						nouveau_display_resume(drm_dev);
 | 
				
			||||||
 | 
						drm_kms_helper_poll_enable(drm_dev);
 | 
				
			||||||
 | 
						/* do magic */
 | 
				
			||||||
 | 
						nv_mask(device, 0x88488, (1 << 25), (1 << 25));
 | 
				
			||||||
 | 
						vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_ON);
 | 
				
			||||||
 | 
						drm_dev->switch_power_state = DRM_SWITCH_POWER_ON;
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int nouveau_pmops_runtime_idle(struct device *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct pci_dev *pdev = to_pci_dev(dev);
 | 
				
			||||||
 | 
						struct drm_device *drm_dev = pci_get_drvdata(pdev);
 | 
				
			||||||
 | 
						struct nouveau_drm *drm = nouveau_drm(drm_dev);
 | 
				
			||||||
 | 
						struct drm_crtc *crtc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (nouveau_runtime_pm == 0)
 | 
				
			||||||
 | 
							return -EBUSY;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* are we optimus enabled? */
 | 
				
			||||||
 | 
						if (nouveau_runtime_pm == -1 && !nouveau_is_optimus() && !nouveau_is_v1_dsm()) {
 | 
				
			||||||
 | 
							DRM_DEBUG_DRIVER("failing to power off - not optimus\n");
 | 
				
			||||||
 | 
							return -EBUSY;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* if we have a hdmi audio device - make sure it has a driver loaded */
 | 
				
			||||||
 | 
						if (drm->hdmi_device) {
 | 
				
			||||||
 | 
							if (!drm->hdmi_device->driver) {
 | 
				
			||||||
 | 
								DRM_DEBUG_DRIVER("failing to power off - no HDMI audio driver loaded\n");
 | 
				
			||||||
 | 
								pm_runtime_mark_last_busy(dev);
 | 
				
			||||||
 | 
								return -EBUSY;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						list_for_each_entry(crtc, &drm->dev->mode_config.crtc_list, head) {
 | 
				
			||||||
 | 
							if (crtc->enabled) {
 | 
				
			||||||
 | 
								DRM_DEBUG_DRIVER("failing to power off - crtc active\n");
 | 
				
			||||||
 | 
								return -EBUSY;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						pm_runtime_mark_last_busy(dev);
 | 
				
			||||||
 | 
						pm_runtime_autosuspend(dev);
 | 
				
			||||||
 | 
						/* we don't want the main rpm_idle to call suspend - we want to autosuspend */
 | 
				
			||||||
 | 
						return 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const struct dev_pm_ops nouveau_pm_ops = {
 | 
					static const struct dev_pm_ops nouveau_pm_ops = {
 | 
				
			||||||
	.suspend = nouveau_pmops_suspend,
 | 
						.suspend = nouveau_pmops_suspend,
 | 
				
			||||||
	.resume = nouveau_pmops_resume,
 | 
						.resume = nouveau_pmops_resume,
 | 
				
			||||||
| 
						 | 
					@ -760,6 +959,9 @@ static const struct dev_pm_ops nouveau_pm_ops = {
 | 
				
			||||||
	.thaw = nouveau_pmops_thaw,
 | 
						.thaw = nouveau_pmops_thaw,
 | 
				
			||||||
	.poweroff = nouveau_pmops_freeze,
 | 
						.poweroff = nouveau_pmops_freeze,
 | 
				
			||||||
	.restore = nouveau_pmops_resume,
 | 
						.restore = nouveau_pmops_resume,
 | 
				
			||||||
 | 
						.runtime_suspend = nouveau_pmops_runtime_suspend,
 | 
				
			||||||
 | 
						.runtime_resume = nouveau_pmops_runtime_resume,
 | 
				
			||||||
 | 
						.runtime_idle = nouveau_pmops_runtime_idle,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct pci_driver
 | 
					static struct pci_driver
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -70,6 +70,8 @@ nouveau_cli(struct drm_file *fpriv)
 | 
				
			||||||
	return fpriv ? fpriv->driver_priv : NULL;
 | 
						return fpriv ? fpriv->driver_priv : NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern int nouveau_runtime_pm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct nouveau_drm {
 | 
					struct nouveau_drm {
 | 
				
			||||||
	struct nouveau_cli client;
 | 
						struct nouveau_cli client;
 | 
				
			||||||
	struct drm_device *dev;
 | 
						struct drm_device *dev;
 | 
				
			||||||
| 
						 | 
					@ -129,6 +131,12 @@ struct nouveau_drm {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* power management */
 | 
						/* power management */
 | 
				
			||||||
	struct nouveau_pm *pm;
 | 
						struct nouveau_pm *pm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* display power reference */
 | 
				
			||||||
 | 
						bool have_disp_power_ref;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct dev_pm_domain vga_pm_domain;
 | 
				
			||||||
 | 
						struct pci_dev *hdmi_device;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline struct nouveau_drm *
 | 
					static inline struct nouveau_drm *
 | 
				
			||||||
| 
						 | 
					@ -146,6 +154,7 @@ nouveau_dev(struct drm_device *dev)
 | 
				
			||||||
int nouveau_pmops_suspend(struct device *);
 | 
					int nouveau_pmops_suspend(struct device *);
 | 
				
			||||||
int nouveau_pmops_resume(struct device *);
 | 
					int nouveau_pmops_resume(struct device *);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define NV_SUSPEND(cli, fmt, args...) nv_suspend((cli), fmt, ##args)
 | 
				
			||||||
#define NV_FATAL(cli, fmt, args...) nv_fatal((cli), fmt, ##args)
 | 
					#define NV_FATAL(cli, fmt, args...) nv_fatal((cli), fmt, ##args)
 | 
				
			||||||
#define NV_ERROR(cli, fmt, args...) nv_error((cli), fmt, ##args)
 | 
					#define NV_ERROR(cli, fmt, args...) nv_error((cli), fmt, ##args)
 | 
				
			||||||
#define NV_WARN(cli, fmt, args...) nv_warn((cli), fmt, ##args)
 | 
					#define NV_WARN(cli, fmt, args...) nv_warn((cli), fmt, ##args)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,6 +32,9 @@ nouveau_switcheroo_set_state(struct pci_dev *pdev,
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct drm_device *dev = pci_get_drvdata(pdev);
 | 
						struct drm_device *dev = pci_get_drvdata(pdev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ((nouveau_is_optimus() || nouveau_is_v1_dsm()) && state == VGA_SWITCHEROO_OFF)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (state == VGA_SWITCHEROO_ON) {
 | 
						if (state == VGA_SWITCHEROO_ON) {
 | 
				
			||||||
		printk(KERN_ERR "VGA switcheroo: switched nouveau on\n");
 | 
							printk(KERN_ERR "VGA switcheroo: switched nouveau on\n");
 | 
				
			||||||
		dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
 | 
							dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
 | 
				
			||||||
| 
						 | 
					@ -78,8 +81,17 @@ void
 | 
				
			||||||
nouveau_vga_init(struct nouveau_drm *drm)
 | 
					nouveau_vga_init(struct nouveau_drm *drm)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct drm_device *dev = drm->dev;
 | 
						struct drm_device *dev = drm->dev;
 | 
				
			||||||
 | 
						bool runtime = false;
 | 
				
			||||||
	vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode);
 | 
						vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode);
 | 
				
			||||||
	vga_switcheroo_register_client(dev->pdev, &nouveau_switcheroo_ops, false);
 | 
					
 | 
				
			||||||
 | 
						if (nouveau_runtime_pm == 1)
 | 
				
			||||||
 | 
							runtime = true;
 | 
				
			||||||
 | 
						if ((nouveau_runtime_pm == -1) && (nouveau_is_optimus() || nouveau_is_v1_dsm()))
 | 
				
			||||||
 | 
							runtime = true;
 | 
				
			||||||
 | 
						vga_switcheroo_register_client(dev->pdev, &nouveau_switcheroo_ops, runtime);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (runtime && nouveau_is_v1_dsm() && !nouveau_is_optimus())
 | 
				
			||||||
 | 
							vga_switcheroo_init_domain_pm_ops(drm->dev->dev, &drm->vga_pm_domain);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void
 | 
					void
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1326,7 +1326,7 @@ static const struct drm_crtc_funcs nv50_crtc_func = {
 | 
				
			||||||
	.cursor_set = nv50_crtc_cursor_set,
 | 
						.cursor_set = nv50_crtc_cursor_set,
 | 
				
			||||||
	.cursor_move = nv50_crtc_cursor_move,
 | 
						.cursor_move = nv50_crtc_cursor_move,
 | 
				
			||||||
	.gamma_set = nv50_crtc_gamma_set,
 | 
						.gamma_set = nv50_crtc_gamma_set,
 | 
				
			||||||
	.set_config = drm_crtc_helper_set_config,
 | 
						.set_config = nouveau_crtc_set_config,
 | 
				
			||||||
	.destroy = nv50_crtc_destroy,
 | 
						.destroy = nv50_crtc_destroy,
 | 
				
			||||||
	.page_flip = nouveau_crtc_page_flip,
 | 
						.page_flip = nouveau_crtc_page_flip,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue