xref: /netbsd-src/sys/external/bsd/drm2/dist/drm/nouveau/dispnv04/nouveau_dispnv04_disp.c (revision 41ec02673d281bbb3d38e6c78504ce6e30c228c1)
1 /*	$NetBSD: nouveau_dispnv04_disp.c,v 1.6 2021/12/18 23:45:32 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.6 2021/12/18 23:45:32 riastradh Exp $");
29 
30 #include <drm/drm_crtc_helper.h>
31 
32 #include "nouveau_drv.h"
33 #include "nouveau_reg.h"
34 #include "hw.h"
35 #include "nouveau_encoder.h"
36 #include "nouveau_connector.h"
37 #include "nouveau_bo.h"
38 
39 #include <nvif/if0004.h>
40 
41 static void
nv04_display_fini(struct drm_device * dev,bool suspend)42 nv04_display_fini(struct drm_device *dev, bool suspend)
43 {
44 	struct nv04_display *disp = nv04_display(dev);
45 	struct drm_crtc *crtc;
46 
47 	/* Disable flip completion events. */
48 	nvif_notify_put(&disp->flip);
49 
50 	/* Disable vblank interrupts. */
51 	NVWriteCRTC(dev, 0, NV_PCRTC_INTR_EN_0, 0);
52 	if (nv_two_heads(dev))
53 		NVWriteCRTC(dev, 1, NV_PCRTC_INTR_EN_0, 0);
54 
55 	if (!suspend)
56 		return;
57 
58 	/* Un-pin FB and cursors so they'll be evicted to system memory. */
59 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
60 		struct nouveau_framebuffer *nouveau_fb;
61 
62 		nouveau_fb = nouveau_framebuffer(crtc->primary->fb);
63 		if (!nouveau_fb || !nouveau_fb->nvbo)
64 			continue;
65 
66 		nouveau_bo_unpin(nouveau_fb->nvbo);
67 	}
68 
69 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
70 		struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
71 		if (nv_crtc->cursor.nvbo) {
72 			if (nv_crtc->cursor.set_offset)
73 				nouveau_bo_unmap(nv_crtc->cursor.nvbo);
74 			nouveau_bo_unpin(nv_crtc->cursor.nvbo);
75 		}
76 	}
77 }
78 
79 static int
nv04_display_init(struct drm_device * dev,bool resume,bool runtime)80 nv04_display_init(struct drm_device *dev, bool resume, bool runtime)
81 {
82 	struct nv04_display *disp = nv04_display(dev);
83 	struct nouveau_drm *drm = nouveau_drm(dev);
84 	struct nouveau_encoder *encoder;
85 	struct drm_crtc *crtc;
86 	int ret;
87 
88 	/* meh.. modeset apparently doesn't setup all the regs and depends
89 	 * on pre-existing state, for now load the state of the card *before*
90 	 * nouveau was loaded, and then do a modeset.
91 	 *
92 	 * best thing to do probably is to make save/restore routines not
93 	 * save/restore "pre-load" state, but more general so we can save
94 	 * on suspend too.
95 	 */
96 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
97 		struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
98 		nv_crtc->save(&nv_crtc->base);
99 	}
100 
101 	list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.base.head)
102 		encoder->enc_save(&encoder->base.base);
103 
104 	/* Enable flip completion events. */
105 	nvif_notify_get(&disp->flip);
106 
107 	if (!resume)
108 		return 0;
109 
110 	/* Re-pin FB/cursors. */
111 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
112 		struct nouveau_framebuffer *nouveau_fb;
113 
114 		nouveau_fb = nouveau_framebuffer(crtc->primary->fb);
115 		if (!nouveau_fb || !nouveau_fb->nvbo)
116 			continue;
117 
118 		ret = nouveau_bo_pin(nouveau_fb->nvbo, TTM_PL_FLAG_VRAM, true);
119 		if (ret)
120 			NV_ERROR(drm, "Could not pin framebuffer\n");
121 	}
122 
123 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
124 		struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
125 		if (!nv_crtc->cursor.nvbo)
126 			continue;
127 
128 		ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM, true);
129 		if (!ret && nv_crtc->cursor.set_offset)
130 			ret = nouveau_bo_map(nv_crtc->cursor.nvbo);
131 		if (ret)
132 			NV_ERROR(drm, "Could not pin/map cursor.\n");
133 	}
134 
135 	/* Force CLUT to get re-loaded during modeset. */
136 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
137 		struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
138 
139 		nv_crtc->lut.depth = 0;
140 	}
141 
142 	/* This should ensure we don't hit a locking problem when someone
143 	 * wakes us up via a connector.  We should never go into suspend
144 	 * while the display is on anyways.
145 	 */
146 	if (runtime)
147 		return 0;
148 
149 	/* Restore mode. */
150 	drm_helper_resume_force_mode(dev);
151 
152 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
153 		struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
154 
155 		if (!nv_crtc->cursor.nvbo)
156 			continue;
157 
158 		if (nv_crtc->cursor.set_offset)
159 			nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.nvbo->bo.offset);
160 		nv_crtc->cursor.set_pos(nv_crtc, nv_crtc->cursor_saved_x,
161 						 nv_crtc->cursor_saved_y);
162 	}
163 
164 	return 0;
165 }
166 
167 static void
nv04_display_destroy(struct drm_device * dev)168 nv04_display_destroy(struct drm_device *dev)
169 {
170 	struct nv04_display *disp = nv04_display(dev);
171 	struct nouveau_drm *drm = nouveau_drm(dev);
172 	struct nouveau_encoder *encoder;
173 	struct nouveau_crtc *nv_crtc;
174 
175 	/* Restore state */
176 	list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.base.head)
177 		encoder->enc_restore(&encoder->base.base);
178 
179 	list_for_each_entry(nv_crtc, &dev->mode_config.crtc_list, base.head)
180 		nv_crtc->restore(&nv_crtc->base);
181 
182 	nouveau_hw_save_vga_fonts(dev, 0);
183 
184 	nvif_notify_fini(&disp->flip);
185 
186 	nouveau_display(dev)->priv = NULL;
187 	kfree(disp);
188 
189 	nvif_object_unmap(&drm->client.device.object);
190 }
191 
192 int
nv04_display_create(struct drm_device * dev)193 nv04_display_create(struct drm_device *dev)
194 {
195 	struct nouveau_drm *drm = nouveau_drm(dev);
196 	struct nvkm_i2c *i2c = nvxx_i2c(&drm->client.device);
197 	struct dcb_table *dcb = &drm->vbios.dcb;
198 	struct drm_connector *connector, *ct;
199 	struct drm_encoder *encoder;
200 	struct nouveau_encoder *nv_encoder;
201 	struct nouveau_crtc *crtc;
202 	struct nv04_display *disp;
203 	int i, ret;
204 
205 	disp = kzalloc(sizeof(*disp), GFP_KERNEL);
206 	if (!disp)
207 		return -ENOMEM;
208 
209 	nvif_object_map(&drm->client.device.object, NULL, 0);
210 
211 	nouveau_display(dev)->priv = disp;
212 	nouveau_display(dev)->dtor = nv04_display_destroy;
213 	nouveau_display(dev)->init = nv04_display_init;
214 	nouveau_display(dev)->fini = nv04_display_fini;
215 
216 	/* Pre-nv50 doesn't support atomic, so don't expose the ioctls */
217 	dev->driver_features &= ~DRIVER_ATOMIC;
218 
219 	/* Request page flip completion event. */
220 	if (drm->nvsw.client) {
221 		nvif_notify_init(&drm->nvsw, nv04_flip_complete,
222 				 false, NV04_NVSW_NTFY_UEVENT,
223 				 NULL, 0, 0, &disp->flip);
224 	}
225 
226 	nouveau_hw_save_vga_fonts(dev, 1);
227 
228 	nv04_crtc_create(dev, 0);
229 	if (nv_two_heads(dev))
230 		nv04_crtc_create(dev, 1);
231 
232 	for (i = 0; i < dcb->entries; i++) {
233 		struct dcb_output *dcbent = &dcb->entry[i];
234 
235 		connector = nouveau_connector_create(dev, dcbent);
236 		if (IS_ERR(connector))
237 			continue;
238 
239 		switch (dcbent->type) {
240 		case DCB_OUTPUT_ANALOG:
241 			ret = nv04_dac_create(connector, dcbent);
242 			break;
243 		case DCB_OUTPUT_LVDS:
244 		case DCB_OUTPUT_TMDS:
245 			ret = nv04_dfp_create(connector, dcbent);
246 			break;
247 		case DCB_OUTPUT_TV:
248 			if (dcbent->location == DCB_LOC_ON_CHIP)
249 				ret = nv17_tv_create(connector, dcbent);
250 			else
251 				ret = nv04_tv_create(connector, dcbent);
252 			break;
253 		default:
254 			NV_WARN(drm, "DCB type %d not known\n", dcbent->type);
255 			continue;
256 		}
257 
258 		if (ret)
259 			continue;
260 	}
261 
262 	list_for_each_entry_safe(connector, ct,
263 				 &dev->mode_config.connector_list, head) {
264 		if (!connector->possible_encoders) {
265 			NV_WARN(drm, "%s has no encoders, removing\n",
266 				connector->name);
267 			connector->funcs->destroy(connector);
268 		}
269 	}
270 
271 	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
272 		struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
273 		struct nvkm_i2c_bus *bus =
274 			nvkm_i2c_bus_find(i2c, nv_encoder->dcb->i2c_index);
275 		nv_encoder->i2c = bus ? &bus->i2c : NULL;
276 	}
277 
278 	/* Save previous state */
279 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head)
280 		crtc->save(&crtc->base);
281 
282 	list_for_each_entry(nv_encoder, &dev->mode_config.encoder_list, base.base.head)
283 		nv_encoder->enc_save(&nv_encoder->base.base);
284 
285 	nouveau_overlay_init(dev);
286 
287 	return 0;
288 }
289