| 
									
										
										
										
											2009-12-11 19:24:15 +10:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Copyright 2009 Red Hat Inc. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Permission is hereby granted, free of charge, to any person obtaining a | 
					
						
							|  |  |  |  * copy of this software and associated documentation files (the "Software"), | 
					
						
							|  |  |  |  * to deal in the Software without restriction, including without limitation | 
					
						
							|  |  |  |  * the rights to use, copy, modify, merge, publish, distribute, sublicense, | 
					
						
							|  |  |  |  * and/or sell copies of the Software, and to permit persons to whom the | 
					
						
							|  |  |  |  * Software is furnished to do so, subject to the following conditions: | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * The above copyright notice and this permission notice shall be included in | 
					
						
							|  |  |  |  * all copies or substantial portions of the Software. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
					
						
							|  |  |  |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
					
						
							|  |  |  |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL | 
					
						
							|  |  |  |  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | 
					
						
							|  |  |  |  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | 
					
						
							|  |  |  |  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | 
					
						
							|  |  |  |  * OTHER DEALINGS IN THE SOFTWARE. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Author: Ben Skeggs | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "drmP.h"
 | 
					
						
							|  |  |  | #include "drm.h"
 | 
					
						
							|  |  |  | #include "drm_crtc_helper.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "nouveau_drv.h"
 | 
					
						
							|  |  |  | #include "nouveau_fb.h"
 | 
					
						
							|  |  |  | #include "nouveau_hw.h"
 | 
					
						
							|  |  |  | #include "nouveau_encoder.h"
 | 
					
						
							|  |  |  | #include "nouveau_connector.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define MULTIPLE_ENCODERS(e) (e & (e - 1))
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | nv04_display_store_initial_head_owner(struct drm_device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct drm_nouveau_private *dev_priv = dev->dev_private; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (dev_priv->chipset != 0x11) { | 
					
						
							|  |  |  | 		dev_priv->crtc_owner = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_44); | 
					
						
							|  |  |  | 		goto ownerknown; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* reading CR44 is broken on nv11, so we attempt to infer it */ | 
					
						
							|  |  |  | 	if (nvReadMC(dev, NV_PBUS_DEBUG_1) & (1 << 28))	/* heads tied, restore both */ | 
					
						
							|  |  |  | 		dev_priv->crtc_owner = 0x4; | 
					
						
							|  |  |  | 	else { | 
					
						
							|  |  |  | 		uint8_t slaved_on_A, slaved_on_B; | 
					
						
							|  |  |  | 		bool tvA = false; | 
					
						
							|  |  |  | 		bool tvB = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		NVLockVgaCrtcs(dev, false); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		slaved_on_B = NVReadVgaCrtc(dev, 1, NV_CIO_CRE_PIXEL_INDEX) & | 
					
						
							|  |  |  | 									0x80; | 
					
						
							|  |  |  | 		if (slaved_on_B) | 
					
						
							|  |  |  | 			tvB = !(NVReadVgaCrtc(dev, 1, NV_CIO_CRE_LCD__INDEX) & | 
					
						
							|  |  |  | 					MASK(NV_CIO_CRE_LCD_LCD_SELECT)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		slaved_on_A = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX) & | 
					
						
							|  |  |  | 									0x80; | 
					
						
							|  |  |  | 		if (slaved_on_A) | 
					
						
							|  |  |  | 			tvA = !(NVReadVgaCrtc(dev, 0, NV_CIO_CRE_LCD__INDEX) & | 
					
						
							|  |  |  | 					MASK(NV_CIO_CRE_LCD_LCD_SELECT)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		NVLockVgaCrtcs(dev, true); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (slaved_on_A && !tvA) | 
					
						
							|  |  |  | 			dev_priv->crtc_owner = 0x0; | 
					
						
							|  |  |  | 		else if (slaved_on_B && !tvB) | 
					
						
							|  |  |  | 			dev_priv->crtc_owner = 0x3; | 
					
						
							|  |  |  | 		else if (slaved_on_A) | 
					
						
							|  |  |  | 			dev_priv->crtc_owner = 0x0; | 
					
						
							|  |  |  | 		else if (slaved_on_B) | 
					
						
							|  |  |  | 			dev_priv->crtc_owner = 0x3; | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			dev_priv->crtc_owner = 0x0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ownerknown: | 
					
						
							|  |  |  | 	NV_INFO(dev, "Initial CRTC_OWNER is %d\n", dev_priv->crtc_owner); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* we need to ensure the heads are not tied henceforth, or reading any
 | 
					
						
							|  |  |  | 	 * 8 bit reg on head B will fail | 
					
						
							|  |  |  | 	 * setting a single arbitrary head solves that */ | 
					
						
							|  |  |  | 	NVSetOwner(dev, 0); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int | 
					
						
							|  |  |  | nv04_display_create(struct drm_device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct drm_nouveau_private *dev_priv = dev->dev_private; | 
					
						
							| 
									
										
										
										
											2010-02-24 10:03:05 +10:00
										 |  |  | 	struct dcb_table *dcb = &dev_priv->vbios.dcb; | 
					
						
							| 
									
										
										
										
											2009-12-11 19:24:15 +10:00
										 |  |  | 	struct drm_encoder *encoder; | 
					
						
							|  |  |  | 	struct drm_crtc *crtc; | 
					
						
							|  |  |  | 	int i, ret; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-13 16:53:12 +01:00
										 |  |  | 	NV_DEBUG_KMS(dev, "\n"); | 
					
						
							| 
									
										
										
										
											2009-12-11 19:24:15 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (nv_two_heads(dev)) | 
					
						
							|  |  |  | 		nv04_display_store_initial_head_owner(dev); | 
					
						
							| 
									
										
										
										
											2009-12-11 23:44:49 +01:00
										 |  |  | 	nouveau_hw_save_vga_fonts(dev, 1); | 
					
						
							| 
									
										
										
										
											2009-12-11 19:24:15 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	drm_mode_config_init(dev); | 
					
						
							|  |  |  | 	drm_mode_create_scaling_mode_property(dev); | 
					
						
							|  |  |  | 	drm_mode_create_dithering_property(dev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dev->mode_config.funcs = (void *)&nouveau_mode_config_funcs; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dev->mode_config.min_width = 0; | 
					
						
							|  |  |  | 	dev->mode_config.min_height = 0; | 
					
						
							|  |  |  | 	switch (dev_priv->card_type) { | 
					
						
							|  |  |  | 	case NV_04: | 
					
						
							|  |  |  | 		dev->mode_config.max_width = 2048; | 
					
						
							|  |  |  | 		dev->mode_config.max_height = 2048; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		dev->mode_config.max_width = 4096; | 
					
						
							|  |  |  | 		dev->mode_config.max_height = 4096; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dev->mode_config.fb_base = dev_priv->fb_phys; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	nv04_crtc_create(dev, 0); | 
					
						
							|  |  |  | 	if (nv_two_heads(dev)) | 
					
						
							|  |  |  | 		nv04_crtc_create(dev, 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < dcb->entries; i++) { | 
					
						
							|  |  |  | 		struct dcb_entry *dcbent = &dcb->entry[i]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		switch (dcbent->type) { | 
					
						
							|  |  |  | 		case OUTPUT_ANALOG: | 
					
						
							|  |  |  | 			ret = nv04_dac_create(dev, dcbent); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case OUTPUT_LVDS: | 
					
						
							|  |  |  | 		case OUTPUT_TMDS: | 
					
						
							|  |  |  | 			ret = nv04_dfp_create(dev, dcbent); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case OUTPUT_TV: | 
					
						
							|  |  |  | 			if (dcbent->location == DCB_LOC_ON_CHIP) | 
					
						
							|  |  |  | 				ret = nv17_tv_create(dev, dcbent); | 
					
						
							|  |  |  | 			else | 
					
						
							|  |  |  | 				ret = nv04_tv_create(dev, dcbent); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			NV_WARN(dev, "DCB type %d not known\n", dcbent->type); | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (ret) | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-02-24 12:07:31 +10:00
										 |  |  | 	for (i = 0; i < dcb->connector.entries; i++) | 
					
						
							|  |  |  | 		nouveau_connector_create(dev, &dcb->connector.entry[i]); | 
					
						
							| 
									
										
										
										
											2009-12-11 19:24:15 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Save previous state */ | 
					
						
							|  |  |  | 	NVLockVgaCrtcs(dev, false); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) | 
					
						
							|  |  |  | 		crtc->funcs->save(crtc); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | 
					
						
							|  |  |  | 		struct drm_encoder_helper_funcs *func = encoder->helper_private; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		func->save(encoder); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void | 
					
						
							|  |  |  | nv04_display_destroy(struct drm_device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct drm_encoder *encoder; | 
					
						
							|  |  |  | 	struct drm_crtc *crtc; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-13 16:53:12 +01:00
										 |  |  | 	NV_DEBUG_KMS(dev, "\n"); | 
					
						
							| 
									
										
										
										
											2009-12-11 19:24:15 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Turn every CRTC off. */ | 
					
						
							|  |  |  | 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | 
					
						
							|  |  |  | 		struct drm_mode_set modeset = { | 
					
						
							|  |  |  | 			.crtc = crtc, | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		crtc->funcs->set_config(&modeset); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Restore state */ | 
					
						
							|  |  |  | 	NVLockVgaCrtcs(dev, false); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | 
					
						
							|  |  |  | 		struct drm_encoder_helper_funcs *func = encoder->helper_private; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		func->restore(encoder); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) | 
					
						
							|  |  |  | 		crtc->funcs->restore(crtc); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	drm_mode_config_cleanup(dev); | 
					
						
							| 
									
										
										
										
											2009-12-11 23:44:49 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	nouveau_hw_save_vga_fonts(dev, 0); | 
					
						
							| 
									
										
										
										
											2009-12-11 19:24:15 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void | 
					
						
							|  |  |  | nv04_display_restore(struct drm_device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct drm_nouveau_private *dev_priv = dev->dev_private; | 
					
						
							|  |  |  | 	struct drm_encoder *encoder; | 
					
						
							|  |  |  | 	struct drm_crtc *crtc; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	NVLockVgaCrtcs(dev, false); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* meh.. modeset apparently doesn't setup all the regs and depends
 | 
					
						
							|  |  |  | 	 * on pre-existing state, for now load the state of the card *before* | 
					
						
							|  |  |  | 	 * nouveau was loaded, and then do a modeset. | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * best thing to do probably is to make save/restore routines not | 
					
						
							|  |  |  | 	 * save/restore "pre-load" state, but more general so we can save | 
					
						
							|  |  |  | 	 * on suspend too. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | 
					
						
							|  |  |  | 		struct drm_encoder_helper_funcs *func = encoder->helper_private; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		func->restore(encoder); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) | 
					
						
							|  |  |  | 		crtc->funcs->restore(crtc); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (nv_two_heads(dev)) { | 
					
						
							|  |  |  | 		NV_INFO(dev, "Restoring CRTC_OWNER to %d.\n", | 
					
						
							|  |  |  | 			dev_priv->crtc_owner); | 
					
						
							|  |  |  | 		NVSetOwner(dev, dev_priv->crtc_owner); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	NVLockVgaCrtcs(dev, true); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 |