| .. | .. |
|---|
| 22 | 22 | * Author: Ben Skeggs |
|---|
| 23 | 23 | */ |
|---|
| 24 | 24 | |
|---|
| 25 | | -#include <drm/drmP.h> |
|---|
| 26 | 25 | #include <drm/drm_crtc_helper.h> |
|---|
| 27 | 26 | |
|---|
| 28 | 27 | #include "nouveau_drv.h" |
|---|
| .. | .. |
|---|
| 30 | 29 | #include "hw.h" |
|---|
| 31 | 30 | #include "nouveau_encoder.h" |
|---|
| 32 | 31 | #include "nouveau_connector.h" |
|---|
| 32 | +#include "nouveau_bo.h" |
|---|
| 33 | +#include "nouveau_gem.h" |
|---|
| 34 | +#include "nouveau_chan.h" |
|---|
| 35 | + |
|---|
| 36 | +#include <nvif/if0004.h> |
|---|
| 37 | + |
|---|
| 38 | +struct nouveau_connector * |
|---|
| 39 | +nv04_encoder_get_connector(struct nouveau_encoder *encoder) |
|---|
| 40 | +{ |
|---|
| 41 | + struct drm_device *dev = to_drm_encoder(encoder)->dev; |
|---|
| 42 | + struct drm_connector *connector; |
|---|
| 43 | + struct drm_connector_list_iter conn_iter; |
|---|
| 44 | + struct nouveau_connector *nv_connector = NULL; |
|---|
| 45 | + |
|---|
| 46 | + drm_connector_list_iter_begin(dev, &conn_iter); |
|---|
| 47 | + drm_for_each_connector_iter(connector, &conn_iter) { |
|---|
| 48 | + if (connector->encoder == to_drm_encoder(encoder)) |
|---|
| 49 | + nv_connector = nouveau_connector(connector); |
|---|
| 50 | + } |
|---|
| 51 | + drm_connector_list_iter_end(&conn_iter); |
|---|
| 52 | + |
|---|
| 53 | + return nv_connector; |
|---|
| 54 | +} |
|---|
| 55 | + |
|---|
| 56 | +static void |
|---|
| 57 | +nv04_display_fini(struct drm_device *dev, bool runtime, bool suspend) |
|---|
| 58 | +{ |
|---|
| 59 | + struct nouveau_drm *drm = nouveau_drm(dev); |
|---|
| 60 | + struct nv04_display *disp = nv04_display(dev); |
|---|
| 61 | + struct drm_crtc *crtc; |
|---|
| 62 | + |
|---|
| 63 | + /* Disable flip completion events. */ |
|---|
| 64 | + nvif_notify_put(&disp->flip); |
|---|
| 65 | + |
|---|
| 66 | + /* Disable vblank interrupts. */ |
|---|
| 67 | + NVWriteCRTC(dev, 0, NV_PCRTC_INTR_EN_0, 0); |
|---|
| 68 | + if (nv_two_heads(dev)) |
|---|
| 69 | + NVWriteCRTC(dev, 1, NV_PCRTC_INTR_EN_0, 0); |
|---|
| 70 | + |
|---|
| 71 | + if (!runtime) |
|---|
| 72 | + cancel_work_sync(&drm->hpd_work); |
|---|
| 73 | + |
|---|
| 74 | + if (!suspend) |
|---|
| 75 | + return; |
|---|
| 76 | + |
|---|
| 77 | + /* Un-pin FB and cursors so they'll be evicted to system memory. */ |
|---|
| 78 | + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
|---|
| 79 | + struct drm_framebuffer *fb = crtc->primary->fb; |
|---|
| 80 | + struct nouveau_bo *nvbo; |
|---|
| 81 | + |
|---|
| 82 | + if (!fb || !fb->obj[0]) |
|---|
| 83 | + continue; |
|---|
| 84 | + nvbo = nouveau_gem_object(fb->obj[0]); |
|---|
| 85 | + nouveau_bo_unpin(nvbo); |
|---|
| 86 | + } |
|---|
| 87 | + |
|---|
| 88 | + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
|---|
| 89 | + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); |
|---|
| 90 | + if (nv_crtc->cursor.nvbo) { |
|---|
| 91 | + if (nv_crtc->cursor.set_offset) |
|---|
| 92 | + nouveau_bo_unmap(nv_crtc->cursor.nvbo); |
|---|
| 93 | + nouveau_bo_unpin(nv_crtc->cursor.nvbo); |
|---|
| 94 | + } |
|---|
| 95 | + } |
|---|
| 96 | +} |
|---|
| 97 | + |
|---|
| 98 | +static int |
|---|
| 99 | +nv04_display_init(struct drm_device *dev, bool resume, bool runtime) |
|---|
| 100 | +{ |
|---|
| 101 | + struct nv04_display *disp = nv04_display(dev); |
|---|
| 102 | + struct nouveau_drm *drm = nouveau_drm(dev); |
|---|
| 103 | + struct nouveau_encoder *encoder; |
|---|
| 104 | + struct drm_crtc *crtc; |
|---|
| 105 | + int ret; |
|---|
| 106 | + |
|---|
| 107 | + /* meh.. modeset apparently doesn't setup all the regs and depends |
|---|
| 108 | + * on pre-existing state, for now load the state of the card *before* |
|---|
| 109 | + * nouveau was loaded, and then do a modeset. |
|---|
| 110 | + * |
|---|
| 111 | + * best thing to do probably is to make save/restore routines not |
|---|
| 112 | + * save/restore "pre-load" state, but more general so we can save |
|---|
| 113 | + * on suspend too. |
|---|
| 114 | + */ |
|---|
| 115 | + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
|---|
| 116 | + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); |
|---|
| 117 | + nv_crtc->save(&nv_crtc->base); |
|---|
| 118 | + } |
|---|
| 119 | + |
|---|
| 120 | + list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.base.head) |
|---|
| 121 | + encoder->enc_save(&encoder->base.base); |
|---|
| 122 | + |
|---|
| 123 | + /* Enable flip completion events. */ |
|---|
| 124 | + nvif_notify_get(&disp->flip); |
|---|
| 125 | + |
|---|
| 126 | + if (!resume) |
|---|
| 127 | + return 0; |
|---|
| 128 | + |
|---|
| 129 | + /* Re-pin FB/cursors. */ |
|---|
| 130 | + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
|---|
| 131 | + struct drm_framebuffer *fb = crtc->primary->fb; |
|---|
| 132 | + struct nouveau_bo *nvbo; |
|---|
| 133 | + |
|---|
| 134 | + if (!fb || !fb->obj[0]) |
|---|
| 135 | + continue; |
|---|
| 136 | + nvbo = nouveau_gem_object(fb->obj[0]); |
|---|
| 137 | + ret = nouveau_bo_pin(nvbo, NOUVEAU_GEM_DOMAIN_VRAM, true); |
|---|
| 138 | + if (ret) |
|---|
| 139 | + NV_ERROR(drm, "Could not pin framebuffer\n"); |
|---|
| 140 | + } |
|---|
| 141 | + |
|---|
| 142 | + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
|---|
| 143 | + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); |
|---|
| 144 | + if (!nv_crtc->cursor.nvbo) |
|---|
| 145 | + continue; |
|---|
| 146 | + |
|---|
| 147 | + ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, |
|---|
| 148 | + NOUVEAU_GEM_DOMAIN_VRAM, true); |
|---|
| 149 | + if (!ret && nv_crtc->cursor.set_offset) |
|---|
| 150 | + ret = nouveau_bo_map(nv_crtc->cursor.nvbo); |
|---|
| 151 | + if (ret) |
|---|
| 152 | + NV_ERROR(drm, "Could not pin/map cursor.\n"); |
|---|
| 153 | + } |
|---|
| 154 | + |
|---|
| 155 | + /* Force CLUT to get re-loaded during modeset. */ |
|---|
| 156 | + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
|---|
| 157 | + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); |
|---|
| 158 | + |
|---|
| 159 | + nv_crtc->lut.depth = 0; |
|---|
| 160 | + } |
|---|
| 161 | + |
|---|
| 162 | + /* This should ensure we don't hit a locking problem when someone |
|---|
| 163 | + * wakes us up via a connector. We should never go into suspend |
|---|
| 164 | + * while the display is on anyways. |
|---|
| 165 | + */ |
|---|
| 166 | + if (runtime) |
|---|
| 167 | + return 0; |
|---|
| 168 | + |
|---|
| 169 | + /* Restore mode. */ |
|---|
| 170 | + drm_helper_resume_force_mode(dev); |
|---|
| 171 | + |
|---|
| 172 | + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
|---|
| 173 | + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); |
|---|
| 174 | + |
|---|
| 175 | + if (!nv_crtc->cursor.nvbo) |
|---|
| 176 | + continue; |
|---|
| 177 | + |
|---|
| 178 | + if (nv_crtc->cursor.set_offset) |
|---|
| 179 | + nv_crtc->cursor.set_offset(nv_crtc, |
|---|
| 180 | + nv_crtc->cursor.nvbo->offset); |
|---|
| 181 | + nv_crtc->cursor.set_pos(nv_crtc, nv_crtc->cursor_saved_x, |
|---|
| 182 | + nv_crtc->cursor_saved_y); |
|---|
| 183 | + } |
|---|
| 184 | + |
|---|
| 185 | + return 0; |
|---|
| 186 | +} |
|---|
| 187 | + |
|---|
| 188 | +static void |
|---|
| 189 | +nv04_display_destroy(struct drm_device *dev) |
|---|
| 190 | +{ |
|---|
| 191 | + struct nv04_display *disp = nv04_display(dev); |
|---|
| 192 | + struct nouveau_drm *drm = nouveau_drm(dev); |
|---|
| 193 | + struct nouveau_encoder *encoder; |
|---|
| 194 | + struct nouveau_crtc *nv_crtc; |
|---|
| 195 | + |
|---|
| 196 | + /* Restore state */ |
|---|
| 197 | + list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.base.head) |
|---|
| 198 | + encoder->enc_restore(&encoder->base.base); |
|---|
| 199 | + |
|---|
| 200 | + list_for_each_entry(nv_crtc, &dev->mode_config.crtc_list, base.head) |
|---|
| 201 | + nv_crtc->restore(&nv_crtc->base); |
|---|
| 202 | + |
|---|
| 203 | + nouveau_hw_save_vga_fonts(dev, 0); |
|---|
| 204 | + |
|---|
| 205 | + nvif_notify_dtor(&disp->flip); |
|---|
| 206 | + |
|---|
| 207 | + nouveau_display(dev)->priv = NULL; |
|---|
| 208 | + vfree(disp); |
|---|
| 209 | + |
|---|
| 210 | + nvif_object_unmap(&drm->client.device.object); |
|---|
| 211 | +} |
|---|
| 33 | 212 | |
|---|
| 34 | 213 | int |
|---|
| 35 | 214 | nv04_display_create(struct drm_device *dev) |
|---|
| .. | .. |
|---|
| 44 | 223 | struct nv04_display *disp; |
|---|
| 45 | 224 | int i, ret; |
|---|
| 46 | 225 | |
|---|
| 47 | | - disp = kzalloc(sizeof(*disp), GFP_KERNEL); |
|---|
| 226 | + disp = vzalloc(sizeof(*disp)); |
|---|
| 48 | 227 | if (!disp) |
|---|
| 49 | 228 | return -ENOMEM; |
|---|
| 50 | 229 | |
|---|
| .. | .. |
|---|
| 56 | 235 | nouveau_display(dev)->fini = nv04_display_fini; |
|---|
| 57 | 236 | |
|---|
| 58 | 237 | /* Pre-nv50 doesn't support atomic, so don't expose the ioctls */ |
|---|
| 59 | | - dev->driver->driver_features &= ~DRIVER_ATOMIC; |
|---|
| 238 | + dev->driver_features &= ~DRIVER_ATOMIC; |
|---|
| 239 | + |
|---|
| 240 | + /* Request page flip completion event. */ |
|---|
| 241 | + if (drm->channel) { |
|---|
| 242 | + nvif_notify_ctor(&drm->channel->nvsw, "kmsFlip", nv04_flip_complete, |
|---|
| 243 | + false, NV04_NVSW_NTFY_UEVENT, |
|---|
| 244 | + NULL, 0, 0, &disp->flip); |
|---|
| 245 | + } |
|---|
| 60 | 246 | |
|---|
| 61 | 247 | nouveau_hw_save_vga_fonts(dev, 1); |
|---|
| 62 | 248 | |
|---|
| .. | .. |
|---|
| 67 | 253 | for (i = 0; i < dcb->entries; i++) { |
|---|
| 68 | 254 | struct dcb_output *dcbent = &dcb->entry[i]; |
|---|
| 69 | 255 | |
|---|
| 70 | | - connector = nouveau_connector_create(dev, dcbent->connector); |
|---|
| 256 | + connector = nouveau_connector_create(dev, dcbent); |
|---|
| 71 | 257 | if (IS_ERR(connector)) |
|---|
| 72 | 258 | continue; |
|---|
| 73 | 259 | |
|---|
| .. | .. |
|---|
| 96 | 282 | |
|---|
| 97 | 283 | list_for_each_entry_safe(connector, ct, |
|---|
| 98 | 284 | &dev->mode_config.connector_list, head) { |
|---|
| 99 | | - if (!connector->encoder_ids[0]) { |
|---|
| 285 | + if (!connector->possible_encoders) { |
|---|
| 100 | 286 | NV_WARN(drm, "%s has no encoders, removing\n", |
|---|
| 101 | 287 | connector->name); |
|---|
| 102 | 288 | connector->funcs->destroy(connector); |
|---|
| .. | .. |
|---|
| 120 | 306 | nouveau_overlay_init(dev); |
|---|
| 121 | 307 | |
|---|
| 122 | 308 | return 0; |
|---|
| 123 | | -} |
|---|
| 124 | | - |
|---|
| 125 | | -void |
|---|
| 126 | | -nv04_display_destroy(struct drm_device *dev) |
|---|
| 127 | | -{ |
|---|
| 128 | | - struct nv04_display *disp = nv04_display(dev); |
|---|
| 129 | | - struct nouveau_drm *drm = nouveau_drm(dev); |
|---|
| 130 | | - struct nouveau_encoder *encoder; |
|---|
| 131 | | - struct nouveau_crtc *nv_crtc; |
|---|
| 132 | | - |
|---|
| 133 | | - /* Restore state */ |
|---|
| 134 | | - list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.base.head) |
|---|
| 135 | | - encoder->enc_restore(&encoder->base.base); |
|---|
| 136 | | - |
|---|
| 137 | | - list_for_each_entry(nv_crtc, &dev->mode_config.crtc_list, base.head) |
|---|
| 138 | | - nv_crtc->restore(&nv_crtc->base); |
|---|
| 139 | | - |
|---|
| 140 | | - nouveau_hw_save_vga_fonts(dev, 0); |
|---|
| 141 | | - |
|---|
| 142 | | - nouveau_display(dev)->priv = NULL; |
|---|
| 143 | | - kfree(disp); |
|---|
| 144 | | - |
|---|
| 145 | | - nvif_object_unmap(&drm->client.device.object); |
|---|
| 146 | | -} |
|---|
| 147 | | - |
|---|
| 148 | | -int |
|---|
| 149 | | -nv04_display_init(struct drm_device *dev) |
|---|
| 150 | | -{ |
|---|
| 151 | | - struct nouveau_encoder *encoder; |
|---|
| 152 | | - struct nouveau_crtc *crtc; |
|---|
| 153 | | - |
|---|
| 154 | | - /* meh.. modeset apparently doesn't setup all the regs and depends |
|---|
| 155 | | - * on pre-existing state, for now load the state of the card *before* |
|---|
| 156 | | - * nouveau was loaded, and then do a modeset. |
|---|
| 157 | | - * |
|---|
| 158 | | - * best thing to do probably is to make save/restore routines not |
|---|
| 159 | | - * save/restore "pre-load" state, but more general so we can save |
|---|
| 160 | | - * on suspend too. |
|---|
| 161 | | - */ |
|---|
| 162 | | - list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) |
|---|
| 163 | | - crtc->save(&crtc->base); |
|---|
| 164 | | - |
|---|
| 165 | | - list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.base.head) |
|---|
| 166 | | - encoder->enc_save(&encoder->base.base); |
|---|
| 167 | | - |
|---|
| 168 | | - return 0; |
|---|
| 169 | | -} |
|---|
| 170 | | - |
|---|
| 171 | | -void |
|---|
| 172 | | -nv04_display_fini(struct drm_device *dev) |
|---|
| 173 | | -{ |
|---|
| 174 | | - /* disable vblank interrupts */ |
|---|
| 175 | | - NVWriteCRTC(dev, 0, NV_PCRTC_INTR_EN_0, 0); |
|---|
| 176 | | - if (nv_two_heads(dev)) |
|---|
| 177 | | - NVWriteCRTC(dev, 1, NV_PCRTC_INTR_EN_0, 0); |
|---|
| 178 | 309 | } |
|---|