162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright 2009 Red Hat Inc. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 562306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 662306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation 762306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 862306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 962306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * The above copyright notice and this permission notice shall be included in 1262306a36Sopenharmony_ci * all copies or substantial portions of the Software. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1562306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1662306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1762306a36Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 1862306a36Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 1962306a36Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 2062306a36Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE. 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * Author: Ben Skeggs 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <drm/drm_crtc_helper.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include "nouveau_drv.h" 2862306a36Sopenharmony_ci#include "nouveau_reg.h" 2962306a36Sopenharmony_ci#include "hw.h" 3062306a36Sopenharmony_ci#include "nouveau_encoder.h" 3162306a36Sopenharmony_ci#include "nouveau_connector.h" 3262306a36Sopenharmony_ci#include "nouveau_bo.h" 3362306a36Sopenharmony_ci#include "nouveau_gem.h" 3462306a36Sopenharmony_ci#include "nouveau_chan.h" 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#include <nvif/if0004.h> 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistruct nouveau_connector * 3962306a36Sopenharmony_cinv04_encoder_get_connector(struct nouveau_encoder *encoder) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci struct drm_device *dev = to_drm_encoder(encoder)->dev; 4262306a36Sopenharmony_ci struct drm_connector *connector; 4362306a36Sopenharmony_ci struct drm_connector_list_iter conn_iter; 4462306a36Sopenharmony_ci struct nouveau_connector *nv_connector = NULL; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci drm_connector_list_iter_begin(dev, &conn_iter); 4762306a36Sopenharmony_ci drm_for_each_connector_iter(connector, &conn_iter) { 4862306a36Sopenharmony_ci if (connector->encoder == to_drm_encoder(encoder)) 4962306a36Sopenharmony_ci nv_connector = nouveau_connector(connector); 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci drm_connector_list_iter_end(&conn_iter); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci return nv_connector; 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic void 5762306a36Sopenharmony_cinv04_display_fini(struct drm_device *dev, bool runtime, bool suspend) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 6062306a36Sopenharmony_ci struct nv04_display *disp = nv04_display(dev); 6162306a36Sopenharmony_ci struct drm_crtc *crtc; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci /* Disable flip completion events. */ 6462306a36Sopenharmony_ci nvif_event_block(&disp->flip); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci /* Disable vblank interrupts. */ 6762306a36Sopenharmony_ci NVWriteCRTC(dev, 0, NV_PCRTC_INTR_EN_0, 0); 6862306a36Sopenharmony_ci if (nv_two_heads(dev)) 6962306a36Sopenharmony_ci NVWriteCRTC(dev, 1, NV_PCRTC_INTR_EN_0, 0); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (!runtime) 7262306a36Sopenharmony_ci cancel_work_sync(&drm->hpd_work); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (!suspend) 7562306a36Sopenharmony_ci return; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci /* Un-pin FB and cursors so they'll be evicted to system memory. */ 7862306a36Sopenharmony_ci list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 7962306a36Sopenharmony_ci struct drm_framebuffer *fb = crtc->primary->fb; 8062306a36Sopenharmony_ci struct nouveau_bo *nvbo; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci if (!fb || !fb->obj[0]) 8362306a36Sopenharmony_ci continue; 8462306a36Sopenharmony_ci nvbo = nouveau_gem_object(fb->obj[0]); 8562306a36Sopenharmony_ci nouveau_bo_unpin(nvbo); 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 8962306a36Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); 9062306a36Sopenharmony_ci if (nv_crtc->cursor.nvbo) { 9162306a36Sopenharmony_ci if (nv_crtc->cursor.set_offset) 9262306a36Sopenharmony_ci nouveau_bo_unmap(nv_crtc->cursor.nvbo); 9362306a36Sopenharmony_ci nouveau_bo_unpin(nv_crtc->cursor.nvbo); 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic int 9962306a36Sopenharmony_cinv04_display_init(struct drm_device *dev, bool resume, bool runtime) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct nv04_display *disp = nv04_display(dev); 10262306a36Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 10362306a36Sopenharmony_ci struct nouveau_encoder *encoder; 10462306a36Sopenharmony_ci struct drm_crtc *crtc; 10562306a36Sopenharmony_ci int ret; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* meh.. modeset apparently doesn't setup all the regs and depends 10862306a36Sopenharmony_ci * on pre-existing state, for now load the state of the card *before* 10962306a36Sopenharmony_ci * nouveau was loaded, and then do a modeset. 11062306a36Sopenharmony_ci * 11162306a36Sopenharmony_ci * best thing to do probably is to make save/restore routines not 11262306a36Sopenharmony_ci * save/restore "pre-load" state, but more general so we can save 11362306a36Sopenharmony_ci * on suspend too. 11462306a36Sopenharmony_ci */ 11562306a36Sopenharmony_ci list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 11662306a36Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); 11762306a36Sopenharmony_ci nv_crtc->save(&nv_crtc->base); 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.base.head) 12162306a36Sopenharmony_ci encoder->enc_save(&encoder->base.base); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* Enable flip completion events. */ 12462306a36Sopenharmony_ci nvif_event_allow(&disp->flip); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (!resume) 12762306a36Sopenharmony_ci return 0; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* Re-pin FB/cursors. */ 13062306a36Sopenharmony_ci list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 13162306a36Sopenharmony_ci struct drm_framebuffer *fb = crtc->primary->fb; 13262306a36Sopenharmony_ci struct nouveau_bo *nvbo; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (!fb || !fb->obj[0]) 13562306a36Sopenharmony_ci continue; 13662306a36Sopenharmony_ci nvbo = nouveau_gem_object(fb->obj[0]); 13762306a36Sopenharmony_ci ret = nouveau_bo_pin(nvbo, NOUVEAU_GEM_DOMAIN_VRAM, true); 13862306a36Sopenharmony_ci if (ret) 13962306a36Sopenharmony_ci NV_ERROR(drm, "Could not pin framebuffer\n"); 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 14362306a36Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); 14462306a36Sopenharmony_ci if (!nv_crtc->cursor.nvbo) 14562306a36Sopenharmony_ci continue; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, 14862306a36Sopenharmony_ci NOUVEAU_GEM_DOMAIN_VRAM, true); 14962306a36Sopenharmony_ci if (!ret && nv_crtc->cursor.set_offset) 15062306a36Sopenharmony_ci ret = nouveau_bo_map(nv_crtc->cursor.nvbo); 15162306a36Sopenharmony_ci if (ret) 15262306a36Sopenharmony_ci NV_ERROR(drm, "Could not pin/map cursor.\n"); 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* Force CLUT to get re-loaded during modeset. */ 15662306a36Sopenharmony_ci list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 15762306a36Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci nv_crtc->lut.depth = 0; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci /* This should ensure we don't hit a locking problem when someone 16362306a36Sopenharmony_ci * wakes us up via a connector. We should never go into suspend 16462306a36Sopenharmony_ci * while the display is on anyways. 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_ci if (runtime) 16762306a36Sopenharmony_ci return 0; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* Restore mode. */ 17062306a36Sopenharmony_ci drm_helper_resume_force_mode(dev); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 17362306a36Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (!nv_crtc->cursor.nvbo) 17662306a36Sopenharmony_ci continue; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (nv_crtc->cursor.set_offset) 17962306a36Sopenharmony_ci nv_crtc->cursor.set_offset(nv_crtc, 18062306a36Sopenharmony_ci nv_crtc->cursor.nvbo->offset); 18162306a36Sopenharmony_ci nv_crtc->cursor.set_pos(nv_crtc, nv_crtc->cursor_saved_x, 18262306a36Sopenharmony_ci nv_crtc->cursor_saved_y); 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci return 0; 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic void 18962306a36Sopenharmony_cinv04_display_destroy(struct drm_device *dev) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci struct nv04_display *disp = nv04_display(dev); 19262306a36Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 19362306a36Sopenharmony_ci struct nouveau_encoder *encoder; 19462306a36Sopenharmony_ci struct nouveau_crtc *nv_crtc; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci /* Restore state */ 19762306a36Sopenharmony_ci list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.base.head) 19862306a36Sopenharmony_ci encoder->enc_restore(&encoder->base.base); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci list_for_each_entry(nv_crtc, &dev->mode_config.crtc_list, base.head) 20162306a36Sopenharmony_ci nv_crtc->restore(&nv_crtc->base); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci nouveau_hw_save_vga_fonts(dev, 0); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci nvif_event_dtor(&disp->flip); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci nouveau_display(dev)->priv = NULL; 20862306a36Sopenharmony_ci vfree(disp); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci nvif_object_unmap(&drm->client.device.object); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ciint 21462306a36Sopenharmony_cinv04_display_create(struct drm_device *dev) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 21762306a36Sopenharmony_ci struct nvkm_i2c *i2c = nvxx_i2c(&drm->client.device); 21862306a36Sopenharmony_ci struct dcb_table *dcb = &drm->vbios.dcb; 21962306a36Sopenharmony_ci struct drm_connector *connector, *ct; 22062306a36Sopenharmony_ci struct drm_encoder *encoder; 22162306a36Sopenharmony_ci struct nouveau_encoder *nv_encoder; 22262306a36Sopenharmony_ci struct nouveau_crtc *crtc; 22362306a36Sopenharmony_ci struct nv04_display *disp; 22462306a36Sopenharmony_ci int i, ret; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci disp = vzalloc(sizeof(*disp)); 22762306a36Sopenharmony_ci if (!disp) 22862306a36Sopenharmony_ci return -ENOMEM; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci disp->drm = drm; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci nvif_object_map(&drm->client.device.object, NULL, 0); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci nouveau_display(dev)->priv = disp; 23562306a36Sopenharmony_ci nouveau_display(dev)->dtor = nv04_display_destroy; 23662306a36Sopenharmony_ci nouveau_display(dev)->init = nv04_display_init; 23762306a36Sopenharmony_ci nouveau_display(dev)->fini = nv04_display_fini; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci /* Pre-nv50 doesn't support atomic, so don't expose the ioctls */ 24062306a36Sopenharmony_ci dev->driver_features &= ~DRIVER_ATOMIC; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci /* Request page flip completion event. */ 24362306a36Sopenharmony_ci if (drm->channel) { 24462306a36Sopenharmony_ci ret = nvif_event_ctor(&drm->channel->nvsw, "kmsFlip", 0, nv04_flip_complete, 24562306a36Sopenharmony_ci true, NULL, 0, &disp->flip); 24662306a36Sopenharmony_ci if (ret) 24762306a36Sopenharmony_ci return ret; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci nouveau_hw_save_vga_fonts(dev, 1); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci nv04_crtc_create(dev, 0); 25362306a36Sopenharmony_ci if (nv_two_heads(dev)) 25462306a36Sopenharmony_ci nv04_crtc_create(dev, 1); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci for (i = 0; i < dcb->entries; i++) { 25762306a36Sopenharmony_ci struct dcb_output *dcbent = &dcb->entry[i]; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci connector = nouveau_connector_create(dev, dcbent); 26062306a36Sopenharmony_ci if (IS_ERR(connector)) 26162306a36Sopenharmony_ci continue; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci switch (dcbent->type) { 26462306a36Sopenharmony_ci case DCB_OUTPUT_ANALOG: 26562306a36Sopenharmony_ci ret = nv04_dac_create(connector, dcbent); 26662306a36Sopenharmony_ci break; 26762306a36Sopenharmony_ci case DCB_OUTPUT_LVDS: 26862306a36Sopenharmony_ci case DCB_OUTPUT_TMDS: 26962306a36Sopenharmony_ci ret = nv04_dfp_create(connector, dcbent); 27062306a36Sopenharmony_ci break; 27162306a36Sopenharmony_ci case DCB_OUTPUT_TV: 27262306a36Sopenharmony_ci if (dcbent->location == DCB_LOC_ON_CHIP) 27362306a36Sopenharmony_ci ret = nv17_tv_create(connector, dcbent); 27462306a36Sopenharmony_ci else 27562306a36Sopenharmony_ci ret = nv04_tv_create(connector, dcbent); 27662306a36Sopenharmony_ci break; 27762306a36Sopenharmony_ci default: 27862306a36Sopenharmony_ci NV_WARN(drm, "DCB type %d not known\n", dcbent->type); 27962306a36Sopenharmony_ci continue; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if (ret) 28362306a36Sopenharmony_ci continue; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci list_for_each_entry_safe(connector, ct, 28762306a36Sopenharmony_ci &dev->mode_config.connector_list, head) { 28862306a36Sopenharmony_ci if (!connector->possible_encoders) { 28962306a36Sopenharmony_ci NV_WARN(drm, "%s has no encoders, removing\n", 29062306a36Sopenharmony_ci connector->name); 29162306a36Sopenharmony_ci connector->funcs->destroy(connector); 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 29662306a36Sopenharmony_ci struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); 29762306a36Sopenharmony_ci struct nvkm_i2c_bus *bus = 29862306a36Sopenharmony_ci nvkm_i2c_bus_find(i2c, nv_encoder->dcb->i2c_index); 29962306a36Sopenharmony_ci nv_encoder->i2c = bus ? &bus->i2c : NULL; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* Save previous state */ 30362306a36Sopenharmony_ci list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) 30462306a36Sopenharmony_ci crtc->save(&crtc->base); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci list_for_each_entry(nv_encoder, &dev->mode_config.encoder_list, base.base.head) 30762306a36Sopenharmony_ci nv_encoder->enc_save(&nv_encoder->base.base); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci nouveau_overlay_init(dev); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci return 0; 31262306a36Sopenharmony_ci} 313