18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright 2009 Red Hat Inc. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 58c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 68c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation 78c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 88c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 98c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice shall be included in 128c2ecf20Sopenharmony_ci * all copies or substantial portions of the Software. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 158c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 168c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 178c2ecf20Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 188c2ecf20Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 198c2ecf20Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 208c2ecf20Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE. 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * Author: Ben Skeggs 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <drm/drm_crtc_helper.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include "nouveau_drv.h" 288c2ecf20Sopenharmony_ci#include "nouveau_reg.h" 298c2ecf20Sopenharmony_ci#include "hw.h" 308c2ecf20Sopenharmony_ci#include "nouveau_encoder.h" 318c2ecf20Sopenharmony_ci#include "nouveau_connector.h" 328c2ecf20Sopenharmony_ci#include "nouveau_bo.h" 338c2ecf20Sopenharmony_ci#include "nouveau_gem.h" 348c2ecf20Sopenharmony_ci#include "nouveau_chan.h" 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#include <nvif/if0004.h> 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistruct nouveau_connector * 398c2ecf20Sopenharmony_cinv04_encoder_get_connector(struct nouveau_encoder *encoder) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci struct drm_device *dev = to_drm_encoder(encoder)->dev; 428c2ecf20Sopenharmony_ci struct drm_connector *connector; 438c2ecf20Sopenharmony_ci struct drm_connector_list_iter conn_iter; 448c2ecf20Sopenharmony_ci struct nouveau_connector *nv_connector = NULL; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci drm_connector_list_iter_begin(dev, &conn_iter); 478c2ecf20Sopenharmony_ci drm_for_each_connector_iter(connector, &conn_iter) { 488c2ecf20Sopenharmony_ci if (connector->encoder == to_drm_encoder(encoder)) 498c2ecf20Sopenharmony_ci nv_connector = nouveau_connector(connector); 508c2ecf20Sopenharmony_ci } 518c2ecf20Sopenharmony_ci drm_connector_list_iter_end(&conn_iter); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci return nv_connector; 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic void 578c2ecf20Sopenharmony_cinv04_display_fini(struct drm_device *dev, bool runtime, bool suspend) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 608c2ecf20Sopenharmony_ci struct nv04_display *disp = nv04_display(dev); 618c2ecf20Sopenharmony_ci struct drm_crtc *crtc; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci /* Disable flip completion events. */ 648c2ecf20Sopenharmony_ci nvif_notify_put(&disp->flip); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci /* Disable vblank interrupts. */ 678c2ecf20Sopenharmony_ci NVWriteCRTC(dev, 0, NV_PCRTC_INTR_EN_0, 0); 688c2ecf20Sopenharmony_ci if (nv_two_heads(dev)) 698c2ecf20Sopenharmony_ci NVWriteCRTC(dev, 1, NV_PCRTC_INTR_EN_0, 0); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci if (!runtime) 728c2ecf20Sopenharmony_ci cancel_work_sync(&drm->hpd_work); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (!suspend) 758c2ecf20Sopenharmony_ci return; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci /* Un-pin FB and cursors so they'll be evicted to system memory. */ 788c2ecf20Sopenharmony_ci list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 798c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = crtc->primary->fb; 808c2ecf20Sopenharmony_ci struct nouveau_bo *nvbo; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (!fb || !fb->obj[0]) 838c2ecf20Sopenharmony_ci continue; 848c2ecf20Sopenharmony_ci nvbo = nouveau_gem_object(fb->obj[0]); 858c2ecf20Sopenharmony_ci nouveau_bo_unpin(nvbo); 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 898c2ecf20Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); 908c2ecf20Sopenharmony_ci if (nv_crtc->cursor.nvbo) { 918c2ecf20Sopenharmony_ci if (nv_crtc->cursor.set_offset) 928c2ecf20Sopenharmony_ci nouveau_bo_unmap(nv_crtc->cursor.nvbo); 938c2ecf20Sopenharmony_ci nouveau_bo_unpin(nv_crtc->cursor.nvbo); 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic int 998c2ecf20Sopenharmony_cinv04_display_init(struct drm_device *dev, bool resume, bool runtime) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct nv04_display *disp = nv04_display(dev); 1028c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 1038c2ecf20Sopenharmony_ci struct nouveau_encoder *encoder; 1048c2ecf20Sopenharmony_ci struct drm_crtc *crtc; 1058c2ecf20Sopenharmony_ci int ret; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci /* meh.. modeset apparently doesn't setup all the regs and depends 1088c2ecf20Sopenharmony_ci * on pre-existing state, for now load the state of the card *before* 1098c2ecf20Sopenharmony_ci * nouveau was loaded, and then do a modeset. 1108c2ecf20Sopenharmony_ci * 1118c2ecf20Sopenharmony_ci * best thing to do probably is to make save/restore routines not 1128c2ecf20Sopenharmony_ci * save/restore "pre-load" state, but more general so we can save 1138c2ecf20Sopenharmony_ci * on suspend too. 1148c2ecf20Sopenharmony_ci */ 1158c2ecf20Sopenharmony_ci list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 1168c2ecf20Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); 1178c2ecf20Sopenharmony_ci nv_crtc->save(&nv_crtc->base); 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.base.head) 1218c2ecf20Sopenharmony_ci encoder->enc_save(&encoder->base.base); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* Enable flip completion events. */ 1248c2ecf20Sopenharmony_ci nvif_notify_get(&disp->flip); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (!resume) 1278c2ecf20Sopenharmony_ci return 0; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci /* Re-pin FB/cursors. */ 1308c2ecf20Sopenharmony_ci list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 1318c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = crtc->primary->fb; 1328c2ecf20Sopenharmony_ci struct nouveau_bo *nvbo; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (!fb || !fb->obj[0]) 1358c2ecf20Sopenharmony_ci continue; 1368c2ecf20Sopenharmony_ci nvbo = nouveau_gem_object(fb->obj[0]); 1378c2ecf20Sopenharmony_ci ret = nouveau_bo_pin(nvbo, NOUVEAU_GEM_DOMAIN_VRAM, true); 1388c2ecf20Sopenharmony_ci if (ret) 1398c2ecf20Sopenharmony_ci NV_ERROR(drm, "Could not pin framebuffer\n"); 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 1438c2ecf20Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); 1448c2ecf20Sopenharmony_ci if (!nv_crtc->cursor.nvbo) 1458c2ecf20Sopenharmony_ci continue; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, 1488c2ecf20Sopenharmony_ci NOUVEAU_GEM_DOMAIN_VRAM, true); 1498c2ecf20Sopenharmony_ci if (!ret && nv_crtc->cursor.set_offset) 1508c2ecf20Sopenharmony_ci ret = nouveau_bo_map(nv_crtc->cursor.nvbo); 1518c2ecf20Sopenharmony_ci if (ret) 1528c2ecf20Sopenharmony_ci NV_ERROR(drm, "Could not pin/map cursor.\n"); 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci /* Force CLUT to get re-loaded during modeset. */ 1568c2ecf20Sopenharmony_ci list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 1578c2ecf20Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci nv_crtc->lut.depth = 0; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* This should ensure we don't hit a locking problem when someone 1638c2ecf20Sopenharmony_ci * wakes us up via a connector. We should never go into suspend 1648c2ecf20Sopenharmony_ci * while the display is on anyways. 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_ci if (runtime) 1678c2ecf20Sopenharmony_ci return 0; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci /* Restore mode. */ 1708c2ecf20Sopenharmony_ci drm_helper_resume_force_mode(dev); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 1738c2ecf20Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (!nv_crtc->cursor.nvbo) 1768c2ecf20Sopenharmony_ci continue; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (nv_crtc->cursor.set_offset) 1798c2ecf20Sopenharmony_ci nv_crtc->cursor.set_offset(nv_crtc, 1808c2ecf20Sopenharmony_ci nv_crtc->cursor.nvbo->offset); 1818c2ecf20Sopenharmony_ci nv_crtc->cursor.set_pos(nv_crtc, nv_crtc->cursor_saved_x, 1828c2ecf20Sopenharmony_ci nv_crtc->cursor_saved_y); 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci return 0; 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic void 1898c2ecf20Sopenharmony_cinv04_display_destroy(struct drm_device *dev) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci struct nv04_display *disp = nv04_display(dev); 1928c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 1938c2ecf20Sopenharmony_ci struct nouveau_encoder *encoder; 1948c2ecf20Sopenharmony_ci struct nouveau_crtc *nv_crtc; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci /* Restore state */ 1978c2ecf20Sopenharmony_ci list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.base.head) 1988c2ecf20Sopenharmony_ci encoder->enc_restore(&encoder->base.base); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci list_for_each_entry(nv_crtc, &dev->mode_config.crtc_list, base.head) 2018c2ecf20Sopenharmony_ci nv_crtc->restore(&nv_crtc->base); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci nouveau_hw_save_vga_fonts(dev, 0); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci nvif_notify_dtor(&disp->flip); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci nouveau_display(dev)->priv = NULL; 2088c2ecf20Sopenharmony_ci vfree(disp); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci nvif_object_unmap(&drm->client.device.object); 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ciint 2148c2ecf20Sopenharmony_cinv04_display_create(struct drm_device *dev) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 2178c2ecf20Sopenharmony_ci struct nvkm_i2c *i2c = nvxx_i2c(&drm->client.device); 2188c2ecf20Sopenharmony_ci struct dcb_table *dcb = &drm->vbios.dcb; 2198c2ecf20Sopenharmony_ci struct drm_connector *connector, *ct; 2208c2ecf20Sopenharmony_ci struct drm_encoder *encoder; 2218c2ecf20Sopenharmony_ci struct nouveau_encoder *nv_encoder; 2228c2ecf20Sopenharmony_ci struct nouveau_crtc *crtc; 2238c2ecf20Sopenharmony_ci struct nv04_display *disp; 2248c2ecf20Sopenharmony_ci int i, ret; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci disp = vzalloc(sizeof(*disp)); 2278c2ecf20Sopenharmony_ci if (!disp) 2288c2ecf20Sopenharmony_ci return -ENOMEM; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci nvif_object_map(&drm->client.device.object, NULL, 0); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci nouveau_display(dev)->priv = disp; 2338c2ecf20Sopenharmony_ci nouveau_display(dev)->dtor = nv04_display_destroy; 2348c2ecf20Sopenharmony_ci nouveau_display(dev)->init = nv04_display_init; 2358c2ecf20Sopenharmony_ci nouveau_display(dev)->fini = nv04_display_fini; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* Pre-nv50 doesn't support atomic, so don't expose the ioctls */ 2388c2ecf20Sopenharmony_ci dev->driver_features &= ~DRIVER_ATOMIC; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci /* Request page flip completion event. */ 2418c2ecf20Sopenharmony_ci if (drm->channel) { 2428c2ecf20Sopenharmony_ci nvif_notify_ctor(&drm->channel->nvsw, "kmsFlip", nv04_flip_complete, 2438c2ecf20Sopenharmony_ci false, NV04_NVSW_NTFY_UEVENT, 2448c2ecf20Sopenharmony_ci NULL, 0, 0, &disp->flip); 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci nouveau_hw_save_vga_fonts(dev, 1); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci nv04_crtc_create(dev, 0); 2508c2ecf20Sopenharmony_ci if (nv_two_heads(dev)) 2518c2ecf20Sopenharmony_ci nv04_crtc_create(dev, 1); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci for (i = 0; i < dcb->entries; i++) { 2548c2ecf20Sopenharmony_ci struct dcb_output *dcbent = &dcb->entry[i]; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci connector = nouveau_connector_create(dev, dcbent); 2578c2ecf20Sopenharmony_ci if (IS_ERR(connector)) 2588c2ecf20Sopenharmony_ci continue; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci switch (dcbent->type) { 2618c2ecf20Sopenharmony_ci case DCB_OUTPUT_ANALOG: 2628c2ecf20Sopenharmony_ci ret = nv04_dac_create(connector, dcbent); 2638c2ecf20Sopenharmony_ci break; 2648c2ecf20Sopenharmony_ci case DCB_OUTPUT_LVDS: 2658c2ecf20Sopenharmony_ci case DCB_OUTPUT_TMDS: 2668c2ecf20Sopenharmony_ci ret = nv04_dfp_create(connector, dcbent); 2678c2ecf20Sopenharmony_ci break; 2688c2ecf20Sopenharmony_ci case DCB_OUTPUT_TV: 2698c2ecf20Sopenharmony_ci if (dcbent->location == DCB_LOC_ON_CHIP) 2708c2ecf20Sopenharmony_ci ret = nv17_tv_create(connector, dcbent); 2718c2ecf20Sopenharmony_ci else 2728c2ecf20Sopenharmony_ci ret = nv04_tv_create(connector, dcbent); 2738c2ecf20Sopenharmony_ci break; 2748c2ecf20Sopenharmony_ci default: 2758c2ecf20Sopenharmony_ci NV_WARN(drm, "DCB type %d not known\n", dcbent->type); 2768c2ecf20Sopenharmony_ci continue; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci if (ret) 2808c2ecf20Sopenharmony_ci continue; 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci list_for_each_entry_safe(connector, ct, 2848c2ecf20Sopenharmony_ci &dev->mode_config.connector_list, head) { 2858c2ecf20Sopenharmony_ci if (!connector->possible_encoders) { 2868c2ecf20Sopenharmony_ci NV_WARN(drm, "%s has no encoders, removing\n", 2878c2ecf20Sopenharmony_ci connector->name); 2888c2ecf20Sopenharmony_ci connector->funcs->destroy(connector); 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 2938c2ecf20Sopenharmony_ci struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); 2948c2ecf20Sopenharmony_ci struct nvkm_i2c_bus *bus = 2958c2ecf20Sopenharmony_ci nvkm_i2c_bus_find(i2c, nv_encoder->dcb->i2c_index); 2968c2ecf20Sopenharmony_ci nv_encoder->i2c = bus ? &bus->i2c : NULL; 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci /* Save previous state */ 3008c2ecf20Sopenharmony_ci list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) 3018c2ecf20Sopenharmony_ci crtc->save(&crtc->base); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci list_for_each_entry(nv_encoder, &dev->mode_config.encoder_list, base.base.head) 3048c2ecf20Sopenharmony_ci nv_encoder->enc_save(&nv_encoder->base.base); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci nouveau_overlay_init(dev); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci return 0; 3098c2ecf20Sopenharmony_ci} 310