Commit | Line | Data |
---|---|---|
6ee73861 BS |
1 | /* |
2 | * Copyright 2009 Red Hat Inc. | |
3 | * | |
4 | * Permission is hereby granted, free of charge, to any person obtaining a | |
5 | * copy of this software and associated documentation files (the "Software"), | |
6 | * to deal in the Software without restriction, including without limitation | |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
8 | * and/or sell copies of the Software, and to permit persons to whom the | |
9 | * Software is furnished to do so, subject to the following conditions: | |
10 | * | |
11 | * The above copyright notice and this permission notice shall be included in | |
12 | * all copies or substantial portions of the Software. | |
13 | * | |
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
17 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
18 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
20 | * OTHER DEALINGS IN THE SOFTWARE. | |
21 | * | |
22 | * Author: Ben Skeggs | |
23 | */ | |
24 | ||
760285e7 | 25 | #include <drm/drm_crtc_helper.h> |
6ee73861 | 26 | |
4dc28134 | 27 | #include "nouveau_drv.h" |
77145f1c | 28 | #include "nouveau_reg.h" |
1a646342 | 29 | #include "hw.h" |
6ee73861 BS |
30 | #include "nouveau_encoder.h" |
31 | #include "nouveau_connector.h" | |
f04a4186 | 32 | #include "nouveau_bo.h" |
18340587 | 33 | #include "nouveau_gem.h" |
2bf00037 | 34 | #include "nouveau_chan.h" |
6ee73861 | 35 | |
fcd6f048 BS |
36 | #include <nvif/if0004.h> |
37 | ||
09838c4e LP |
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 | ||
ba801ef0 | 56 | static void |
a0922278 | 57 | nv04_display_fini(struct drm_device *dev, bool runtime, bool suspend) |
ba801ef0 | 58 | { |
a0922278 | 59 | struct nouveau_drm *drm = nouveau_drm(dev); |
fcd6f048 | 60 | struct nv04_display *disp = nv04_display(dev); |
f04a4186 | 61 | struct drm_crtc *crtc; |
fcd6f048 BS |
62 | |
63 | /* Disable flip completion events. */ | |
801bc858 | 64 | nvif_event_block(&disp->flip); |
fcd6f048 | 65 | |
ba801ef0 BS |
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); | |
f04a4186 | 70 | |
b96a2253 | 71 | if (!runtime && !drm->headless) |
a0922278 LP |
72 | cancel_work_sync(&drm->hpd_work); |
73 | ||
f04a4186 BS |
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) { | |
18340587 TZ |
79 | struct drm_framebuffer *fb = crtc->primary->fb; |
80 | struct nouveau_bo *nvbo; | |
f04a4186 | 81 | |
18340587 | 82 | if (!fb || !fb->obj[0]) |
f04a4186 | 83 | continue; |
18340587 TZ |
84 | nvbo = nouveau_gem_object(fb->obj[0]); |
85 | nouveau_bo_unpin(nvbo); | |
f04a4186 BS |
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 | } | |
ba801ef0 BS |
96 | } |
97 | ||
98 | static int | |
0f9976dd | 99 | nv04_display_init(struct drm_device *dev, bool resume, bool runtime) |
ba801ef0 | 100 | { |
fcd6f048 | 101 | struct nv04_display *disp = nv04_display(dev); |
0f9976dd | 102 | struct nouveau_drm *drm = nouveau_drm(dev); |
ba801ef0 | 103 | struct nouveau_encoder *encoder; |
0f9976dd BS |
104 | struct drm_crtc *crtc; |
105 | int ret; | |
ba801ef0 BS |
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 | */ | |
0f9976dd BS |
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 | } | |
ba801ef0 BS |
119 | |
120 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.base.head) | |
121 | encoder->enc_save(&encoder->base.base); | |
122 | ||
fcd6f048 | 123 | /* Enable flip completion events. */ |
801bc858 | 124 | nvif_event_allow(&disp->flip); |
0f9976dd BS |
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) { | |
18340587 TZ |
131 | struct drm_framebuffer *fb = crtc->primary->fb; |
132 | struct nouveau_bo *nvbo; | |
0f9976dd | 133 | |
18340587 | 134 | if (!fb || !fb->obj[0]) |
0f9976dd | 135 | continue; |
18340587 | 136 | nvbo = nouveau_gem_object(fb->obj[0]); |
81b61579 | 137 | ret = nouveau_bo_pin(nvbo, NOUVEAU_GEM_DOMAIN_VRAM, true); |
0f9976dd BS |
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 | ||
81b61579 CK |
147 | ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, |
148 | NOUVEAU_GEM_DOMAIN_VRAM, true); | |
0f9976dd BS |
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) | |
0dc9b286 ND |
179 | nv_crtc->cursor.set_offset(nv_crtc, |
180 | nv_crtc->cursor.nvbo->offset); | |
0f9976dd BS |
181 | nv_crtc->cursor.set_pos(nv_crtc, nv_crtc->cursor_saved_x, |
182 | nv_crtc->cursor_saved_y); | |
183 | } | |
184 | ||
ba801ef0 BS |
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); | |
ba801ef0 BS |
192 | struct nouveau_encoder *encoder; |
193 | struct nouveau_crtc *nv_crtc; | |
194 | ||
195 | /* Restore state */ | |
196 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.base.head) | |
197 | encoder->enc_restore(&encoder->base.base); | |
198 | ||
199 | list_for_each_entry(nv_crtc, &dev->mode_config.crtc_list, base.head) | |
200 | nv_crtc->restore(&nv_crtc->base); | |
201 | ||
202 | nouveau_hw_save_vga_fonts(dev, 0); | |
203 | ||
801bc858 | 204 | nvif_event_dtor(&disp->flip); |
fcd6f048 | 205 | |
ba801ef0 | 206 | nouveau_display(dev)->priv = NULL; |
bd6e07e7 | 207 | vfree(disp); |
ba801ef0 BS |
208 | } |
209 | ||
6ee73861 BS |
210 | int |
211 | nv04_display_create(struct drm_device *dev) | |
212 | { | |
77145f1c | 213 | struct nouveau_drm *drm = nouveau_drm(dev); |
6901f1d6 | 214 | struct nvkm_i2c *i2c = nvxx_i2c(drm); |
77145f1c | 215 | struct dcb_table *dcb = &drm->vbios.dcb; |
8f1a6086 | 216 | struct drm_connector *connector, *ct; |
6ee73861 | 217 | struct drm_encoder *encoder; |
129b7820 | 218 | struct nouveau_encoder *nv_encoder; |
2c3d7715 | 219 | struct nouveau_crtc *crtc; |
017e6e29 | 220 | struct nv04_display *disp; |
6ee73861 BS |
221 | int i, ret; |
222 | ||
bd6e07e7 | 223 | disp = vzalloc(sizeof(*disp)); |
017e6e29 BS |
224 | if (!disp) |
225 | return -ENOMEM; | |
226 | ||
801bc858 BS |
227 | disp->drm = drm; |
228 | ||
77145f1c BS |
229 | nouveau_display(dev)->priv = disp; |
230 | nouveau_display(dev)->dtor = nv04_display_destroy; | |
231 | nouveau_display(dev)->init = nv04_display_init; | |
232 | nouveau_display(dev)->fini = nv04_display_fini; | |
6ee73861 | 233 | |
eb493fbc | 234 | /* Pre-nv50 doesn't support atomic, so don't expose the ioctls */ |
ad45354a | 235 | dev->driver_features &= ~DRIVER_ATOMIC; |
eb493fbc | 236 | |
fcd6f048 | 237 | /* Request page flip completion event. */ |
2bf00037 | 238 | if (drm->channel) { |
801bc858 BS |
239 | ret = nvif_event_ctor(&drm->channel->nvsw, "kmsFlip", 0, nv04_flip_complete, |
240 | true, NULL, 0, &disp->flip); | |
241 | if (ret) | |
242 | return ret; | |
fcd6f048 BS |
243 | } |
244 | ||
95f158ea | 245 | nouveau_hw_save_vga_fonts(dev, 1); |
6ee73861 | 246 | |
6ee73861 BS |
247 | nv04_crtc_create(dev, 0); |
248 | if (nv_two_heads(dev)) | |
249 | nv04_crtc_create(dev, 1); | |
250 | ||
251 | for (i = 0; i < dcb->entries; i++) { | |
cb75d97e | 252 | struct dcb_output *dcbent = &dcb->entry[i]; |
6ee73861 | 253 | |
8b7d92ca | 254 | connector = nouveau_connector_create(dev, dcbent->connector); |
8f1a6086 BS |
255 | if (IS_ERR(connector)) |
256 | continue; | |
257 | ||
6ee73861 | 258 | switch (dcbent->type) { |
cb75d97e | 259 | case DCB_OUTPUT_ANALOG: |
8f1a6086 | 260 | ret = nv04_dac_create(connector, dcbent); |
6ee73861 | 261 | break; |
cb75d97e BS |
262 | case DCB_OUTPUT_LVDS: |
263 | case DCB_OUTPUT_TMDS: | |
8f1a6086 | 264 | ret = nv04_dfp_create(connector, dcbent); |
6ee73861 | 265 | break; |
cb75d97e | 266 | case DCB_OUTPUT_TV: |
6ee73861 | 267 | if (dcbent->location == DCB_LOC_ON_CHIP) |
8f1a6086 | 268 | ret = nv17_tv_create(connector, dcbent); |
6ee73861 | 269 | else |
8f1a6086 | 270 | ret = nv04_tv_create(connector, dcbent); |
6ee73861 BS |
271 | break; |
272 | default: | |
77145f1c | 273 | NV_WARN(drm, "DCB type %d not known\n", dcbent->type); |
6ee73861 BS |
274 | continue; |
275 | } | |
276 | ||
277 | if (ret) | |
278 | continue; | |
6ee73861 BS |
279 | } |
280 | ||
8f1a6086 BS |
281 | list_for_each_entry_safe(connector, ct, |
282 | &dev->mode_config.connector_list, head) { | |
62afb4ad | 283 | if (!connector->possible_encoders) { |
77145f1c | 284 | NV_WARN(drm, "%s has no encoders, removing\n", |
8c6c361a | 285 | connector->name); |
8f1a6086 BS |
286 | connector->funcs->destroy(connector); |
287 | } | |
288 | } | |
6ee73861 | 289 | |
5ed50209 BS |
290 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
291 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | |
2aa5eac5 BS |
292 | struct nvkm_i2c_bus *bus = |
293 | nvkm_i2c_bus_find(i2c, nv_encoder->dcb->i2c_index); | |
294 | nv_encoder->i2c = bus ? &bus->i2c : NULL; | |
5ed50209 BS |
295 | } |
296 | ||
6ee73861 | 297 | /* Save previous state */ |
2c3d7715 DV |
298 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) |
299 | crtc->save(&crtc->base); | |
6ee73861 | 300 | |
129b7820 DV |
301 | list_for_each_entry(nv_encoder, &dev->mode_config.encoder_list, base.base.head) |
302 | nv_encoder->enc_save(&nv_encoder->base.base); | |
6ee73861 | 303 | |
515de6b2 IM |
304 | nouveau_overlay_init(dev); |
305 | ||
6ee73861 BS |
306 | return 0; |
307 | } |