nouveau_dispnv04_disp.c revision 1.2
1/* $NetBSD: nouveau_dispnv04_disp.c,v 1.2 2014/08/06 15:01:34 riastradh Exp $ */ 2 3/* 4 * Copyright 2009 Red Hat Inc. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 * OTHER DEALINGS IN THE SOFTWARE. 23 * 24 * Author: Ben Skeggs 25 */ 26 27#include <sys/cdefs.h> 28__KERNEL_RCSID(0, "$NetBSD: nouveau_dispnv04_disp.c,v 1.2 2014/08/06 15:01:34 riastradh Exp $"); 29 30#include <linux/err.h> 31 32#include <core/object.h> 33#include <core/class.h> 34 35#include <drm/drmP.h> 36#include <drm/drm_crtc_helper.h> 37 38#include "nouveau_drm.h" 39#include "nouveau_reg.h" 40#include "hw.h" 41#include "nouveau_encoder.h" 42#include "nouveau_connector.h" 43 44#include <subdev/i2c.h> 45 46int 47nv04_display_early_init(struct drm_device *dev) 48{ 49 /* ensure vblank interrupts are off, they can't be enabled until 50 * drm_vblank has been initialised 51 */ 52 NVWriteCRTC(dev, 0, NV_PCRTC_INTR_EN_0, 0); 53 if (nv_two_heads(dev)) 54 NVWriteCRTC(dev, 1, NV_PCRTC_INTR_EN_0, 0); 55 56 return 0; 57} 58 59void 60nv04_display_late_takedown(struct drm_device *dev) 61{ 62} 63 64int 65nv04_display_create(struct drm_device *dev) 66{ 67 struct nouveau_drm *drm = nouveau_drm(dev); 68 struct nouveau_i2c *i2c = nouveau_i2c(drm->device); 69 struct dcb_table *dcb = &drm->vbios.dcb; 70 struct drm_connector *connector, *ct; 71 struct drm_encoder *encoder; 72 struct drm_crtc *crtc; 73 struct nv04_display *disp; 74 int i, ret; 75 76 disp = kzalloc(sizeof(*disp), GFP_KERNEL); 77 if (!disp) 78 return -ENOMEM; 79 80 nouveau_display(dev)->priv = disp; 81 nouveau_display(dev)->dtor = nv04_display_destroy; 82 nouveau_display(dev)->init = nv04_display_init; 83 nouveau_display(dev)->fini = nv04_display_fini; 84 85 nouveau_hw_save_vga_fonts(dev, 1); 86 87 nv04_crtc_create(dev, 0); 88 if (nv_two_heads(dev)) 89 nv04_crtc_create(dev, 1); 90 91 for (i = 0; i < dcb->entries; i++) { 92 struct dcb_output *dcbent = &dcb->entry[i]; 93 94 connector = nouveau_connector_create(dev, dcbent->connector); 95 if (IS_ERR(connector)) 96 continue; 97 98 switch (dcbent->type) { 99 case DCB_OUTPUT_ANALOG: 100 ret = nv04_dac_create(connector, dcbent); 101 break; 102 case DCB_OUTPUT_LVDS: 103 case DCB_OUTPUT_TMDS: 104 ret = nv04_dfp_create(connector, dcbent); 105 break; 106 case DCB_OUTPUT_TV: 107 if (dcbent->location == DCB_LOC_ON_CHIP) 108 ret = nv17_tv_create(connector, dcbent); 109 else 110 ret = nv04_tv_create(connector, dcbent); 111 break; 112 default: 113 NV_WARN(drm, "DCB type %d not known\n", dcbent->type); 114 continue; 115 } 116 117 if (ret) 118 continue; 119 } 120 121 list_for_each_entry_safe(connector, ct, 122 &dev->mode_config.connector_list, head) { 123 if (!connector->encoder_ids[0]) { 124 NV_WARN(drm, "%s has no encoders, removing\n", 125 drm_get_connector_name(connector)); 126 connector->funcs->destroy(connector); 127 } 128 } 129 130 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 131 struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); 132 nv_encoder->i2c = i2c->find(i2c, nv_encoder->dcb->i2c_index); 133 } 134 135 /* Save previous state */ 136 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) 137 crtc->funcs->save(crtc); 138 139 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 140 struct drm_encoder_helper_funcs *func = encoder->helper_private; 141 142 func->save(encoder); 143 } 144 145 nouveau_overlay_init(dev); 146 147 return 0; 148} 149 150void 151nv04_display_destroy(struct drm_device *dev) 152{ 153 struct nv04_display *disp = nv04_display(dev); 154 struct drm_encoder *encoder; 155 struct drm_crtc *crtc; 156 157 /* Turn every CRTC off. */ 158 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 159 struct drm_mode_set modeset = { 160 .crtc = crtc, 161 }; 162 163 drm_mode_set_config_internal(&modeset); 164 } 165 166 /* Restore state */ 167 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 168 struct drm_encoder_helper_funcs *func = encoder->helper_private; 169 170 func->restore(encoder); 171 } 172 173 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) 174 crtc->funcs->restore(crtc); 175 176 nouveau_hw_save_vga_fonts(dev, 0); 177 178 nouveau_display(dev)->priv = NULL; 179 kfree(disp); 180} 181 182int 183nv04_display_init(struct drm_device *dev) 184{ 185 struct drm_encoder *encoder; 186 struct drm_crtc *crtc; 187 188 /* meh.. modeset apparently doesn't setup all the regs and depends 189 * on pre-existing state, for now load the state of the card *before* 190 * nouveau was loaded, and then do a modeset. 191 * 192 * best thing to do probably is to make save/restore routines not 193 * save/restore "pre-load" state, but more general so we can save 194 * on suspend too. 195 */ 196 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 197 struct drm_encoder_helper_funcs *func = encoder->helper_private; 198 199 func->restore(encoder); 200 } 201 202 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) 203 crtc->funcs->restore(crtc); 204 205 return 0; 206} 207 208void 209nv04_display_fini(struct drm_device *dev) 210{ 211 /* disable vblank interrupts */ 212 NVWriteCRTC(dev, 0, NV_PCRTC_INTR_EN_0, 0); 213 if (nv_two_heads(dev)) 214 NVWriteCRTC(dev, 1, NV_PCRTC_INTR_EN_0, 0); 215} 216